WPF 開發,優化 AvalonEdit 顯示單行超長文字的效能。

Soar、毅發表於2022-03-02

路遙工具箱內建了一個名為“JSON 格式化”的功能。通過該功能可以將 JSON 字串進行美化和簡化(設定縮排和取消縮排)。

該功能上線後收到很多客戶的反饋:大部分經由網路傳輸的JSON訊息都是未縮排的,在嘗試使用JSON美化工具對其進行載入時發生了卡死的情況。經筆者實測:一個兩兆左右的 JSON 檔案需要三分鐘左右去渲染。

在展示單行超長文字時,包括 Visual Studio、Notpad2 在內的軟體表現都不盡人意。卡頓在載入和拖動時都會發生,特別是需要自動折行的時候。

原生的 TextBox 控制元件在面對這麼長的單行文字時也會出現卡頓。如果取消自動折行(設定 WrapText 為 False)可以改善這種情況。AvalonEdit 支援調整折行顯示,但仍舊需要較長的時間去渲染。

使用 TruncateLongLines 改善這個情況

經過一番探索,筆者在官方 GitHub 倉庫中找到了一個相關的 Issues ,Performance issue with word wraphttps://github.com/icsharpcode/AvalonEdit/issues/11 。其中提到了一個名為 TruncateLongLines 的型別:

public class TruncateLongLines : VisualLineElementGenerator
{
    const int maxLength = 2000;
    const string ellipsis = "...";
    const int charactersAfterEllipsis = 100;

    public override int GetFirstInterestedOffset(int startOffset)
    {
        DocumentLine line = CurrentContext.VisualLine.LastDocumentLine;
        if (line.Length > maxLength) {
            int ellipsisOffset = line.Offset + maxLength - charactersAfterEllipsis - ellipsis.Length;
            if (startOffset <= ellipsisOffset)
                return ellipsisOffset;
        }
        return -1;
    }

    public override VisualLineElement ConstructElement(int offset)
    {
        return new FormattedTextElement(ellipsis, CurrentContext.VisualLine.LastDocumentLine.EndOffset - offset - charactersAfterEllipsis);
    }
}

這段程式碼的核心邏輯是:如果檢測到單行的字串數量超過 2000 個(通過 maxLength 常量控制),那麼就省略中間部分的內容直接渲染為省略號(通過 ellipsis 常量控制)。此操作雖然會改變文字的顯示,但不會對全選複製造成影響。考慮到 JSON 在未縮排的情況下本身就不便於閱讀和編輯,所以使用該策略並不會對客戶造成影響。

JSON美化功能的超長文字自動截斷功能

應用該策略後,即便是超長的 JSON 也沒有造成效能問題(單行渲染的字元數被限制後,程式碼著色也失效了)。美化縮排後的 JSON 很難觸發這個策略,且 AvalonEdit 在面對多行文字時效率表現優秀。

TruncateLongLines 的使用方法

使用以下程式碼將 TruncateLongLines 新增到編輯器:

editor.TextArea.TextView.ElementGenerators.Add(new TruncateLongLines());

此外,為了獲得更好的效能你需要:

  1. 如果非必要,請禁用 textEditor.Options.EnableHyperlinks 和 EnableEmailHyperlinks 。
  2. 不要啟用 ShowSpaces 和 ShowTabs 。

相關文章