WPF-具有可选文本的可绑定聊天视图
我想使用WPF创建一个简单的文本聊天应用程序。当然,用户应该能够选择并复制文本。 它非常易于使用,例如,将ListSource与ItemsSource绑定到消息。外观可以调整,但是主要问题是文本选择。只能在一个控件(一条消息)中选择文本。
此刻,我使用WebBrowser来显示消息。所以我有大量的HTML + JS + CSS。我想我什至不必说这有多可怕。
您能指出我正确的方向吗?
答案 0 :(得分:3)
您可以看看FlowDocument
。此类可用于自定义类似于ItemsControl
的块(段落)的外观,它也可以包含UI控件(以备不时之需)。当然,文本选择将适用于整个文档。
不幸的是,FlowDocument
不支持绑定,因此您必须为此编写一些代码。
让我给你一个例子。您可以使用Behavior
命名空间中的System.Windows.Interactivity
为FlowDocument
类创建可重用的功能扩展。
这是您可以开始的:
<FlowDocumentScrollViewer>
<FlowDocument ColumnWidth="400">
<i:Interaction.Behaviors>
<myApp:ChatFlowDocumentBehavior Messages="{Binding Messages}">
<myApp:ChatFlowDocumentBehavior.ItemTemplate>
<DataTemplate>
<myApp:Fragment>
<Paragraph Background="Aqua" BorderBrush="BlueViolet" BorderThickness="1"/>
</myApp:Fragment>
</DataTemplate>
</myApp:ChatFlowDocumentBehavior.ItemTemplate>
</myApp:ChatFlowDocumentBehavior>
</i:Interaction.Behaviors>
</FlowDocument>
</FlowDocumentScrollViewer>
(i
命名空间为xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
)
因此我们的ChatFlowDocumentBehavior
具有可绑定的Messages
属性,用于显示聊天消息。另外,还有一个ItemTemplate
属性,您可以在其中定义单个聊天消息的外观。
请注意Fragment
类。这只是一个简单的包装器(下面的代码)。 DataTemplate
类将不接受Paragraph
作为其内容,但是我们需要将项目设为Paragraph
。
您可以根据需要配置Paragraph
(例如颜色,字体,可能还有其他子项或控件等)
因此,Fragment
类是一个简单的包装器:
[ContentProperty("Content")]
sealed class Fragment : FrameworkElement
{
public static readonly DependencyProperty ContentProperty = DependencyProperty.Register(
nameof(Content),
typeof(FrameworkContentElement),
typeof(Fragment));
public FrameworkContentElement Content
{
get => (FrameworkContentElement)GetValue(ContentProperty);
set => SetValue(ContentProperty, value);
}
}
行为类具有更多代码,但并不复杂。
sealed class ChatFlowDocumentBehavior : Behavior<FlowDocument>
{
// This is our dependency property for the messages
public static readonly DependencyProperty MessagesProperty =
DependencyProperty.Register(
nameof(Messages),
typeof(ObservableCollection<string>),
typeof(ChatFlowDocumentBehavior),
new PropertyMetadata(defaultValue: null, MessagesChanged));
public ObservableCollection<string> Messages
{
get => (ObservableCollection<string>)GetValue(MessagesProperty);
set => SetValue(MessagesProperty, value);
}
// This defines how our items will look like
public DataTemplate ItemTemplate { get; set; }
// This method will be called by the framework when the behavior attaches to flow document
protected override void OnAttached()
{
RefreshMessages();
}
private static void MessagesChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (!(d is ChatFlowDocumentBehavior b))
{
return;
}
if (e.OldValue is ObservableCollection<string> oldValue)
{
oldValue.CollectionChanged -= b.MessagesCollectionChanged;
}
if (e.NewValue is ObservableCollection<string> newValue)
{
newValue.CollectionChanged += b.MessagesCollectionChanged;
}
// When the binding engine updates the dependency property value,
// update the flow doocument
b.RefreshMessages();
}
private void MessagesCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
AddNewItems(e.NewItems.OfType<string>());
break;
case NotifyCollectionChangedAction.Reset:
AssociatedObject.Blocks.Clear();
break;
}
}
private void RefreshMessages()
{
if (AssociatedObject == null)
{
return;
}
AssociatedObject.Blocks.Clear();
if (Messages == null)
{
return;
}
AddNewItems(Messages);
}
private void AddNewItems(IEnumerable<string> items)
{
foreach (var message in items)
{
// If the template was provided, create an instance from the template;
// otherwise, create a default non-styled paragraph instance
var newItem = (Paragraph)(ItemTemplate?.LoadContent() as Fragment)?.Content ?? new Paragraph();
// This inserts the message text directly into the paragraph as an inline item.
// You might want to change this logic.
newItem.Inlines.Add(message);
AssociatedObject.Blocks.Add(newItem);
}
}
}
以此为起点,您可以扩展行为以适合您的需求。例如。添加事件处理逻辑以删除或重新排序消息,实现全面的消息模板等。
几乎总是可以使用XAML功能(样式,模板,资源等)以更少的代码来实现该功能。但是,对于缺少的功能,您只需要使用代码即可。但是在这种情况下,请始终尝试避免在视图中隐藏代码。为此创建Behavior
或附加属性。
答案 1 :(得分:0)
我认为,文本框应该为您提供所需的内容。您将需要进行样式设计,使其看起来像您想要的,但是这里是代码: XAML:
<TextBox Text="{Binding AllMessages}"/>
ViewModel:
public IEnumerable<string> Messages { get; set; }
public string AllMessages => GetAllMessages();
private string GetAllMessages()
{
var builder = new StringBuilder();
foreach (var message in Messages)
{
//Add in whatever for context
builder.AppendLine(message);
}
return builder.ToString();
}
您可能希望使用RichTextBox以获得更好的格式。