2009-08-17 66 views
4

我试图在WPF RichTextBox中显示基本的语法高亮显示。它主要工作,但渲染性能很糟糕。WPF:将格式应用到RichTextBox的快速方法

首先,我天真地试着:

/// <summary> 
/// Main event handler for syntax highlighting. 
/// </summary> 
private void XmlChanged(object sender, TextChangedEventArgs e) 
{ 
    VM.Dirty = true; 
    if (VM.Pretty) 
    { 
     var range = new TextRange(XmlView.Document.ContentStart, XmlView.Document.ContentEnd); 
     Render(range.Text); 
    } 
} 

/// <summary> 
/// Entry point for programmatically resetting the textbox contents 
/// </summary> 
private void Render(string text) 
{ 
    XmlView.TextChanged -= this.XmlChanged; 

    if (VM.Pretty) 
    { 
     var tokens = tokenizer.Tokenize(text); 
     Format(XmlView.Document, tokens); 
    } 

    XmlView.TextChanged += this.XmlChanged;  
} 

private void Format(FlowDocument doc, List<Token> tokens) 
{ 
    var start = doc.ContentStart; 
    foreach (var token in tokens) 
    { 
     TextRange range = new TextRange(start.GetPositionAtOffset(token.StartPosition, LogicalDirection.Forward), 
             start.GetPositionAtOffset(token.EndPosition, LogicalDirection.Forward)); 
     range.ApplyPropertyValue(TextElement.ForegroundProperty, m_syntaxColors[token.Type]); 
    } 
} 

测试与刚刚超过100个令牌的2KB文件,花了1-2秒后,每个按键重绘;显然不能接受。分析表明我的标记器比Format()函数快几个数量级。于是,我尝试了一些双缓冲:

private void Render(string text) 
{ 
    XmlView.TextChanged -= this.XmlChanged; 

    // create new doc offscreen 
    var doc = new FlowDocument(); 
    var range = new TextRange(doc.ContentStart, doc.ContentEnd); 
    range.Text = text; 

    if (VM.Pretty) 
    { 
     var tokens = tokenizer.Tokenize(text); 
     Format(doc, tokens); 
    } 

    // copy to active buffer 
    var stream = new MemoryStream(65536); 
    range.Save(stream, DataFormats.XamlPackage); 
    var activeRange = new TextRange(XmlView.Document.ContentStart, XmlView.Document.ContentEnd); 
    activeRange.Load(stream, DataFormats.XamlPackage); 

    XmlView.TextChanged += this.XmlChanged;  
} 

基准测试表明格式()运行速度稍快渲染屏幕外,但感知性能是现在更惨!

什么是正确的方式去做这件事?

+0

Hi Richard, 您是否曾经为此解决过问题?我遇到类似WPF RichTextBox的问题。 祝好, Alan – 2010-07-09 09:04:51

+0

我的第三次尝试是这样的:(1)创建一个长时间运行的后台线程,在按键时管理它自己的文档(2)的XAML表示,发布到另一个线程观看(3)出列,手动操作XAML文档(即使用字符串解析,而不是FlowDocument)(4),每当你遇到一个好的停止点时,将修订后的XAML发回UI线程并执行TextRange.Load()。这表现好得多,但仍然不是很好。如果我仍然关心这个项目,我会在Reflector中查看它是如何执行的:) – 2010-07-10 04:50:59

回答

1

我试着尽可能多地从方法/循环中取出对象实例,然后传入引用。每次按键每循环呼叫新的次数不少于几次。

+0

不幸的是,TextRange类的Start和End属性是只读的。只有在每次迭代中发现代表正确范围的方法是创建新范围。 – 2009-08-24 14:39:15