程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> WPF中根據文本內容自動設置大小的RichTextBox

WPF中根據文本內容自動設置大小的RichTextBox

編輯:關於.NET

很懷念windows forms當中的AutoSize屬性啊,但可惜的是WPF並沒有實現這個屬性,這多少讓人有些郁悶。

那就自個寫吧,相對比較容易的是TextBox之類的僅僅顯示平文本的控件,你 可以根據你的文本,字體等等屬性構造一個FormattedText

實例,這個實例有Width/Height屬性(我還是很懷念Font.MeasureString方 法),最讓人糾結的是RichTextBox控件,哎,又是它。

思路很簡單,監視文本變化,文本變化時調整控件大小:

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


         public void AdjustSizeByConent()
         {
             //myHeight = ... 取得正確的高度
             Height = myHeight;    
             //myWidth = ... 取得正確的寬度
             Width = myWidth;
         }

如何獲取正確的高度呢,有一個非常撿便宜的方法,分別對 Document.ContentStart和Document.ContentEnd調用 TextPointer.GetCharacterRect()方法,我們可以獲得文檔開始處和結束處的內 容邊框,如下圖所示:

注意到兩個紅色邊框了嗎,用第二個邊框的bottom減去第一個邊框的top,就 可以得到內容的高度,所以:

Rect rectStart = Document.ContentStart.GetCharacterRect (LogicalDirection.Forward);
             Rect rectEnd =  Document.ContentEnd.GetCharacterRect(LogicalDirection.Forward);
             var height = rectEnd.Bottom -  rectStart.Top;
             var remainH = rectEnd.Height/2.0;
             Height = Math.Min(MaxHeight, Math.Max (MinHeight, height + remainH));

(代碼中的remainH 是預留的一點點空白)

那麼求寬度時,是不是“同理可證”了(呵呵,如果是在上高中,我可真要 這麼寫了,但程序是嚴謹的,忽悠不過去的~)

不行!

因為,上面代碼中的rectStart和rectEnd寬度始終返回的是0(而高度卻返回 的是正確的值),不知道為啥。

這導致獲取寬度是非常麻煩,下面是一種解決方案,將控件中的文本抽取出 來,構造成一個比較復雜的FormattedText,然後由它來求寬度:

var formattedText = GetFormattedText(Document);
// ReSharper disable ConvertToConstant.Local
             var remainW = 20;
// ReSharper restore ConvertToConstant.Local
             Width = Math.Min(MaxWidth, Math.Max (MinWidth, formattedText.WidthIncludingTrailingWhitespace +  remainW));

OK,有人會問了,既然可以通過FormattedText獲取寬度,那為啥不能通過它 同理可證求高度呢?

不可以的,不信你在RichTextBox中敲幾次回車試試,一個回車導致一個段落 , richTextBox段落之間是有距離的,默認很大(大得有點不協調), FormattedText是不會計算段落間隔的,所以 FormattedText的高度比實際高度 要小,夠糾結吧。

好了,完整的代碼在這裡(注意哦,我這裡只處理的文本,那我向其中插入 圖片呢...恩,不work)

AutoSizeRichTextBox

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Media;
namespace AutoSizeTextBoxDemo
{
     internal class AutoSizeRichTextBox : RichTextBox
     {
         public AutoSizeRichTextBox()
         {
             Loaded += ((sender, args) =>  AdjustSizeByConent());
         }
         protected override void OnTextChanged (TextChangedEventArgs e)
         {
             base.OnTextChanged(e);
             AdjustSizeByConent();
         }

         public void AdjustSizeByConent()
         {
             var formattedText = GetFormattedText (Document);
// ReSharper disable ConvertToConstant.Local
             var remainW = 20;
// ReSharper restore ConvertToConstant.Local
             Width = Math.Min(MaxWidth, Math.Max (MinWidth, formattedText.WidthIncludingTrailingWhitespace +  remainW));
             Rect rectStart =  Document.ContentStart.GetCharacterRect(LogicalDirection.Forward);
             Rect rectEnd =  Document.ContentEnd.GetCharacterRect(LogicalDirection.Forward);
             var height = rectEnd.Bottom -  rectStart.Top;
             var remainH = rectEnd.Height/2.0;
             Height = Math.Min(MaxHeight, Math.Max (MinHeight, height + remainH));
         }
         private static FormattedText GetFormattedText (FlowDocument doc)
         {
             var output = new FormattedText(
                 GetText(doc),
                 CultureInfo.CurrentCulture,
                 doc.FlowDirection,
                 new Typeface(doc.FontFamily,  doc.FontStyle, doc.FontWeight, doc.FontStretch),
                 doc.FontSize,
                 doc.Foreground);
             int offset = 0;
             foreach (TextElement textElement in  GetRunsAndParagraphs(doc))
             {
                 var run = textElement as  Run;
                 if (run != null)
                 {
                     int count =  run.Text.Length;
                     output.SetFontFamily (run.FontFamily, offset, count);
                     output.SetFontSize (run.FontSize, offset, count);
                     output.SetFontStretch (run.FontStretch, offset, count);
                     output.SetFontStyle (run.FontStyle, offset, count);
                     output.SetFontWeight (run.FontWeight, offset, count);
                     output.SetForegroundBrush (run.Foreground, offset, count);
                     output.SetTextDecorations (run.TextDecorations, offset, count);
                     offset += count;
                 }
                 else
                 {
                     offset +=  Environment.NewLine.Length;
                 }
             }

             return output;
         }
         private static IEnumerable<TextElement>  GetRunsAndParagraphs(FlowDocument doc)
         {
             for (TextPointer position =  doc.ContentStart;
                 position != null &&  position.CompareTo(doc.ContentEnd) <= 0;
                 position =  position.GetNextContextPosition(LogicalDirection.Forward))
             {
                 if (position.GetPointerContext (LogicalDirection.Forward) == TextPointerContext.ElementEnd)
                 {
                     var run =  position.Parent as Run;
                     if (run != null)
                     {
                         yield return  run;
                     }
                     else
                     {
                         var para =  position.Parent as Paragraph;
                         if (para !=  null)
                         {
                             yield  return para;
                         }
                         else
                         {
                             var  lineBreak = position.Parent as LineBreak;
                             if (lineBreak != null)
                             {
                                 yield return lineBreak;
                             }
                         }
                     }
                 }
             }
         }
         private static string GetText(FlowDocument  doc)
         {
             var sb = new StringBuilder();
             foreach (TextElement text in  GetRunsAndParagraphs(doc))
             {
                 var run = text as Run;
                 sb.Append(run == null ?  Environment.NewLine : run.Text);
             }
             return sb.ToString();
         }

     }
}

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved