4

J'essaie d'afficher la coloration syntaxique de base dans un RichTextBox WPF. Cela fonctionne principalement, mais la performance de rendu est horrible.WPF: façon rapide d'appliquer le formatage à RichTextBox

D'abord, je naïvement essayé:

/// <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]); 
    } 
} 

test sur un document de 2 Ko avec un peu plus de 100 jetons, il a fallu 1-2 secondes pour redessiner après chaque pression de touche; clairement pas acceptable. Le profilage montrait que mon tokenizer était plus rapide que la fonction Format(). J'ai donc essayé une double mise en mémoire tampon:

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;  
} 

benchmarks montrent que Format() fonctionne rendu un peu plus rapide offscreen, mais la performance perçue est encore pire maintenant!

Quelle est la bonne façon de procéder?

+0

Bonjour Richard, Avez-vous déjà trouvé une solution à ce problème? J'ai des problèmes similaires avec le RichTextBox de WPF. Cordialement, Alan –

+0

Ma 3ème tentative ressemblait à ceci: (1) créer un thread d'arrière-plan de longue durée qui gère sa propre représentation XAML du document (2) sur une frappe, poster dans une file d'attente que l'autre thread est En regardant (3) sur dequeue, manipulez manuellement le document XAML (utilisez l'analyse de chaîne, pas FlowDocument) (4) chaque fois que vous atteignez un bon point d'arrêt, postez le XAML révisé dans le thread UI et faites un TextRange.Load(). Cela a fonctionné beaucoup mieux, mais toujours pas génial. Si je tenais encore au projet, je ferais un tour dans Reflector pour voir comment VS le fait :) –

Répondre

1

J'essaierais de retirer autant d'instanciation d'objet de la méthode/boucle que possible et de passer des références à la place. Vous appelez nouveau plusieurs fois par boucle par frappe.

+0

Malheureusement, les propriétés Start et End de la classe TextRange sont en lecture seule. La seule façon que j'ai trouvé pour représenter la plage correcte à chaque itération est d'en créer de nouvelles. –

Questions connexes