首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> 關於.NET

WPF RichTextBox 依內容自動調整大小完整解法

     編輯:關於.NET

WPF 缺乏 WinForms 的 AutoSize 屬性,RichTextBox 更因支援段落、格式化文字,導致自動調整尺寸極具挑戰。本文提供一個可直接使用的 AutoSizeRichTextBox 實作,解決高度與寬度自動計算的所有細節。

核心難點

  • 高度:可透過 TextPointer.GetCharacterRect() 取得文件首尾邊界,Bottom - Top 即為內容高度。
  • 寬度:GetCharacterRect 回傳寬度恆為 0,必須萃取純文字重建 FormattedText 計算。
  • 段落間距:FormattedText 忽略段落間距,直接用它算高度會偏小,故高度仍須靠 TextPointer。
  • 行內格式:Run 可能有不同字型、大小、顏色,FormattedText 需逐段 SetFontFamily、SetFontSize 等套用。

完整實作:AutoSizeRichTextBox

public class AutoSizeRichTextBox : RichTextBox
{
    public AutoSizeRichTextBox()
    {
        Loaded += (s, e) => AdjustSizeByContent();
    }

    protected override void OnTextChanged(TextChangedEventArgs e)
    {
        base.OnTextChanged(e);
        AdjustSizeByContent();
    }

    public void AdjustSizeByContent()
    {
        // 寬度:FormattedText 計算
        var formatted = GetFormattedText(Document);
        const double remainW = 20; // 預留邊距
        Width = Math.Min(MaxWidth, Math.Max(MinWidth, 
            formatted.WidthIncludingTrailingWhitespace + remainW));

        // 高度:TextPointer 邊界計算
        var startRect = Document.ContentStart.GetCharacterRect(LogicalDirection.Forward);
        var endRect = Document.ContentEnd.GetCharacterRect(LogicalDirection.Forward);
        var height = endRect.Bottom - startRect.Top;
        var remainH = endRect.Height / 2.0; // 預留半行高
        Height = Math.Min(MaxHeight, Math.Max(MinHeight, height + remainH));
    }

    // 將 FlowDocument 文字與行內格式轉為 FormattedText
    private static FormattedText GetFormattedText(FlowDocument doc)
    {
        var text = GetText(doc);
        var ft = new FormattedText(text, CultureInfo.CurrentCulture, doc.FlowDirection,
            new Typeface(doc.FontFamily, doc.FontStyle, doc.FontWeight, doc.FontStretch),
            doc.FontSize, doc.Foreground);

        int offset = 0;
        foreach (var elem in GetRunsAndParagraphs(doc))
        {
            if (elem is Run run)
            {
                int count = run.Text.Length;
                ft.SetFontFamily(run.FontFamily, offset, count);
                ft.SetFontSize(run.FontSize, offset, count);
                ft.SetFontStretch(run.FontStretch, offset, count);
                ft.SetFontStyle(run.FontStyle, offset, count);
                ft.SetFontWeight(run.FontWeight, offset, count);
                ft.SetForegroundBrush(run.Foreground, offset, count);
                ft.SetTextDecorations(run.TextDecorations, offset, count);
                offset += count;
            }
            else // Paragraph 或 LineBreak
            {
                offset += Environment.NewLine.Length;
            }
        }
        return ft;
    }

    // 遍歷文件取得 Run 與 Paragraph/LineBreak 序列
    private static IEnumerable<TextElement> GetRunsAndParagraphs(FlowDocument doc)
    {
        for (var pos = doc.ContentStart; 
             pos != null && pos.CompareTo(doc.ContentEnd) <= 0; 
             pos = pos.GetNextContextPosition(LogicalDirection.Forward))
        {
            if (pos.GetPointerContext(LogicalDirection.Forward) == TextPointerContext.ElementEnd)
            {
                if (pos.Parent is Run run) yield return run;
                else if (pos.Parent is Paragraph para) yield return para;
                else if (pos.Parent is LineBreak lb) yield return lb;
            }
        }
    }

    // 萃取純文字(段落換行)
    private static string GetText(FlowDocument doc)
    {
        var sb = new StringBuilder();
        foreach (var elem in GetRunsAndParagraphs(doc))
            sb.Append(elem is Run r ? r.Text : Environment.NewLine);
        return sb.ToString();
    }
}

使用方式

<local:AutoSizeRichTextBox 
    MinWidth="100" MaxWidth="400"
    MinHeight="30" MaxHeight="200"
    VerticalScrollBarVisibility="Hidden"
    HorizontalScrollBarVisibility="Hidden" />

設定 Min/Max Width/Height 限制尺寸範圍,隱藏捲軸避免干擾。

已知限制

  • 僅支援純文字內容,插入圖片(InlineUIContainer)、表格、浮動元素不生效。
  • 極大文件效能未優化,適合輸入框、標籤編輯等短文本場景。

總結

結合 TextPointer 精準計算高度、FormattedText 重建行內格式計算寬度,並處理段落換行與預留邊距,實現真正可用的 RichTextBox 自動調整大小。程式碼已封裝為可直接繼承使用的控件,歡迎實專案引用。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved