WPF呈现文本的控件有TextBlock,TextBox,RichTextBox.再者复杂一些的有如visual studio 2010的wpf编辑器,wpf提供了一套可扩展的文本的api用于文本呈现管理.
下面我们还是以呈现Hello World为目标
我们都写过作文,一般写作文都会分段落,很少会整篇作文是只有一个段落的.
那么一个段落的文本也具备了一些属性,TextParagraphProperties定义了一系列有关于文本的属性,其属性不允许动态更改即只读的
比如Hello,其中H,e,l,l,o都是单个字符,其也用于自己的属性,比如文本大小,颜色等.比较以下xaml
<TextBlock Text="Hello World"></TextBlock> <TextBlock Margin="5"> <Run Foreground="Gray">H</Run> <Run Foreground="Blue">e</Run> <Run Foreground="Red">l</Run> <Run Foreground="Yellow">l</Run> <Run Foreground="Violet">o</Run> </TextBlock>
第二段xaml显示样式
class GenericTextRunProperties : TextRunProperties { #region Constructors public GenericTextRunProperties( Typeface typeface, double size, double hintingSize, TextDecorationCollection textDecorations, Brush forgroundBrush, Brush backgroundBrush, BaselineAlignment baselineAlignment, CultureInfo culture) { if (typeface == null) throw new ArgumentNullException("typeface"); ValidateCulture(culture); _typeface = typeface; _emSize = size; _emHintingSize = hintingSize; _textDecorations = textDecorations; _foregroundBrush = forgroundBrush; _backgroundBrush = backgroundBrush; _baselineAlignment = baselineAlignment; _culture = culture; } public GenericTextRunProperties(FontRendering newRender) { _typeface = newRender.Typeface; _emSize = newRender.FontSize; _emHintingSize = newRender.FontSize; _textDecorations = newRender.TextDecorations; _foregroundBrush = newRender.TextColor; _backgroundBrush = null; _baselineAlignment = BaselineAlignment.Baseline; _culture = CultureInfo.CurrentUICulture; } #endregion #region Private Methods private static void ValidateCulture(CultureInfo culture) { if (culture == null) throw new ArgumentNullException("culture"); if (culture.IsNeutralCulture || culture.Equals(CultureInfo.InvariantCulture)) throw new ArgumentException("Specific Culture Required", "culture"); } private static void ValidateFontSize(double emSize) { if (emSize <= 0) throw new ArgumentOutOfRangeException("emSize", "Parameter Must Be Greater Than Zero."); //if (emSize > MaxFontEmSize) // throw new ArgumentOutOfRangeException("emSize", "Parameter Is Too Large."); if (double.IsNaN(emSize)) throw new ArgumentOutOfRangeException("emSize", "Parameter Cannot Be NaN."); } #endregion #region Properties public override Typeface Typeface { get { return _typeface; } } public override double FontRenderingEmSize { get { return _emSize; } } public override double FontHintingEmSize { get { return _emHintingSize; } } public override TextDecorationCollection TextDecorations { get { return _textDecorations; } } public override Brush ForegroundBrush { get { return _foregroundBrush; } } public override Brush BackgroundBrush { get { return _backgroundBrush; } } public override BaselineAlignment BaselineAlignment { get { return _baselineAlignment; } } public override CultureInfo CultureInfo { get { return _culture; } } public override TextRunTypographyProperties TypographyProperties { get { return null; } } public override TextEffectCollection TextEffects { get { return null; } } public override NumberSubstitution NumberSubstitution { get { return null; } } #endregion #region Private Fields private Typeface _typeface; private double _emSize; private double _emHintingSize; private TextDecorationCollection _textDecorations; private Brush _foregroundBrush; private Brush _backgroundBrush; private BaselineAlignment _baselineAlignment; private CultureInfo _culture; #endregion }
/// <summary> /// Class to implement TextParagraphProperties, used by TextSource /// </summary> class GenericTextParagraphProperties : TextParagraphProperties { #region Constructors public GenericTextParagraphProperties( FlowDirection flowDirection, TextAlignment textAlignment, bool firstLineInParagraph, bool alwaysCollapsible, TextRunProperties defaultTextRunProperties, TextWrapping textWrap, double lineHeight, double indent) { _flowDirection = flowDirection; _textAlignment = textAlignment; _firstLineInParagraph = firstLineInParagraph; _alwaysCollapsible = alwaysCollapsible; _defaultTextRunProperties = defaultTextRunProperties; _textWrap = textWrap; _lineHeight = lineHeight; _indent = indent; } public GenericTextParagraphProperties(FontRendering newRendering) { _flowDirection = FlowDirection.LeftToRight; _textAlignment = newRendering.TextAlignment; _firstLineInParagraph = false; _alwaysCollapsible = false; _defaultTextRunProperties = new GenericTextRunProperties( newRendering.Typeface, newRendering.FontSize, newRendering.FontSize, newRendering.TextDecorations, newRendering.TextColor, null, BaselineAlignment.Baseline, CultureInfo.CurrentUICulture); _textWrap = TextWrapping.Wrap; _lineHeight = 0; _indent = 0; _paragraphIndent = 0; } #endregion #region Properties public override FlowDirection FlowDirection { get { return _flowDirection; } } public override TextAlignment TextAlignment { get { return _textAlignment; } } public override bool FirstLineInParagraph { get { return _firstLineInParagraph; } } public override bool AlwaysCollapsible { get { return _alwaysCollapsible; } } public override TextRunProperties DefaultTextRunProperties { get { return _defaultTextRunProperties; } } public override TextWrapping TextWrapping { get { return _textWrap; } } public override double LineHeight { get { return _lineHeight; } } public override double Indent { get { return _indent; } } public override TextMarkerProperties TextMarkerProperties { get { return null; } } public override double ParagraphIndent { get { return _paragraphIndent; } } #endregion #region Private Fields private FlowDirection _flowDirection; private TextAlignment _textAlignment; private bool _firstLineInParagraph; private bool _alwaysCollapsible; private TextRunProperties _defaultTextRunProperties; private TextWrapping _textWrap; private double _indent; private double _paragraphIndent; private double _lineHeight; #endregion }
比如输出Hello,可以5个字符一起输出,也可以一个字符输出由TextSource的GetTextRun方法负责输出字符
class CustomTextSource : TextSource { // Used by the TextFormatter object to retrieve a run of text from the text source. public override TextRun GetTextRun(int textSourceCharacterIndex) { if (textSourceCharacterIndex >= _text.Length) { return new TextEndOfParagraph(1); } // Create TextCharacters using the current font rendering properties. if (textSourceCharacterIndex < _text.Length) { return new TextCharacters( _text, textSourceCharacterIndex, _text.Length - textSourceCharacterIndex, new GenericTextRunProperties(_currentRendering)); } // Return an end-of-paragraph if no more text source. return new TextEndOfParagraph(1); } public override TextSpan<CultureSpecificCharacterBufferRange> GetPrecedingText(int textSourceCharacterIndexLimit) { CharacterBufferRange cbr = new CharacterBufferRange(_text, 0, textSourceCharacterIndexLimit); return new TextSpan<CultureSpecificCharacterBufferRange>( textSourceCharacterIndexLimit, new CultureSpecificCharacterBufferRange(CultureInfo.CurrentUICulture, cbr) ); } public override int GetTextEffectCharacterIndexFromTextSourceCharacterIndex(int textSourceCharacterIndex) { throw new Exception("The method or operation is not implemented."); } #region Properties public string Text { get { return _text; } set { _text = value; } } public FontRendering FontRendering { get { return _currentRendering; } set { _currentRendering = value; } } #endregion #region Private Fields private string _text; //text store private FontRendering _currentRendering; #endregion }
特别要关注GetTextRun方法,这里TextRun用到了定义的TextParagraphProperties 的属性
protected override void OnRender(DrawingContext dc) { TextFormatter formatter= TextFormatter.Create(); var textStore = new CustomTextSource(); var currentRendering = new FontRendering( 12, TextAlignment.Left, null, Brushes.Black, new Typeface("Arial")); textStore.FontRendering = currentRendering; textStore.Text = "Hello"; using (TextLine myTextLine = formatter.FormatLine( textStore, 0, 100, new GenericTextParagraphProperties(currentRendering), null)) { // Draw the formatted text into the drawing context. myTextLine.Draw(dc, new Point(0, 0), InvertAxes.None); } }
代码太多先停
输出结果