数据模板选择器

一般情况下,为集合控件定义了数据模板后,数据视图中的每一个子项都会套用相同的模板,即每个项都呈现相同的布局。然而在某些特殊情况下,可能需要让集合中的一个或多个数据项应用不同的模板。例如,一个呈现实时新闻的数据视图,开发者可能会做这样的安排:如果新闻内容是以文字为主的,则显示新闻标题和摘要如果新闻内容是以图片为主的,即图片新闻,则显示新闻标题和图片的缩略图。

要让数据项能够有选择地加载不同的数据模板,需要实现数据模板选择器,即从DataTemplateSelector类(位于Windows.UI.Xaml.Controls命名空间)派生出一个自定义类型(DataTempalteSelector类不能直接使用),然后将这个自定义类型的实例赋值给集合控件的ItemTemplateSelector属性即可。

在继承DataTemplateSelector类时,要重写基类的虚方法来返回合适的DataTemplate对象,DataTemplateSelector类有两个可以重载的虚方法:

        //
        // 摘要:
        //     返回给定项和容器的特定 DataTemplate。
        //
        // 参数:
        //   item:
        //     要为其返回模板的项。
        //
        //   container:
        //     模版项的父容器。
        //
        // 返回结果:
        //     用于特定项目和/或容器的模板。
        public DataTemplate SelectTemplate(object item, DependencyObject container);
        //
        // 摘要:
        //     返回给定项的特定 DataTemplate。
        //
        // 参数:
        //   item:
        //     要为其返回模板的项。
        //
        // 返回结果:
        //     用于特定项目和/或容器的模板。
        [Overload("SelectTemplateForItem")]
        public DataTemplate SelectTemplate(object item);

在编写自定义的数据模板选择器时,应当重写第二种重载的方法,因为集合控件在分析数据模板时调用的是第二种重载方法。item参数引用的是数据视图中的项,即放入集合控件Items集合中的对象;container参数引用呈现数据项的容器控件,该容器一般是ContentControl的派生类,即将数据项通过Content属性来呈现。不同的集合控件所使用的项容器控件不同,例如,ListBox控件中的想容器为ListBoxItem控件,ListView控件中的项容器为ListViewItem控件,ComboBox控件中的项容器为ComboBoxItem控件。。。可以发现这些命名都是有规律的,基本格式为"<集合控件类名>Item"。

下面示例将制作一个显示聊天记录的数据视图,其中就用到数据模板选择器。

定义一个MessageEntity类,表示单条聊天记录的信息,代码如下:

    class MessageEntity
    {
        ///<summary>
        ///消息类型
        ///</summary>
        public enum MsgType
        {
            ///<summary>
            ///接收到的消息
            ///</summary>
            From,
            ///<summary>
            ///发送的消息
            ///</summary>
            To
        }
        ///<summary>
        ///消息内容
        ///</summary>
        public string Content { get; set; }
        ///<summary>
        ///消息类型
        ///</summary>
        public MsgType MessageType { get; set; }
    }

然后实现一个自定义的数据模板选择器:

    sealed class CustomDataTemplateSelector : DataTemplateSelector
    {
        ///<summary>
        ///显示收到的消息的模版
        ///</summary>
        public DataTemplate MessageFromTemplate { get; set; }
        ///<summary>
        ///显示已发送消息的模版
        ///</summary>
        public DataTemplate MessageToTemplate { get; set; }
        protected override DataTemplate SelectTemplateCore(object item, DependencyObject container)
        {
            MessageEntity msgent = item as MessageEntity;
            if (msgent != null)
            {
                //判断消息类型,返回对应的模版
                if (msgent.MessageType is MessageEntity.MsgType.From)
                {
                    return MessageFromTemplate;
                }
                return MessageToTemplate;
            }
            return null;
        }
    }

CustomDataTempalteSelector类公开了两个属性,类型均为DataTemplate,这是方便在XAML文档中引用不同的数据模板资源。重写SelectTemplateCore方法,根据MessageEntity实例的MessageType属性来决定使用哪个模板。由于稍后会向集合控件中添加MessageEntity实例,因此传递给SelectTemplateCore方法的item参数的对象就是一个MessageEntity实例引用。

一般来说,收到的消息在屏幕上左对齐,用户头像在左边,消息正文在右边;而发出去的消息则在屏幕上右对齐,用户头像在右边,消息正文在左边。因此需要在XAML文档中分别声明这两种数据模板。

    <Page.Resources>
        <ResourceDictionary>
            <DataTemplate x:Key="from">
                <Grid HorizontalAlignment="Left">
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="auto"/>
                        <ColumnDefinition/>
                    </Grid.ColumnDefinitions>
                    <Image Width="40" Height="40" VerticalAlignment="Top" Source="ms-appx:///Assets/meinv_01.jpg"/>
                    <Border Grid.Column="1" Margin="8,0" Padding="10" Background="DarkBlue">
                        <TextBlock FontSize="20" Text="{Binding Content}" TextWrapping="Wrap"/>
                    </Border>
                </Grid>
            </DataTemplate>
            <DataTemplate x:Key="to">
                <Grid HorizontalAlignment="Right">
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition/>
                        <ColumnDefinition Width="auto"/>
                    </Grid.ColumnDefinitions>
                    <Image Width="40" Grid.Column="1" Height="40" VerticalAlignment="Top" Source="ms-appx:///Assets/meinv_02.jpg"/>
                    <Border Margin="8,0" Padding="10" Background="Green">
                        <TextBlock FontSize="20" Text="{Binding Content}" TextWrapping="Wrap"/>
                    </Border>
                </Grid>
            </DataTemplate>
        </ResourceDictionary>
    </Page.Resources>

接着在用户界面中放置一个ListView控件,并让它的ItemTemplateSelector属性引用前面定义的CustomDataTemplateSelector实例。

    <Grid>
        <ListView x:Name="lvMsgs" Margin="30,15">
            <ListView.ItemContainerStyle>
                <Style TargetType="ListViewItem">
                    <Setter Property="HorizontalContentAlignment" Value="Stretch"/>
                    <Setter Property="Margin" Value="0,20"/>
                </Style>
            </ListView.ItemContainerStyle>
            <ListView.ItemTemplateSelector>
                <local:CustomDataTemplateSelector MessageFromTemplate="{StaticResource from}"
                                                  MessageToTemplate="{StaticResource to}"/>
            </ListView.ItemTemplateSelector>
        </ListView>
    </Grid>

由于ListView控件中的子项容器为ListViewItem类,所以ItemContainerStyle属性所设置的样式的目标类型应该面向ListViewItem类,设置HorizontalContentAlignmente属性的值为Strech,目的是让每个数据项在用户界面进行横向填充,只有项目在界面填充所有的空间后,数据模板的Grid控件才有可能向左或向右对齐,如果使用默认对其方案,那么所有子项都会偏向左方,就无法满足布局需求了。

最后在页面类的构造方法中为ListView控件设置数据源。

        public MainPage()
        {
            this.InitializeComponent();
            lvMsgs.ItemsSource = new MessageEntity[]
            {
                new MessageEntity{MessageType = MessageEntity.MsgType.From,Content = "小明,你今天在家吗?"},
                new MessageEntity{MessageType = MessageEntity.MsgType.To,Content = "在啊。"},
                new MessageEntity{MessageType = MessageEntity.MsgType.From,Content = "待会儿帮你把东西送过去。"},
                new MessageEntity{MessageType = MessageEntity.MsgType.To,Content = "好的,十分感谢。"}
            };
        }

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值