我们今天学习一下ContentControl,主要介绍如何使用ContentControl搭配DataTemplate来进行界面的复用,以及通过ContentTemplateSelector进一步减少页面元素数量,提高性能。

  假设我们的UWP APP为左右分开两列,左边为ListView显示集合,右边为ListView中选中项的明细页面。左侧ListView会列出每一项的Avatar,共分三种:1.有图像的显示图像。2.没图像有名字显示首字母,3.图像名字都没有,显示两个圈圈。同时在ListView被选中某项时,就在右侧显示大号的Avatar。

  是不是挺眼熟的,一看又是把生产上的东西简化出来做Demo了……

  

  可以拿来复用的就是Avatar这块了,我们先尝试着用ContentControl和DataTemplate来绘制这块内容:

        <DataTemplate x:Key="AvatarWithVisibility">
<Grid >
<Grid Visibility="{Binding Converter={StaticResource RoomTypeConverter},ConverterParameter=Name}">
<Ellipse Fill="Green"></Ellipse>
<TextBlock Text="{Binding Index}" TextAlignment="Center" VerticalAlignment="Center"></TextBlock>
</Grid>
<Grid Visibility="{Binding Converter={StaticResource RoomTypeConverter},ConverterParameter=Image}">
<Ellipse >
<Ellipse.Fill>
<ImageBrush ImageSource="{Binding ImageUri}"></ImageBrush>
</Ellipse.Fill>
</Ellipse>
</Grid>
<Grid CacheMode="BitmapCache" Visibility="{Binding Converter={StaticResource RoomTypeConverter},ConverterParameter=Default}">
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition Height="2*"></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition Width="2*"></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions> <Ellipse Grid.Row="0" Grid.Column="0" Grid.RowSpan="2" Grid.ColumnSpan="2" Fill="Green">
</Ellipse>
<Ellipse Grid.Row="1" Grid.Column="1" Grid.RowSpan="2" Grid.ColumnSpan="2" Fill="Orange">
</Ellipse>
</Grid>
</Grid>
</DataTemplate>

  很常规的写法,三个重叠的Grid通过RoomTypeConverter来确定Visibility的值,以便确定具体显示哪一个。该DataTemplate的使用页面如下:

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<ListView x:Name="listViewAvatar" Grid.Column="0" ItemsSource="{Binding Rooms}">
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition ></ColumnDefinition>
<ColumnDefinition></ColumnDefinition> </Grid.ColumnDefinitions> <ContentControl Grid.Column="0" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch" Width="50" Height="50"
ContentTemplate="{StaticResource AvatarWithVisibility}"></ContentControl> <StackPanel Grid.Column="1" >
<TextBlock Text="{Binding Name}"></TextBlock>
<TextBlock Text="{Binding ImageUri}"></TextBlock>
</StackPanel>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView> <ContentControl Grid.Column="1" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch"
Content="{Binding SelectedItem,ElementName=listViewAvatar}" Width="150" Height="150"
ContentTemplate="{StaticResource AvatarWithVisibility}"></ContentControl>
</Grid>

  可以看到页面分成了左右两块,左边ListView的ItemTemplate以及右半边各自使用了ContentControl,其中右半边的ContentControl把Content属性Binding到了ListView的SelectedItem上。通过上述例子可以明显地看到ContentControl复用了DataTemplate的内容,那有没有更进一步的优化呢?

  

  原则上我们希望尽最大可能的减少页面上显示的UIElement的数量,这样能够使布局设置和呈现的速度更快。下面我们尝试用ContentTemplateSelector来优化一下当前的页面。

  首先不再通过Visibility区分显示的Grid,而是将三个不同的Grid拆分开来,通过ContentTemplateSelector每次选择正确的样式来显示。因为Visibility即时为Collapsed,在可视化树上仍然会作为UIElement存在。

        <DataTemplate x:Key="Name">
<Grid>
<Ellipse Fill="Green"></Ellipse>
<TextBlock Text="{Binding Index}" TextAlignment="Center" VerticalAlignment="Center"></TextBlock>
</Grid> </DataTemplate> <DataTemplate x:Key="Image">
<Grid>
<Ellipse >
<Ellipse.Fill>
<ImageBrush ImageSource="{Binding ImageUri}"></ImageBrush>
</Ellipse.Fill>
</Ellipse>
</Grid>
</DataTemplate> <DataTemplate x:Key="Default">
<Grid CacheMode="BitmapCache">
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition Height="2*"></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition Width="2*"></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions> <Ellipse Grid.Row="0" Grid.Column="0" Grid.RowSpan="2" Grid.ColumnSpan="2" Fill="Green">
</Ellipse>
<Ellipse Grid.Row="1" Grid.Column="1" Grid.RowSpan="2" Grid.ColumnSpan="2" Fill="Orange">
</Ellipse>
</Grid>
</DataTemplate>

  之前的DataTemplate被拆成三个,并通过CustomTemplateSelector来使用,同一时间可视化树上将只会显示其中一个Grid的内容。

    public class CustomTemplateSelector : DataTemplateSelector
{
protected override DataTemplate SelectTemplateCore(object item, DependencyObject container)
{
var room = item as Room;
if (item == null)
{
return base.SelectTemplateCore(item, container);
}
if (room.ImageUri == null)
{
if (string.IsNullOrEmpty(room.Name))
{
return App.Current.Resources["Default"] as DataTemplate;
} return App.Current.Resources["Name"] as DataTemplate;
} return App.Current.Resources["Image"] as DataTemplate;
}
}

  实际使用的XAML有点类似于ListView本身的ItemTemplateSelector:

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<ListView x:Name="listViewAvatar" Grid.Column="0" ItemsSource="{Binding Rooms}">
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition ></ColumnDefinition>
<ColumnDefinition></ColumnDefinition> </Grid.ColumnDefinitions>
<ContentControl Grid.Column="0" ContentTemplateSelector="{StaticResource CustomTemplateSelector}"
Content="{Binding}" Width="50" Height="50" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch"></ContentControl>
<StackPanel Grid.Column="1" >
<TextBlock Text="{Binding Name}"></TextBlock>
<TextBlock Text="{Binding ImageUri}"></TextBlock>
</StackPanel>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView> <ContentControl Grid.Column="1" ContentTemplateSelector="{StaticResource CustomTemplateSelector}"
Content="{Binding SelectedItem,ElementName=listViewAvatar}" Width="150" Height="150"
HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch">
</ContentControl>
</Grid>

  XAML看上去和之前似乎没什么不同,但即使是这种简单的模板,在ListView中元素数量达到1000个时(因为虚拟化的缘故,实际远远不到1000个),可视化树上已经有了不小的数量差距。1300 VS 850,实际更复杂的DataTemplate将会有更大的数量差。

  

  另外需要指出的是,以上代码是在VS2015 Update3下完成的。如果是旧的VS2015 Update1,在列表快速滑动时,虚拟化会导致列表中ContentControl错位的显示,需要额外的Binding一个DataContext:

            <ContentControl Grid.Column="0" ContentTemplateSelector="{StaticResource CustomTemplateSelector}" DataContext="{Binding}"
Content="{Binding}" Width="50" Height="50" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch"></ContentControl>
<StackPanel Grid.Column="1" >

  以上就是今天的内容了,希望能给各位一点帮助。谢谢能看到这里的各位!

  顺便吐槽一下UWP不好做啊,除了明面上强大的竞争对手iOS和安卓,其实身后还有传统的Win32程序,毕竟UWP可以在PC上跑就等于想分别的人蛋糕啊,遭到打压是必然的,实在太惨了……

  本篇的完整代码依旧放在GitHub上,欢迎取用:

  https://github.com/manupstairs/UWPSamples/tree/master/UWPSamples/ContentControlWithTemplateSelector

UWP开发入门(十八)——使用ContentControl减少页面元素数量的更多相关文章

  1. [译]Kinect for Windows SDK开发入门(十八):Kinect Interaction交互控件

    本文译自 http://dotneteers.net/blogs/vbandi/archive/2013/03/25/kinect-interactions-with-wpf-part-i-getti ...

  2. UWP开发入门(八)——聊天窗口和ItemTemplateSelector

    我们平常用的最多的APP可能就是企鹅和微信了.有没有想过聊天窗口如何实现的?本篇我们将简单模拟一个聊天窗口. 聊天窗口大致上就是消息的一个集合列表.集合列表最常见的展现形式无非就是ListView.可 ...

  3. UWP开发入门(十六)——常见的内存泄漏的原因

    本篇借鉴了同事翔哥的劳动成果,在巨人的肩膀上把稿子又念了一遍. 内存泄漏的概念我这里就不说了,之前<UWP开发入门(十三)——用Diagnostic Tool检查内存泄漏>中提到过,即使有 ...

  4. UWP开发入门(十)——通过继承来扩展ListView

    本篇之所以起这样一个名字,是因为重点并非如何自定义控件,不涉及创建CustomControl和UserControl使用的Template和XAML概念.而是通过继承的方法来扩展一个现有的类,在继承的 ...

  5. UWP开发入门系列笔记之(一):UWP初览

    标签: 随着微软Build2015带来的好消息,Win10正式版发布的日子已经离我们越来越近了,我们也终于欣喜地看到:一个统一的Windows平台对于开发人员来说充满了吸引力,这局棋下的好大的说--于 ...

  6. UWP开发入门(四)——自定义CommandBar

    各位好,再次回到UWP开发入门系列,刚回归可能有些不适应,所以今天我们讲个简单的,自定义CommandBar,说通俗点就是自定义类似AppBarButton的东西,然后扔到CommandBar中使用. ...

  7. UWP开发入门(25)——通过Radio控制Bluetooth, WiFi

    回顾写了许久的UWP开发入门,竟然没有讲过通过Windows.Devices.Radios.Radio来控制Bluetooth和WiFi等功能的开关.也许是因为相关的API设计的简单好用,以至于被我给 ...

  8. UWP开发入门(二十二)——Storyboard和Animation

    微博上有同学问我MyerSplash是如何实现那个很炫的图片点亮,然后移动到屏幕中央的效果.惭愧啊,我又不是作者哪里会知道.硬着头皮去GitHub拜读了高手的代码,自愧弗如,比我不知道高到哪里去了…… ...

  9. UWP开发入门(二十三)——WebView

    本篇讨论在UWP开发中使用WebView控件时常见的问题,以及一些小技巧. WebView是实际开发中常用的控件,很多大家抱怨的套网页的应用都是通过WebView来实现的.这里要澄清一个问题,套网页的 ...

随机推荐

  1. 三、oracle 体系结构

    1.oracle内存由SGA+PGA所构成 2.oracle数据库体系结构数据库的体系结构是指数据库的组成.工作过程与原理,以及数据在数据库中的组织与管理机制. oracle工作原理: 1).在数据库 ...

  2. Aapache status / apache2ctl status 总是403

    默认apache2ctl status访问的是http://localhost:80/server_status 所以得搞定default这个站点,放歌html就可以了. 在default的配置里加入 ...

  3. MySQL执行计划中key_len详解

    (1).索引字段的附加信息:可以分为变长和定长数据类型讨论,当索引字段为定长数据类型,比如char,int,datetime,需要有是否为空的标记,这个标记需要占用1个字节:对于变长数据类型,比如:v ...

  4. nodejs配置简单HTTP服务器

    1.介绍 http-server 是一个简单的零配置命令行HTTP服务器, 基于 nodeJs. 如果你不想重复的写 nodeJs 的 web-server.js, 则可以使用这个. 2.安装 npm ...

  5. asp.net core 之静态文件目录的操作

    文章前言 之前写了一篇关于模拟登录的文章,自我感觉内容不太丰富,今天的这篇文章,希望在内容上能丰富些.本人缺少写文章的经验,技术上也是新手,但我会努力的,希望大家多多支持小弟. asp.net cor ...

  6. GNU Radio 之 rtl-sdr

    http://sdr.osmocom.org/trac/wiki/rtl-sdr 我使用的是去年买的一个电视棒(ezcap DVB-TFMDAB),50多元,它的频宽为52Mhz - 2.2GHz ! ...

  7. Android中AutoCompleteTextView的使用

    1.http://blog.csdn.net/FX_SKY/article/details/9326129 此博客讲解了auto组件如何使用baseAdapter来扩展功能,推荐参照此博客写demo ...

  8. 加锁解锁PHP实现 -转载

    PHP并没有完善的线程支持,甚至部署到基于线程模型的httpd服务器都会产生一些问题,但即使是多进程模型下的PHP,也难免出现多进程共同访问同一资源的情况. 比如整个程序共享的数据缓存,或者因为资源受 ...

  9. jQuery诞生记-原理与机制

    一.看似偶然的东西实际是必然会发生的 我大学时候在图书馆翻过一本很破旧的书,讲生物理论的,主要内容就是探讨生命的产生是偶然还是必然.里面很多亚里士多德都看不懂的公式计算什么的,还有模拟原始地球环境出现 ...

  10. Android源码网站

    https://mirrors.tuna.tsinghua.edu.cn/ https://mirrors.tuna.tsinghua.edu.cn/help/AOSP/ https://mirror ...