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

  我们今天学习一下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

时间: 2024-10-13 12:30:49

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

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

本篇借鉴了同事翔哥的劳动成果,在巨人的肩膀上把稿子又念了一遍. 内存泄漏的概念我这里就不说了,之前<UWP开发入门(十三)——用Diagnostic Tool检查内存泄漏>中提到过,即使有垃圾回收机制,写C#还是有可能发生内存泄漏. 一般来说,以下两种情况会导致内存泄漏: 对象用完了但是没有释放资源 对象本身是做了清理内存的操作,但是对象内部的子对象没有成功释放资源 下面就UWP开发中具体的实例来说明需要避免的写法 从static/global的对象上注册了事件 FakeService.Ins

Cocos2d-x 3.x 开发(十八)10行代码看自动Batch,10行代码看自动剔除

1.概述 在游戏的运行过程中,图形的绘制是非常大的开销.对于良莠不齐的Android手机市场,绘制优化较好的游戏,可以在更多的手机上运行,因此也是优化的重中之重.图形方面的优化主要体现在减少GUP的绘制次数上.这里我们分别从自动优化渲染批次和绘制剔除两个方面来看新版本在绘制上的优化. 2.自动batch 在Cocos2d-x 3.x中,抛弃了先前手动编写BatchNode,采用自动管理的方式.说起BatchNode,就难免涉及到显卡底层的绘制原理.简单的说,每提交一条绘制指令到显卡都会产生消耗,

[WebGL入门]十八,利用索引缓存来绘图

注:文章译自http://wgld.org/,原作者杉本雅広(doxas),文章中如果有我的额外说明,我会加上[lufy:],另外,鄙人webgl研究还不够深入,一些专业词语,如果翻译有误,欢迎大家指正. 本次的demo的运行结果 对应复杂的模型 上次,通过操作模型坐标变换矩阵,实现了多个模型的移动,旋转和放大缩小.但是,渲染的依然是简单的三角形,是个构造及其简单的模型.但是实际中,用WebGL来绘制一个简单的三角形的机会是很少见的.至少是个四角多边形吧,通常会是更复杂的模型.伴随着模型的复杂化

从零开始学ios开发(十八):Storyboards(下)

这篇我们完成Storyboards的最后一个例子,之前的例子中没有view之间的切换,这篇加上这个功能,使Storyboards的功能完整呈现.在Storyboards中负责view切换的东西叫做“segue”,只需对它进行简单的设置即可,一切都是傻瓜式的,无需繁琐的代码.好了,开始我们的例子吧. 1)Create a Simple Storyboard创建一个project,左边选择Application,右边选择Empty Application template(我们这里不使用Single

S3C2416裸机开发系列十八_音频驱动实现(1)

S3C2416裸机开发系列十八 音频驱动实现(1) 象棋小子    1048272975 在消费电子产品中,往往都会用到音频系统来播放音乐.进行通话等多媒体应用,此外,对于一些需语音提示的产品,音频部分都是不可或缺的功能.笔者此处就s3c2416的音频驱动实现作一个简单的介绍. 1. IIS音频总线 s3c2416支持IIS.PCM.AC97这三种音频接口,此处只分析IIS音频接口.IIS接口(Inter-IC Sound)在20世纪80年代首先被飞利浦公司用于消费音频,为数字音频设备之间的音频

Go语言开发(十八)、Go语言MySQL数据库操作

Go语言开发(十八).Go语言MySQL数据库操作 一.MySQL数据库驱动 1.MySQL数据库驱动简介 Go语言官方没有实现MySQL数据库驱动,常用的开源MySQL数据库驱动实现如下:(1)Go MySQL DriverGo MySQL Driver支持database/sql接口,全部采用Go语言实现.官方网站:https://github.com/go-sql-driver/mysql/(2)MyMySQLMyMySQL支持database/sql接口,也支持自定义的接口,全部采用Go

UWP开发入门(二十一)——保持Ui线程处于响应状态

GUI的程序有时候会因为等待一个耗时操作完成,导致界面卡死.本篇我们就UWP开发中可能遇到的情况,来讨论如何优化处理. 假设当前存在点击按钮跳转页面的操作,通过按钮打开的新页面,在初始化过程中存在一些耗时的操作. public void OnNavigatedTo(object obj) { var watch = new Stopwatch(); Debug.WriteLine("---------------Start"); watch.Start(); //假设耗时1秒 DoBu

UWP开发入门(七)——下拉刷新

本篇意在给这几天Win10 Mobile负面新闻不断的某软洗地,想要证明实现一个简单的下拉刷新并不困难.UWP开发更大的困难在于懒惰,缺乏学习的意愿.而不是“某软连下拉刷新控件都没有”这样的想法. 之前我也没有进行过下拉刷新的研究.于是先去google了几篇blog学习了一下,然后再看了某软官方的Sample.(同学们啊官方有下拉刷新的Sample啊!就在Git上啊!不要钱无门槛啊!)学习之后发现实现的方式大体分为两类. 一类是以某软Sample和博客园MS-UAP封装的PullToRefres

UWP开发入门(一)——SplitView

接下来会写一个UWP(Universal Windows Platform)开发入门的系列,自己学习到哪里,有什么心得总结,就会写到哪里.本篇对适用于顶层导航的SplitView控件展开讨论. 首先SplitView是Win10 UWP新增的控件,以前虽然可以通过DockPanel模拟出类似的效果,但又哪里及得上M$原生支持的SplitView快捷方便呢. 至于为什么说SplitView适合顶层导航,可以参考目前尚为数不多的UWP APP,比如微博.QQ和网易等Win10 APP,基本都是通过S