UWP开发入门(十八)——使用ContentControl减少页面元素数量

简介: 原文:UWP开发入门(十八)——使用ContentControl减少页面元素数量  我们今天学习一下ContentControl,主要介绍如何使用ContentControl搭配DataTemplate来进行界面的复用,以及通过ContentTemplateSelector进一步减少页面元素数量,提高性能。
原文: UWP开发入门(十八)——使用ContentControl减少页面元素数量

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

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

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

  

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

        <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>

  可以看到页面分成了左右两块,左边ListViewItemTemplate以及右半边各自使用了ContentControl,其中右半边的ContentControlContent属性Binding到了ListViewSelectedItem上。通过上述例子可以明显地看到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

目录
相关文章
|
6月前
|
小程序 前端开发
【易售小程序项目】项目介绍、小程序页面展示与系列文章集合
【易售小程序项目】项目介绍、小程序页面展示与系列文章集合
93 2
|
2月前
|
前端开发
问题解答:SAP UI5 应用设置禁止被其他应用嵌入运行的工作原理解析试读版
问题解答:SAP UI5 应用设置禁止被其他应用嵌入运行的工作原理解析试读版
113 0
|
6月前
|
存储 小程序 算法
【易售小程序项目】小程序首页完善(滑到底部数据翻页、回到顶端、基于回溯算法的两列数据高宽比平衡)【后端基于若依管理系统开发】
【易售小程序项目】小程序首页完善(滑到底部数据翻页、回到顶端、基于回溯算法的两列数据高宽比平衡)【后端基于若依管理系统开发】
44 0
|
11月前
|
JavaScript 前端开发
jquery 展开收缩改变状态 保姆式教学代码,默认第二项展开
jquery 展开收缩改变状态 保姆式教学代码,默认第二项展开
|
前端开发
#yyds干货盘点 【React工作记录二十】前端常规关于网页文件下载的问题
#yyds干货盘点 【React工作记录二十】前端常规关于网页文件下载的问题
74 0
|
前端开发 图形学
Unity中影响UI排序的组件
Unity中影响UI排序的组件
|
API Android开发
Material Design 实战 之 第六弹 —— 可折叠式标题栏(CollapsingToolbarLayout) & 系统差异型的功能实现(充分利用系统状态栏空间)
Material Design 实战 之 第六弹 —— 可折叠式标题栏(CollapsingToolbarLayout) & 系统差异型的功能实现(充分利用系统状态栏空间)
|
数据可视化 前端开发
#yyds干货盘点# 【React工作记录四十二】获取页面的可视化高度和宽度
#yyds干货盘点# 【React工作记录四十二】获取页面的可视化高度和宽度
192 0
#yyds干货盘点# 【React工作记录四十二】获取页面的可视化高度和宽度
|
JSON 数据可视化 JavaScript
UI库组件属性太多不知道啥意思?没关系来看看可视化设置(一)
UI库提供了很多组件,组件又带有很多属性,有一些常用属性我们可以记住并且手撸,但是有些不常用的属性,或者需要设置多个属性,这样的情况下写起来就麻烦了,有时候还要打开帮助文档看看属性是怎么设定的,需要设置什么样的属性值。那么有没有优雅的方式来设置组件的各种属性呢?我做了一个在线小工具,可以方便的设置属性,并且可以实时看到效果。
UI库组件属性太多不知道啥意思?没关系来看看可视化设置(一)
|
JSON JavaScript 数据可视化
UI库组件属性太多不知道啥意思?没关系来看看可视化设置(二)
UI库提供了很多组件,组件又带有很多属性,有一些常用属性我们可以记住并且手撸,但是有些不常用的属性,或者需要设置多个属性,这样的情况下写起来就麻烦了,有时候还要打开帮助文档看看属性是怎么设定的,需要设置什么样的属性值。那么有没有优雅的方式来设置组件的各种属性呢?我做了一个在线小工具,可以方便的设置属性,并且可以实时看到效果。
UI库组件属性太多不知道啥意思?没关系来看看可视化设置(二)