WPF 缺乏 WinForms 的 AutoSize 屬性,RichTextBox 更因支援段落、格式化文字,導致自動調整尺寸極具挑戰。本文提供一個可直接使用的 AutoSizeRichTextBox 實作,解決高度與寬度自動計算的所有細節。
TextPointer.GetCharacterRect() 取得文件首尾邊界,Bottom - Top 即為內容高度。GetCharacterRect 回傳寬度恆為 0,必須萃取純文字重建 FormattedText 計算。FormattedText 忽略段落間距,直接用它算高度會偏小,故高度仍須靠 TextPointer。Run 可能有不同字型、大小、顏色,FormattedText 需逐段 SetFontFamily、SetFontSize 等套用。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 自動調整大小。程式碼已封裝為可直接繼承使用的控件,歡迎實專案引用。