我正在使用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;
}
}
Run Code Online (Sandbox Code Playgroud)
SelectionFontFamily,SelectionIsBold等都是对用户控件托管每一个DependencyProperty与OneWayToSource的结合模式.他们被绑定到视图模型,这反过来又绑定了一个观点是,字体的组合框,粗体,斜体,下划线等就可以控制.当RTB中的选择发生变化时,这些控件也会更新以反映已选择的内容.这非常有效.
不幸的是,它以牺牲性能为代价,在选择大量文本时会严重影响性能.选择一切都非常慢,然后使用Shift +箭头键来更改选择非常慢.太慢是不可接受的.
难道我做错了什么?是否有任何关于如何实现将RTB中所选文本的属性反映到绑定控件而不会在此过程中破坏RTB性能的建议?
导致性能问题的两个主要原因是:
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;
}
Run Code Online (Sandbox Code Playgroud)
这将大约快3倍,但为了让最终用户感觉非常活泼,不要在每次更改时立即更新设置.而是在ContextIdle上更新:
bool _queuedChange;
private void richTextBox_SelectionChanged(object sender, RoutedEventArgs e)
{
if(!_queuedChange)
{
_queuedChange = true;
Dispatcher.BeginInvoke(DispatcherPriority.ContextIdle, (Action)(() =>
{
_queuedChange = false;
HandleSelectionChange();
}));
}
}
Run Code Online (Sandbox Code Playgroud)
这会调用HandleSelctionChanged()方法(上面)来实际处理选择更改,但会将调用延迟到ContextIdle调度程序优先级,并且无论有多少选择更改事件进入,也只会对一个更新进行排队.
额外的加速可能
上面的代码在一个DispatcherOperation中生成所有四个GetPropertyValue,这意味着只要四次调用你仍然可能有"滞后".要将延迟减少4倍,每个DispatcherOperation只需要一个GetPropertyValue.因此,例如,第一个DispatcherOperation将调用GetPropertyValue(FontFamilyProperty),将结果存储在字段中,并安排下一个DispatcherOperation以获取字体粗细.每个后续DispatcherOperation都将执行相同的操作.
如果这个额外的加速仍然不够,下一步就是将选择分成更小的部分,在单独的DispatcherOperation中对每个部分调用GetPropertyValue,然后合并你得到的结果.
为了获得绝对最大的平滑度,您可以为GetPropertyValue(只是迭代选择中的ContentElements)实现自己的代码,该代码以增量方式工作,并在检查100个元素后返回.下次你打电话时它会从中断的地方继续.这可以保证您通过改变每个DispatcherOperation完成的工作量来防止任何可辨别的延迟.
线程会有帮助吗?
您在评论中询问是否可以使用线程.答案是您可以使用线程来协调工作,但由于您必须始终将Dispatcher.Invoke返回到主线程以调用GetPropertyValue,因此您仍将在每个GetPropertyValue调用的整个持续时间内阻止UI线程,因此其粒度仍然是一个问题.换句话说,线程并没有真正为你买任何东西,除了可能避免使用状态机将你的工作分成一口大小的块.
| 归档时间: |
|
| 查看次数: |
2429 次 |
| 最近记录: |