我想要创建XAML聊天接口,它将根据邻居的不同显示不同的消息。下面是一个例子:

我认为ListBox控件最适合这种情况。我也在考虑不同的控件,如FlowDocumentReader,但我从未使用过它们。另外,我需要提到消息的文本应该是可选择的(跨多个消息),我不知道如何使用ListBox实现这一点。
更新:这里的要点是,如果一方(在本例中是viking)在一行中发送一些消息,则接口应该将这些消息连接起来(使用瘦消息头而不是完整消息头)。因此,消息头的外观取决于先前的消息是否由同一个人发送。
发布于 2011-07-15 00:44:15
如果您只是对标头的格式(完整的还是小的)感兴趣,那么在ListBox/ListView/ItemsControl绑定中使用PreviousData就可以了(正如anivas所指出的)。
但是,由于您添加了支持跨多个消息进行选择的功能,因此,据我所知,这基本上排除了ItemsControl及其派生的类。您将不得不使用类似于FlowDocument的东西。
不幸的是,FlowDocument没有ItemsSource属性。有一些解决方法的例子,比如使用流文档和数据绑定创建灵活的UI,但是这个实现很大程度上使我的VS2010崩溃(我没有调查原因,可能是一个简单的修复)。
,这是我怎么做的,
首先,在设计器中设计FlowDocument块,当您满意时,将它们移动到设置x:Shared="False"的资源中。这将使您能够创建资源的多个实例,而不是反复使用相同的实例。然后,使用ObservableCollection作为FlowDocument的“源”,并订阅CollectionChanged事件,在均衡器中,您将获得资源的一个新实例,检查是否需要完整的或小的标题,然后将这些块添加到FlowDocument中。您还可以添加删除等的逻辑。
示例实现
<!-- xmlns:Collections="clr-namespace:System.Collections;assembly=mscorlib" -->
<Window.Resources>
<Collections:ArrayList x:Key="blocksTemplate" x:Shared="False">
<!-- Full Header -->
<Paragraph Name="fullHeader" Margin="5" BorderBrush="LightGray" BorderThickness="1" TextAlignment="Right">
<Figure HorizontalAnchor="ColumnLeft" BaselineAlignment="Center" Padding="0" Margin="0">
<Paragraph>
<Run Text="{Binding Sender}"/>
</Paragraph>
</Figure>
<Run Text="{Binding TimeSent, StringFormat={}{0:HH:mm:ss}}"/>
</Paragraph>
<!-- Small Header -->
<Paragraph Name="smallHeader" Margin="5" TextAlignment="Right">
<Run Text="{Binding TimeSent, StringFormat={}{0:HH:mm:ss}}"/>
</Paragraph>
<!-- Message -->
<Paragraph Margin="5">
<Run Text="{Binding Message}"/>
</Paragraph>
</Collections:ArrayList>
</Window.Resources>
<Grid>
<FlowDocumentScrollViewer>
<FlowDocument Name="flowDocument"
FontSize="14" FontFamily="Georgia"/>
</FlowDocumentScrollViewer>
</Grid>后面的代码可以是以下代码
public ObservableCollection<ChatMessage> ChatMessages
{
get;
set;
}
public MainWindow()
{
InitializeComponent();
ChatMessages = new ObservableCollection<ChatMessage>();
ChatMessages.CollectionChanged += ChatMessages_CollectionChanged;
}
void ChatMessages_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
ArrayList itemTemplate = flowDocument.TryFindResource("blocksTemplate") as ArrayList;
if (e.Action == NotifyCollectionChangedAction.Add)
{
foreach (ChatMessage chatMessage in e.NewItems)
{
foreach (Block block in itemTemplate)
{
bool addBlock = true;
int index = ChatMessages.IndexOf(chatMessage);
if (block.Name == "fullHeader" &&
(index > 0 && ChatMessages[index].Sender == ChatMessages[index - 1].Sender))
{
addBlock = false;
}
else if (block.Name == "smallHeader" &&
(index == 0 || ChatMessages[index].Sender != ChatMessages[index - 1].Sender))
{
addBlock = false;
}
if (addBlock == true)
{
block.DataContext = chatMessage;
flowDocument.Blocks.Add(block);
}
}
}
}
}在我的样本中,ChatMessage只是
public class ChatMessage
{
public string Sender
{
get;
set;
}
public string Message
{
get;
set;
}
public DateTime TimeSent
{
get;
set;
}
}这将使您能够在消息中选择您喜欢的文本。

如果您使用的是MVVM,您可以创建附加行为,而不是后面的代码,我在这里做了一个类似场景的示例实现:?
另外,用于FlowDocument的MSDN页面非常有用:http://msdn.microsoft.com/en-us/library/aa970909.aspx。
发布于 2011-07-13 17:31:42
假设您的ItemTemplate是StackPanel of TextBlock头和TextBlock消息,您可以使用MultiBinding可见性Converter将标题隐藏为:
<TextBlock Text="{Binding UserName}">
<TextBlock.Visibility>
<MultiBinding Converter="{StaticResource headerVisibilityConverter}">
<Binding RelativeSource="{RelativeSource PreviousData}"/>
<Binding/>
</MultiBinding>
</TextBlock.Visibility>
</TextBlock> IMultiValueConverter逻辑是这样的:
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
var previousMessage = values[0] as MessageItem;
var currentMessage = values[1] as MessageItem;
if ((previousMessage != null) && (currentMessage != null))
{
return previousMessage.UserName.Equals(currentMessage.UserName) ? Visibility.Hidden : Visibility.Visible;
}
return Visibility.Visible;
} 发布于 2011-07-10 10:21:09
尝试给出一个提示--伪代码,如
public abstract class Message {/*Implementation*/
public enum MessageTypeEnum {Client, Viking, None};
public abstract MessageTypeEnum MessageType {get;}
}
public class ClientMessage : Message {
/*Client message concrete implementation.*/
public override MessageTypeEnum MessageType
{
get {
return MessageTypeEnum.Client;
}
}
}
public class VikingMessage : Message
{
/ *Viking message concrete implementation*/
public override MessageTypeEnum MessageType
{
get {
return MessageTypeEnum.Viking;
}
}
}之后,在reference代码中,在绑定控件上使用XAML属性Converter,您可以在其中分配一个实现IValueConverter的类引用。以下是链接
网上资源:
转换器
在那里,您可以在UI/ModelView之间转换类型。
希望这能有所帮助。
https://stackoverflow.com/questions/6640338
复制相似问题