代码改变世界

WPF 文本呈现(1)

2010-12-03 11:52  Clingingboy  阅读(5529)  评论(1编辑  收藏  举报

 

WPF呈现文本的控件有TextBlock,TextBox,RichTextBox.再者复杂一些的有如visual studio 2010的wpf编辑器,wpf提供了一套可扩展的文本的api用于文本呈现管理.

下面我们还是以呈现Hello World为目标

文本段落

image

我们都写过作文,一般写作文都会分段落,很少会整篇作文是只有一个段落的.

那么一个段落的文本也具备了一些属性,TextParagraphProperties定义了一系列有关于文本的属性,其属性不允许动态更改即只读的
image

单个字符TextRun

比如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显示样式

image

image

定义TextRunProperties

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
    }

定义TextParagraphProperties

/// <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 的属性

输出Hello

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);
    }
   
}
  1. 创建TextFormatter
  2. 创建TextLine
  3. 绘制一行Text文本

代码太多先停

输出结果

image