功能简介
- 当你看这篇博客的时候,我就假设你已经看过了第一篇博客或者对UWP的自适应界面已经有了一定的了解了;UWP的自适应界面很强大,但是,UWP的自适应界面存在这样的限制,那就是如果你要设置一个控件自适应变换,你就必须给它声明x:Name;
- 如果我们使用ListView的数据绑定(PS:如果不了解数据绑定,请跳转),那么对于ListView.ItemTemplate里的控件,我们就不能通过声明x:Name绑定到Target上,如下面代码所示
<!-- MainPage.xaml -->
<!-- ItemSource用于指定ListView的数据源,这里的Array为MainPage.xml.cs中MainPage类的一个集合 -->
<ListView Margin="20,20,50,20" ItemsSource="{x:Bind Array}" x:Name="MyList">
<ListView.ItemTemplate>
<!-- DataType指定Array中对象的类型,这里的Item为我们自定义的一个类 -->
<DataTemplate x:DataType="local:Item">
<!--
这里的ListView对应的模板为一个Checkbox,一个Grid(用于分隔开CheckBox和TextBlock)
一个Imag,还有一个文本框
-->
<RelativePanel>
<CheckBox HorizontalAlignment="Left" VerticalAlignment="Top" FontSize="14" CharacterSpacing="0" Margin="10,10,0,0"></CheckBox>
<!--
在宽屏状态下,Space的宽度为90,这时候文本框和Checkbox中间存在的空间正好存放图片
在窄屏状态下,Space的宽度为0,此时图片隐藏,Checkbox后紧跟着文本框
-->
<Grid Width="90" x:Name="Space"></Grid>
<Image HorizontalAlignment="Left" Height="40" Margin="50,10,0,0" VerticalAlignment="Top" Width="40" Source="{x:Bind Source}" x:Name="MyImage"></Image>
<TextBlock Text="{x:Bind Content}" RelativePanel.RightOf="Space" VerticalAlignment="Top" HorizontalAlignment="Left" FontSize="20" Margin="10,12,0,0"></TextBlock>
</RelativePanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
- 如果我们想要在窗口较大的时候显示图片, 而窗口变小的时候隐藏图片,按照之前的方法,我们会发现,即使是在Setter中声明了MyImage.Visibility的Value为Collapsed,图片也不会隐藏
- 所以这篇博客就是探讨在这种情况下如何实现图片的自适应变化
实现效果
- 宽屏状态下:
- 窄屏状态下:
实现过程
在MainPage.xml中创建需要的控件
这里添加的代码就是上面xaml代码,将上述代码添加到 MainPage.xaml里
添加一个新的类Item
位置:解决方案资源管理器—>添加—>新建项—>Visual C#—>代码—>类
在新建的Item.cs文件中添加下面的代码:
//Item.cs
//AdaptiveUIWithListView为你自己所使用的命名空间,
namespace AdaptiveUIWithListView
{
public class Item
{
private string _content;
private string _source;
//封装
public string Content { get => _content; }
public string Source { get => _source; }
//构造函数
public Item(string content, string source)
{
this._content = content;
this._source = source;
}
}
}
需要注意的是,这里的AdaptiveUIWithListView为博客的项目所使用的命名空间,你也可以使用自己定义的命名空间
但是如果你使用了自己定义的命名空间,因为MainPage.xaml.cs和MainPage.xaml里都使用了Item,所以需要添加using指令
假如你的Item使用的命名空间为MyNamespace,那么:
- 首先需要在MainPage.xaml.cs里添加using MyNamespace;
- 其次,在xaml前几行的Page属性里添加xmlns:mynamespace=”using:MyNamespace” 和DataTemplate的x:DataType=”local:Item”更改为x:DataType=”mynamespace:Item” (这里的mynamespace可以更改为你自己想要的值)
在构造函数上添加函数监听窗口尺寸变化和添加Array这一属性值
//MainPage.xaml.cs
//ObservableCollection一般用于数据绑定的Sourc
public ObservableCollection<Item> Array = new ObservableCollection<Item>();
public MainPage()
{
//往Array中添加元素
Array.Add(new Item("This is the head line, with the pictur 0.png in Folder /Assets.", "Assets/0.png"));
Array.Add(new Item("This is the first line, with the pictur 1.png in Folder /Assets.", "Assets/1.png"));
Array.Add(new Item("This is the second line, with the pictur 2.png in Folder /Assets.", "Assets/2.png"));
Array.Add(new Item("This is the third line, with the pictur 3.png in Folder /Assets.", "Assets/3.png"));
Array.Add(new Item("This is the fourth line, with the pictur 4.png in Folder /Assets.", "Assets/4.png"));
this.InitializeComponent();
//监听窗口大小变化事件
this.SizeChanged += (s, e) =>
{
if (e.NewSize.Width > 000 && e.NewSize.Width < 600)
{
//ShowImage为自定义的函数,见第四步
ShowImage(false);
}
else
{
ShowImage(true);
}
};
}
定义ShowImage函数
//flag决定图片是否显示
private void ShowImage(bool flag)
{
/*
* FindChildren是一个自定义的函数,接受两个参数,一个是List<T>,另一个是窗口的一个控件
* FindChildren运行结束后,将找到该控件的所有子控件中类型为T的控件
* 下面的例子即找到MyList控件中所有类型为RelativePanel的控件
*/
List<RelativePanel> list = new List<RelativePanel>();
FindChildren<RelativePanel>(list, MyList);
foreach (RelativePanel panel in list)
{
for (int i = 0; i < panel.Children.Count; i++)
{
//如果为图片,则判断是否显示
if (panel.Children[i] is Image)
{
if (flag)
{
((Image)panel.Children[i]).Visibility = Visibility.Visible;
}
else
{
((Image)panel.Children[i]).Visibility = Visibility.Collapsed;
}
}
//这里的Grid即xaml中的Space
if (panel.Children[i] is Grid)
{
if (flag)
{
((Grid)panel.Children[i]).Width = 90;
}
else
{
((Grid)panel.Children[i]).Width = 40;
}
}
}
}
}
//遍历startNode的子节点,找到类型为T的控件并且放在results中
internal static void FindChildren<T>(List<T> results, DependencyObject startNode)
where T : DependencyObject
{
int count = VisualTreeHelper.GetChildrenCount(startNode);
for (int i = 0; i < count; i++)
{
DependencyObject current = VisualTreeHelper.GetChild(startNode, i);
if ((current.GetType()).Equals(typeof(T)) || (current.GetType().GetTypeInfo().IsSubclassOf(typeof(T))))
{
T asType = (T)current;
results.Add(asType);
}
FindChildren<T>(results, current);
}
}
总结
最后总结一下,整个图片隐藏的过程就是:
FYI
其实,之前我还尝试过另一种方法,就是添加自定义控件UserControl的方法,然后在UserControl里设置VisualStateManager的Setter,然后再ListView里RelativePanel替换为
<UserControl>
......
</UserControl>
这样确实能实现图片的自动隐藏,但是却有出现另一个问题:Item的传值问题
如果使用UserControl,那么Item的属性值就必须传递给UserControl,在Debug模式下,我观察到Item的属性值确确实实已经传递给UserControl,但是UserControl的控件却没有显示出来,所以我想可能是UserControl先渲染出控件,然后才接收Item传递过来的值
如果有好的解决方法,欢迎在下面评论或者邮件(侧边栏点开就能找到了)ᕕ( ՞ ᗜ ՞ )ᕗ
项目下载之后记住把Debug模式从ARM换成X86或X64(根据你自己的机型选择),之前一开始学习的时候不知道这一点,从网上下载下来的项目都运行不了,纠结的一逼(╥╯^╰╥)