探讨WPF的DataTemplateSelector

DataTemplateSelector根据数据类型动态选择模板,在创建聊天界面或任何需要根据数据类型呈现不同 UI 的应用程序中非常有用

在这个示例中,我们将创建一个简单的聊天界面,显示请求和响应消息的不同样式

首先,我们定义一个 MessageTemplateSelector 类,该类继承自 DataTemplateSelector。这个类的主要职责是根据传入的数据类型选择适当的 DataTemplate。

public class MessageTemplateSelector : DataTemplateSelector
{
    public DataTemplate RequestTemplate { get; set; }
    public DataTemplate ResponseTemplate { get; set; }

    public override DataTemplate SelectTemplate(object item, DependencyObject container)
    {
        if (item is Request)
        {
            return RequestTemplate;
        }

        if (item is Response)
        {
            return ResponseTemplate;
        }
        return base.SelectTemplate(item, container);
    }
}

在这个类中,我们定义了两个 DataTemplate,一个用于请求消息,一个用于响应消息。SelectTemplate 方法根据数据对象的类型返回相应的模板。

定义 Request 和 Response 的 XAML
接下来,我们定义两个 UserControl,一个用于显示请求消息,一个用于显示响应消息。

<UserControl x:Class="LinseerCopilot.Controls.Request"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800">
    <Grid>
        <Border BorderThickness="0" CornerRadius="15,15,0,15" HorizontalAlignment="Left" Padding="15,20" Background="#366DFF">
            <TextBlock Text="123123。" FontSize="24" Foreground="#FFFFFF"/>
        </Border>
    </Grid>
</UserControl>

<UserControl x:Class="LinseerCopilot.Controls.Response"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="80"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>  
        <StackPanel Orientation="Horizontal">
            <Button Style="{StaticResource Style.Button.Image}" VerticalAlignment="Center"/>
            <TextBlock Text="灵犀" Margin="10,0" FontSize="28" Foreground="#000000" VerticalAlignment="Center" FontWeight="Bold"/> 
        </StackPanel>
        <Border Grid.Row="1" BorderThickness="0" CornerRadius="0,15,15,15" HorizontalAlignment="Left" Padding="15,20" Background="#FFFFFF">
            <TextBlock Text="你好,我是灵犀助手,请说出您的问题或需求" FontSize="24" Foreground="#000000"/>
        </Border>
    </Grid>
</UserControl>

主界面 XAML
最后,我们在主界面中使用这些控件和 MessageTemplateSelector。

<UserControl x:Class="LinseerCopilot.Controls.Main"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800">

    <UserControl.Resources>
        <DataTemplate x:Key="ResponseTemplate">
            <controls:Response HorizontalAlignment="Left"/>
        </DataTemplate>

        <DataTemplate x:Key="RequestTemplate">
            <controls:Request HorizontalAlignment="Right"/>
        </DataTemplate>

        <linseerCopilot:MessageTemplateSelector x:Key="MessageTemplateSelector"
                                       RequestTemplate="{StaticResource RequestTemplate}"
                                       ResponseTemplate="{StaticResource ResponseTemplate}"/>

        <Style TargetType="{x:Type ListBoxItem}" x:Key="Style.SlideListBoxItem">
            <Setter Property="FocusVisualStyle" Value="{x:Null}" />
            <Setter Property="Background" Value="Transparent" />
            <Setter Property="Margin" Value="2,10"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type ListBoxItem}">
                        <ContentPresenter VerticalAlignment="Center" HorizontalAlignment="Stretch"/>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>

        <ControlTemplate x:Key="Template.ListBox" TargetType="{x:Type ListBox}">
            <ScrollViewer HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Disabled"
                           CanContentScroll="True" 
                           PanningMode="VerticalOnly" Margin="0,20">
                <VirtualizingStackPanel IsItemsHost="True" Orientation="Vertical" HorizontalAlignment="Stretch" FocusVisualStyle="{x:Null}" />
            </ScrollViewer>
        </ControlTemplate>
    </UserControl.Resources>

    <Grid Background="#EFF0F5">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="80"/>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="80"/>
        </Grid.ColumnDefinitions>
        <Button Grid.Column="0" Style="{StaticResource Style.Button.Image}" VerticalAlignment="Top" HorizontalContentAlignment="Center" HorizontalAlignment="Left" Margin="5" Click="ButtonBase_OnClick"/>
        <Button Grid.Column="2" Style="{StaticResource Style.Button.Image}" HorizontalAlignment="Right" VerticalAlignment="Top" Margin="5"/>
        <Grid Grid.Column="1">
            <Grid.RowDefinitions>
                <RowDefinition Height="70"/>
                <RowDefinition Height="*"/>
                <RowDefinition Height="120"/>
            </Grid.RowDefinitions>
            <TextBlock Grid.Row="0" Text="新对话" VerticalAlignment="Center" HorizontalAlignment="Center" FontSize="28" FontWeight="Bold"/>
            <ListBox x:Name="SessionItemsControl"
                     Grid.Row="1"
                     Background="Transparent" 
                     SelectionMode="Single"
                     BorderThickness="0"  
                     ItemsSource="{Binding Session,Mode=OneWay,RelativeSource={RelativeSource AncestorType=UserControl}}"
                     Template="{StaticResource Template.ListBox}"
                     ItemTemplateSelector="{StaticResource MessageTemplateSelector}"
                     ItemContainerStyle="{StaticResource Style.SlideListBoxItem}">
            </ListBox>
            <controls:Input Grid.Row="2"/>
        </Grid>
    </Grid>
</UserControl>

后台添加测试数据

using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Controls;

namespace LinseerCopilot.Controls
{
    /// <summary>
    /// Main.xaml 的交互逻辑
    /// </summary>
    public partial class Main : UserControl
    {
        public Main()
        {
            InitializeComponent();
            //测试添加数据
            Session = new ObservableCollection<UserControl>();

            var response = new Response();
            var request = new Request(); 
            Session.Add(response);
            Session.Add(request);
            Session.Add(response);
            Session.Add(request);
            Session.Add(response);
            Session.Add(request);
            Session.Add(response);
            Session.Add(request);
            Session.Add(response);
            Session.Add(request);
            Session.Add(response);
            Session.Add(request);
            Session.Add(response);
            Session.Add(request);
            Session.Add(response);
            Session.Add(request);
        }

        public static readonly DependencyProperty SessionProperty = DependencyProperty.Register(nameof(Session), typeof(ObservableCollection<UserControl>), typeof(Main), new PropertyMetadata(null));
        public ObservableCollection<UserControl> Session
        {
            get => (ObservableCollection<UserControl>)GetValue(SessionProperty);
            set => SetValue(SessionProperty, value);
        }

        public static readonly RoutedEvent OnSwitchEvent = EventManager.RegisterRoutedEvent("OnSwitch",
            RoutingStrategy.Bubble,
            typeof(RoutedEventHandler),
            typeof(Main));

        public event RoutedEventHandler OnSwitch
        {
            add => AddHandler(OnSwitchEvent, value);
            remove => RemoveHandler(OnSwitchEvent, value);
        }

        private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
        {
            RaiseEvent(new RoutedEventArgs(OnSwitchEvent, e.Source));
        }

        private void UIElement_OnManipulationBoundaryFeedback(object sender, System.Windows.Input.ManipulationBoundaryFeedbackEventArgs e)
        {
            e.Handled = true;
        }
    }
}


image.png

posted on 2026-07-20 10:39  TanZhiWei  阅读(7)  评论(0)    收藏  举报