探讨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;
}
}
}

浙公网安备 33010602011771号