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

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

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

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

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

使用 TruncateLongLines 改善這個情況

經過一番探索,筆者在官方 GitHub 倉庫中找到了一個相關的 Issues ,Performance issue with word wrap//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 。