WPF RichTextBox选择更改性能
我正在使用WPF RichTextBox开发一个文字处理器类型的应用程序。我使用SelectionChanged事件通过以下代码计算RTB中当前所选内容的字体、字体大小、样式等:WPF RichTextBox选择更改性能,wpf,wpf-controls,Wpf,Wpf Controls,我正在使用WPF RichTextBox开发一个文字处理器类型的应用程序。我使用SelectionChanged事件通过以下代码计算RTB中当前所选内容的字体、字体大小、样式等: private void richTextBox_SelectionChanged(object sender, RoutedEventArgs e) { TextSelection selection = richTextBox.Selection; if (selectio
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、SelectionBold等都是托管用户控件的从属属性,绑定模式为OneWayToSource。它们被绑定到ViewModel,而ViewModel又绑定了一个视图,该视图上有字体组合框、粗体、斜体、下划线等控件。当RTB中的选择更改时,这些控件也会更新以反映所选内容。这很有效
不幸的是,它是以牺牲性能为代价的,这在选择大量文本时会受到严重影响。选择所有内容都非常慢,然后使用Shift+箭头键等更改选择非常慢。太慢了,不能接受
我做错什么了吗?对于如何实现将RTB中所选文本的属性反映到绑定控件而不影响RTB在该过程中的性能,是否有任何建议 性能问题的两个主要原因是:
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();
}));
}
}
这将调用handleSelectionChanged()
方法(如上)来实际处理选择更改,但会将调用延迟到ContextIdle dispatcher priority,并且无论出现多少选择更改事件,都只对一次更新进行排队
可能的额外加速
上面的代码在一个DispatcherOperation中生成所有四个GetPropertyValue,这意味着您可能仍然有一个“滞后”,只要这四个调用。若要将延迟再减少4倍,请在每个DispatcherOperation中仅设置一个GetPropertyValue。因此,例如,第一次DispatcherOperation将调用GetPropertyValue(FontFamilyProperty),将结果存储在字段中,并安排下一次DispatcherOperation以获取字体权重。每个后续DispatcherOperation都将执行相同的操作
如果这个额外的加速仍然不够,下一步将是将选择分割成更小的部分,在单独的DispatcherOperation中对每个部分调用GetPropertyValue,然后合并得到的结果
为了获得绝对最大平滑度,您可以为GetPropertyValue实现自己的代码(只需迭代选择中的ContentElements),该代码以增量方式工作,并在检查(比如)100个元素后返回。下次你给它打电话时,它会从中断的地方恢复过来。这将保证您通过改变每个DispatcherOperation完成的工作量来防止任何明显的延迟
线程化有帮助吗?
您在评论中询问是否可以使用线程来实现这一点。答案是,您可以使用线程来编排工作,但由于您必须始终使用Dispatcher.Invoke返回主线程以调用GetPropertyValue,因此在每次GetPropertyValue调用的整个过程中,您仍然会阻塞UI线程,因此其粒度仍然是一个问题。换言之,线程并不能真正为您带来任何东西,除了避免使用状态机将您的工作分割成小块之外。感谢您的代码,这确实提高了您所说的速度,但当RTB中有相当数量的文本(比如15页左右)时,它仍然相当滞后。当您高亮显示所有文本并使用箭头键取消选择行/词时,它仍然滞后到相当明显的程度。这样更好了,但还是不行。像这样的东西可以放在一个线程中吗?我扩展了我的答案,让你知道进一步加速需要什么,以及线程是否有用。非常好的建议,谢谢Ray。我会更详细地研究你的建议。