我认为你不能纯粹通过 XAML 来做到这一点,你需要在某处编写代码来确定每条消息之间的关系,即消息 n - 1 的作者是否与 n 相同?
我写了一个非常简单的示例,它产生了所需的输出。我的示例和生成的代码 sn-ps 绝不是生产级代码,但它至少应该为您指明正确的方向。
首先,我首先创建了一个非常简单的对象来表示消息:
public class ChatMessage
{
public String Username { get; set; }
public String Message { get; set; }
public DateTime TimeStamp { get; set; }
public Boolean IsConcatenated { get; set; }
}
接下来,我从 ObservableCollection 派生了一个集合,用于在添加每条消息时确定它们之间的关系:
public class ChatMessageCollection : ObservableCollection<ChatMessage>
{
protected override void InsertItem(int index, ChatMessage item)
{
if (index > 0)
item.IsConcatenated = (this[index - 1].Username == item.Username);
base.InsertItem(index, item);
}
}
这个集合现在可以由您的 ViewModel 公开并绑定到您视图中的 ListBox。
有多种方法可以在 XAML 中显示模板化项目。根据您的示例界面,每个项目更改的唯一方面是标题,因此我认为发送最多的是让每个 ListBoxItem 显示一个 HeaderedContentControl,它将根据 IsConcatenated 值显示正确的标题:
<ListBox ItemsSource="{Binding Path=Messages}" HorizontalContentAlignment="Stretch">
<ListBox.ItemTemplate>
<DataTemplate DataType="{x:Type m:ChatMessage}">
<HeaderedContentControl Header="{Binding}">
<HeaderedContentControl.HeaderTemplateSelector>
<m:ChatHeaderTemplateSelector />
</HeaderedContentControl.HeaderTemplateSelector>
<Label Content="{Binding Path=Message}" />
</HeaderedContentControl>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
您会注意到我指定了一个 HeaderTemplateSelector,它负责在两个标题模板之一之间进行选择:
public sealed class ChatHeaderTemplateSelector : DataTemplateSelector
{
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
var chatItem = item as ChatMessage;
if (chatItem.IsConcatenated)
return ((FrameworkElement)container).FindResource("CompactHeader") as DataTemplate;
return ((FrameworkElement)container).FindResource("FullHeader") as DataTemplate;
}
}
最后,这里是定义为视图资源的两个标题模板:
<DataTemplate x:Key="FullHeader">
<Border
Background="Lavender"
BorderBrush="Purple"
BorderThickness="1"
CornerRadius="4"
Padding="2"
>
<DockPanel>
<TextBlock DockPanel.Dock="Left" Text="{Binding Path=Username}" />
<TextBlock DockPanel.Dock="Right" HorizontalAlignment="Right" Text="{Binding Path=TimeStamp, StringFormat='{}{0:HH:mm:ss}'}" />
</DockPanel>
</Border>
</DataTemplate>
<DataTemplate x:Key="CompactHeader">
<Border
Background="Lavender"
BorderBrush="Purple"
BorderThickness="1"
CornerRadius="4"
HorizontalAlignment="Right"
Padding="2"
>
<DockPanel>
<TextBlock DockPanel.Dock="Right" HorizontalAlignment="Right" Text="{Binding Path=TimeStamp, StringFormat='{}{0:HH:mm:ss}'}" />
</DockPanel>
</Border>
</DataTemplate>
同样,这个例子并不完美,可能只是众多可行的例子之一,但至少它应该为您指明正确的方向。