gpt4 book ai didi

c# - 动态聊天窗口

转载 作者:太空宇宙 更新时间:2023-11-03 13:16:49 24 4
gpt4 key购买 nike

我对 wpf gui 的性能有疑问。

首先我会解释我做了什么。我从数据库中读取不同的聊天数据,主要是文本,但有时文本中间有一个图标,如笑脸或类似的图标。或者,没有文本只有图像。

我通过使用 Flowdocument 和带内联的 Textblock 完成了这一切。哦,我忘了,我用的是wpf,抱歉。

这很好用,但目前 Flowdocument 将被绘制到 RichTextbox 或 FlowdocumentReader,这需要很长时间并且 gui 卡住。我考虑过虚拟化,但 RichTextBox 不使用它。所以我的下一个想法是使用一个列表框并为每个 Chatbubble 设置一个 Richtextbox 项目。一个聊天可以包含大约 20.000 个聊天气泡。所以现在我想使用数据绑定(bind),但我找不到绑定(bind)文本 block 内联的方法。

现在是一些代码。

<DataTemplate x:Key="MessageDataTemplate" DataType="{x:Type classes:Message}">
<Grid>
<RichTextBox x:Name="rtbChat"
SpellCheck.IsEnabled="False"
VerticalScrollBarVisibility="Auto"
VerticalContentAlignment="Stretch">
<FlowDocument
FontFamily="Century Gothic"
FontSize="12"
FontStretch="UltraExpanded">
<Paragraph>
<Figure>
<BlockUIContainer>
<Border>
<Border>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="150"/>
<ColumnDefinition Width="80"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="15"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock x:Name="tUser"
Foreground="Gray"
TextAlignment="Right"
FontSize="10"
Grid.Row="0"
Grid.Column="1"
Text="{Binding displayUserName}"/>

<TextBlock x:Name="tTime"
Foreground="Gray"
TextAlignment="Left"
FontSize="10"
Grid.Row="0"
Grid.Column="0"
Text="{Binding sendTime}"/>

<TextBlock x:Name="tMessage"
Foreground="Black"
TextAlignment="Justify"
FontSize="12"
Height="NaN"
TextWrapping="Wrap"
Grid.Row="1"
Grid.Column="0"
Grid.ColumnSpan="2"
Text="{Binding contentText}" />
<Image x:Name="tImage"
Grid.Row="1"
Grid.Column="0"
Grid.ColumnSpan="2"
Height="NaN"
Source="{Binding imageSend}"/>
</Grid>
</Border>
</Border>
</BlockUIContainer>
</Figure>
</Paragraph>
</FlowDocument>
</RichTextBox>
</Grid>
</DataTemplate>

所以这不是最终版本,我正在将其从源代码移植到 xaml,此时缺少一些 setter 。

我对时间进行了基准测试,一切正常,sqlite 为 10 毫秒,构建 FlowDocument 大约需要 4 秒,但在 RichTextBox 中绘制 FlowDocument 最多需要 5 分钟。我知道这就是为什么孔框被绘制(paint)的原因,也是不可见的部分。

我希望这是可以理解的,如果不能问我 :)

这里是移植到 xaml 之前的源代码。

        var rtBox = new RichTextBox
{
//IsEnabled = false,
BorderThickness = new Thickness(0, 0, 0, 0)
};
var doc = new FlowDocument();

Contact contact = null;
contact = _mess.remote_resource != "" ? _contacts.Find(x => x._jid == _mess.remote_resource) : _contacts.Find(x => x._jid == _mess.key_remote_jid);

var para = new Paragraph();

//--- Style of the message -----
para.Padding = new Thickness(0);

BlockUIContainer blockUI = new BlockUIContainer();
blockUI.Margin = new Thickness(0, 0, 0, 0);
blockUI.Padding = new Thickness(0);
blockUI.TextAlignment = _mess.key_from_me == 1 ? TextAlignment.Right : TextAlignment.Left;

Border bShadow = new Border();
bShadow.Width = 231;
bShadow.BorderBrush = Brushes.LightGray;
bShadow.BorderThickness = new Thickness(0, 0, 0, 1);

Border b2 = new Border();
b2.Width = 230;
b2.BorderBrush = Brushes.Gray;
b2.Background = Brushes.White;
b2.BorderThickness = new Thickness(0.5);
b2.Padding = new Thickness(2);

Grid g = new Grid();
g.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(150,GridUnitType.Star) });
g.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(80) });
g.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(15) });
g.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(25,GridUnitType.Auto) });

TextBlock tUser = new TextBlock()
{
Foreground = Brushes.Gray,
TextAlignment = TextAlignment.Right,
FontSize = 10,
};
tUser.SetValue(Grid.RowProperty, 0);
tUser.SetValue(Grid.ColumnProperty, 1);
if(contact != null)
tUser.Text = _mess.key_from_me == 1 ? "ich" : (contact._displayName == "" ? Whatsapp.Contacs.convertJidToNumber(_mess.remote_resource) : contact._displayName);
else
{
tUser.Text = Whatsapp.Contacs.convertJidToNumber(_mess.remote_resource);
}

TextBlock tTime = new TextBlock()
{
Foreground = Brushes.Gray,
TextAlignment = TextAlignment.Left,
FontSize = 10,
};
tTime.SetValue(Grid.RowProperty, 0);
tTime.SetValue(Grid.ColumnProperty, 0);
tTime.Text = UnixTime.TimeReturnUnix2DateUtc(_mess.timestamp, timeZone).ToString();

TextBlock tMessage = new TextBlock()
{
Foreground = Brushes.Black,
TextAlignment = TextAlignment.Justify,
FontSize = 12,
Height = Double.NaN,
TextWrapping = TextWrapping.Wrap

};

tMessage.SetValue(Grid.RowProperty, 1);
tMessage.SetValue(Grid.ColumnProperty, 0);
tMessage.SetValue(Grid.ColumnSpanProperty, 2);



for (var i = 0; i < _mess.data.Length; i += Char.IsSurrogatePair(_mess.data, i) ? 2 : 1)
{
var x = Char.ConvertToUtf32(_mess.data, i);

if (EmojiConverter.EmojiDictionary.ContainsKey(x))
{

//Generate new Image from Emoji
var emoticonImage = new Image
{
Width = 20,
Height = 20,
Margin = new Thickness(0, -5, 0, -5),
Source = EmojiConverter.EmojiDictionary[x]
};

//add grafik to FlowDocument
tMessage.Inlines.Add(emoticonImage);
}
else
{
tMessage.Inlines.Add(new Run("" + _mess.data[i]));
}
}

g.Children.Add(tUser);
g.Children.Add(tTime);
g.Children.Add(tMessage);

b2.Child = g;
bShadow.Child = b2;

blockUI.Child = bShadow;

Figure fig = new Figure(blockUI);
fig.Padding = new Thickness(0);
fig.Margin = new Thickness(0);
fig.Height = new FigureLength(0, FigureUnitType.Auto);

para.Inlines.Add(fig);

doc.Blocks.Add(para);
rtBox.Document = doc;
msgList.Add(rtBox);

问候并感谢您的帮助。

最佳答案

一种方法是使用 ListBox 进行虚拟化, 当然。可以说更好的方法是动态加载所需的消息或创建您自己的虚拟化控件(默认 ListBox 虚拟化的问题包括您必须一次滚动整个项目才能使虚拟化工作......这可能会很糟糕在某些情况下,从用户体验的角度来看有点。)

从它仍然需要永远加载的声音来看,您设置的虚拟化无法正常工作...

要使虚拟化工作,您需要做的主要事情是您需要让列表框模板内的滚动查看器具有 CanContentScroll=True .即:

<ListBox ScrollViewer.CanContentScroll="True" .... >

或者给出 ListBox类似于下面的模板:

<ControlTemplate>
<Border BorderBrush="{TemplateBinding Border.BorderBrush}"
BorderThickness="{TemplateBinding Border.BorderThickness}"
Background="{TemplateBinding Panel.Background}"
SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}">
<ScrollViewer Focusable="False"
Padding="{TemplateBinding Control.Padding}"
MaxHeight="{TemplateBinding Control.MaxHeight}"
CanContentScroll="True">
<ItemsPresenter SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}" />
</ScrollViewer>
</Border>
</ControlTemplate>

另外,除非你想实际选择以前的消息,否则可能是 ListBox不是你想要的,你实际上想要一个 ItemsControl ?参见 Virtualizing an ItemsControl?有关更多信息。

补充 1 - 平滑滚动 + 虚拟化:

见下文 - 如果您还想要平滑滚动,可能值得一看 TreeView - 参见 http://classpattern.com/smooth-scrolling-with-virtualization-wpf-list.html#.VBHWtfldXSg - 虽然我不能保证这是否真的有效,但我自己发现了它!

补充 2 - 澄清 RE 需要的元素

如我在下面的评论中所述,如果您不编辑所有内容,则可以删除所有标签:

<Grid><RichTextBox><FlowDocument><Paragraph><Figure>

在数据模板中。您可能无法绑定(bind) Text发送给 contentText 的消息在 DataTemplate 中,并且必须有一些幕后代码来动态生成 TextBlock 的内联。

补充 3 - 如何绑定(bind) TextBlock 以包含来自 XAML 的图像等

好吧,总的来说(忽略一些样式),我建议如下:

<DataTemplate x:Key="MessageDataTemplate" DataType="{x:Type classes:Message}">
<Border>
<Border>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="150"/>
<ColumnDefinition Width="80"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="15"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock x:Name="tUser"
Foreground="Gray"
TextAlignment="Right"
FontSize="10"
Grid.Row="0"
Grid.Column="1"
Text="{Binding displayUserName}" />
<TextBlock x:Name="tTime"
Foreground="Gray"
TextAlignment="Left"
FontSize="10"
Grid.Row="0"
Grid.Column="0"
Text="{Binding sendTime}" />
<TextBlock x:Name="tMessage"
Foreground="Black"
TextAlignment="Justify"
FontSize="12"
Height="NaN"
TextWrapping="Wrap"
Grid.Row="1"
Grid.Column="0"
Grid.ColumnSpan="2"
classes:TextBlockInlineBinder.Inlines="{Binding contentInlines}" />
<Image x:Name="tImage"
Grid.Row="1"
Grid.Column="0"
Grid.ColumnSpan="2"
Height="NaN"
Source="{Binding imageSend}" />
</Grid>
</Border>
</Border>
</DataTemplate>

注意 classes:TextBlockInlineBinder.Inlines="{Binding contentInlines}" 这行关于留言TextBlock .这是为了能够绑定(bind)到 Inlines ...基本上,这不是依赖属性,因此不能直接绑定(bind)到!

相反,我们可以使用自定义静态类 TextBlockInlineBinder下面创建一个静态依赖属性以添加到我们的 TextBlock ,当它更新时,它运行 InlinesChanged更新内联的方法:

public static class TextBlockInlineBinder
{
#region Static DependencyProperty Implementation

public static readonly DependencyProperty InlinesProperty =
DependencyProperty.RegisterAttached("Inlines",
typeof(IEnumerable<Inline>),
typeof(TextBlockInlineBinder),
new UIPropertyMetadata(new Inline[0], InlinesChanged));

public static string GetInlines(DependencyObject obj)
{
return (string)obj.GetValue(InlinesProperty);
}

public static void SetInlines(DependencyObject obj, string value)
{
obj.SetValue(InlinesProperty, value);
}

#endregion

private static void InlinesChanged(DependencyObject sender,
DependencyPropertyChangedEventArgs e)
{
var value = e.NewValue as IEnumerable<Inline>;
var textBlock = sender as TextBlock;
textBlock.Inlines.Clear();
textBlock.Inlines.AddRange(value);
}
}

最后,绑定(bind)(我已经绑定(bind)到您的 contentInlines 类的 Message 属性)需要是 IEnumerable<Inline> 类型,例如:

public IEnumerable<Inline> contentInlines
{
get {
var inlines = new List<Inline>();
for (var i = 0; i < _mess.data.Length; i += Char.IsSurrogatePair(_mess.data, i) ? 2 : 1)
{
var x = Char.ConvertToUtf32(_mess.data, i);

if (EmojiConverter.EmojiDictionary.ContainsKey(x))
{
//Generate new Image from Emoji
var emoticonImage = new Image
{
Width = 20,
Height = 20,
Margin = new Thickness(0, -5, 0, -5),
Source = EmojiConverter.EmojiDictionary[x]
};
inlines.Add(emoticonImage);
}
else
{
inlines.Add(new Run("" + _mess.data[i]));
}
}
return inlines;
}
}

关于c# - 动态聊天窗口,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25790907/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com