gpt4 book ai didi

WPF RichTextBox 选择更改的性能

转载 作者:行者123 更新时间:2023-12-02 06:50:06 24 4
gpt4 key购买 nike

我正在使用 WPF RichTextBox 开发文字处理器类型的应用程序。我使用 SelectionChanged 事件来确定 RTB 中当前选择的字体、字体粗细、样式等,使用以下代码:

private void richTextBox_SelectionChanged(object sender, RoutedEventArgs e)
{
TextSelection selection = richTextBox.Selection;

if (selection.GetPropertyValue(FontFamilyProperty) != DependencyProperty.UnsetValue)
{
//we have a single font in the selection
SelectionFontFamily = (FontFamily)selection.GetPropertyValue(FontFamilyProperty);
}
else
{
SelectionFontFamily = null;
}

if (selection.GetPropertyValue(FontWeightProperty) == DependencyProperty.UnsetValue)
{
SelectionIsBold = false;
}
else
{
SelectionIsBold = (FontWeights.Bold == ((FontWeight)selection.GetPropertyValue(FontWeightProperty)));
}

if (selection.GetPropertyValue(FontStyleProperty) == DependencyProperty.UnsetValue)
{
SelectionIsItalic = false;
}
else
{
SelectionIsItalic = (FontStyles.Italic == ((FontStyle)selection.GetPropertyValue(FontStyleProperty)));
}

if (selection.GetPropertyValue(Paragraph.TextAlignmentProperty) != DependencyProperty.UnsetValue)
{
SelectionIsLeftAligned = (TextAlignment)selection.GetPropertyValue(Paragraph.TextAlignmentProperty) == TextAlignment.Left;
SelectionIsCenterAligned = (TextAlignment)selection.GetPropertyValue(Paragraph.TextAlignmentProperty) == TextAlignment.Center;
SelectionIsRightAligned = (TextAlignment)selection.GetPropertyValue(Paragraph.TextAlignmentProperty) == TextAlignment.Right;
SelectionIsJustified = (TextAlignment)selection.GetPropertyValue(Paragraph.TextAlignmentProperty) == TextAlignment.Justify;
}
}

SelectionFontFamily、SelectionIsBold 等都是托管 UserControl 上的 DependencyProperty,绑定(bind)模式为 OneWayToSource。它们绑定(bind)到 ViewModel,而 ViewModel 又绑定(bind)了一个 View,该 View 上有字体组合框、粗体、斜体、下划线等控件。当 RTB 中的选择发生更改时,这些控件也会更新以反射(reflect)所选择的内容。这效果很好。

不幸的是,它的工作是以牺牲性能为代价的,当选择大量文本时,性能会受到严重影响。选择所有内容的速度明显很慢,然后使用 Shift+箭头键之类的键来更改选择也非常慢。太慢了,无法接受。

我做错了什么吗?对于如何实现将 RTB 中所选文本的属性反射(reflect)到绑定(bind)控件而不影响 RTB 在此过程中的性能,是否有任何建议?

最佳答案

导致性能问题的两个主要原因是:

  1. 您调用 Selection.GetPropertyValue() 的次数超过了必要的次数
  2. 每次选择更改时都需要重新计算

GetPropertyValue() 方法必须在内部扫描文档中的每个元素,这使得速度变慢。因此,不要使用相同的参数多次调用它,而是存储返回值:

private void HandleSelectionChange()
{
var family = selection.GetPropertyValue(FontFamilyProperty);
var weight = selection.GetPropertyValue(FontWeightProperty);
var style = selection.GetPropertyValue(FontStyleProperty);
var align = selection.GetPropertyValue(Paragraph.TextAlignmentProperty);

var unset = DependencyProperty.UnsetValue;

SelectionFontFamily = family!=unset ? (FontFamily)family : null;
SelectionIsBold = weight!=unset && (FontWeight)weight == FontWeight.Bold;
SelectionIsItalic = style!=unset && (FontStyle)style == FontStyle.Italic;

SelectionIsLeftAligned = align!=unset && (TextAlignment)align == TextAlignment.Left;
SelectionIsCenterAligned = align!=unset && (TextAlignment)align == TextAlignment.Center;
SelectionIsRightAligned = align!=unset && (TextAlignment)align == TextAlignment.Right;
SelectionIsJustified = align!=unset && (TextAlignment)align == TextAlignment.Justify;
}

这将快大约 3 倍,但为了让最终用户感觉非常敏捷,不要在每次更改时立即更新设置。相反,更新 ContextIdle:

bool _queuedChange;

private void richTextBox_SelectionChanged(object sender, RoutedEventArgs e)
{
if(!_queuedChange)
{
_queuedChange = true;
Dispatcher.BeginInvoke(DispatcherPriority.ContextIdle, (Action)(() =>
{
_queuedChange = false;
HandleSelectionChange();
}));
}
}

这将调用 HandleSelctionChanged() 方法(上面)来实际处理选择更改,但会延迟调用,直到 ContextIdle 调度程序优先级,并且无论传入多少个选择更改事件,也仅对一个更新进行排队.

可能进一步加速

上面的代码使所有四个 GetPropertyValue 都在一个 DispatcherOperation 中,这意味着只要这四个调用,您可能仍然会有“滞后”。为了将延迟再减少 4 倍,每个 DispatcherOperation 仅创建一个 GetPropertyValue。因此,例如,第一个 DispatcherOperation 将调用 GetPropertyValue(FontFamilyProperty),将结果存储在字段中,并安排下一个 DispatcherOperation 来获取字体粗细。随后的每个 DispatcherOperation 都会执行相同的操作。

如果这种额外的加速仍然不够,下一步是将选择分成更小的部分,在单独的 DispatcherOperation 中对每个部分调用 GetPropertyValue,然后合并获得的结果。

为了获得绝对最大的平滑度,您可以实现自己的 GetPropertyValue 代码(只需迭代选择中的 ContentElements),该代码增量工作并在检查(例如 100 个元素)后返回。下次您调用它时,它会从上次中断的地方继续。这将保证您能够通过改变每个 DispatcherOperation 完成的工作量来防止任何明显的滞后。

线程有帮助吗?

您在评论中询问是否可以使用线程来完成此操作。答案是,您可以使用线程来编排工作,但由于您必须始终通过 Dispatcher.Invoke 返回主线程来调用 GetPropertyValue,因此您仍然会在每次 GetPropertyValue 调用的整个持续时间内阻塞您的 UI 线程,因此其粒度仍然是一个问题。换句话说,线程实际上并没有给你带来任何好处,除了可能避免使用状态机将你的工作分割成小块的能力之外。

关于WPF RichTextBox 选择更改的性能,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3472638/

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