WPF Label控件文字竖排及控制字间距

下载文件里的代码比较老了 在下也懒得去更新,其实文章已经说的很清楚了,关键代码也贴出来了,

认真看文章的同学应该很容易就能自己实现,下载文件那5个分各位看情谊吧。。。(代码以文章为准)

更新:

之前那个文本横向不支持下划线删除线等

1.添加依赖属性


        public TextDecorationCollection TextDecorations
        {
            get { return (TextDecorationCollection)GetValue(TextDecorationsProperty); }
            set { SetValue(TextDecorationsProperty, value); }
        }
        public static readonly DependencyProperty TextDecorationsProperty =
            DependencyProperty.Register("TextDecorations", typeof(TextDecorationCollection), typeof(ReportButton), new PropertyMetadata());

 

2.修改文字绘制方式

            #region DrawText
            //textOrientation
            if (VM.Direction == ReportContentDirection.Horizontal)
            {
                var fmttxt = MeasureFont(Text.ToString());
                fmttxt.SetTextDecorations(TextDecorations);
                drawingContext.DrawText(fmttxt, new Point(0, BorderThickness.Top));
            }

————————————————————————————————————————————————————————

LabelEx实现说明

功能说明:

LabelEx是对WPF原生的Label控件的功能扩展

扩展的功能有

  1. 文字间距可控
  2. 文字排布方式可控

实现原理简述:

在摒弃了原有的文本属性基础上,通过自定义的依赖属性和Onrender的drawingContext的一系列绘制方法重绘Label的文字。

功能需求分析:

先看最终需要达到的效果

WPF Label控件文字竖排及控制字间距_第1张图片

WPF Label控件文字竖排及控制字间距_第2张图片

上对齐、左对齐

WPF Label控件文字竖排及控制字间距_第3张图片

文字超出边界后

效果主要体现在竖排文字的变换上,从上图分析:

  1. 第一种竖排方式:中文(即非英文、数字、标点、空格)的文本竖排方式下是“站立”的,其余两种可看成实在横排排布的基础上对整个文字区域进行旋转操作
  2. 第一种竖排下:中文(即非英文、数字、标点、空格)的文本的斜体实现效果非普通的文本斜体,需要特殊处理
  3. 竖排下文字的左右对齐和上下对齐是非标准的
  4. 斜体情况下,文字和边界的控制

 

 

自定义依赖属性说明

WPF Label控件文字竖排及控制字间距_第4张图片

分别用于控制文字排布、文本内容、文字字符间距及背景色

Label本身的Content和Backgreound属性必须设置为空文本和透明,是因为我们绘制的操作实在本身的控件下显示的。

自定义方法(other method)的说明

WPF Label控件文字竖排及控制字间距_第5张图片

自定义了3个方法,分别用来:

  1. 区分中文(即非英文、数字、标点、空格)的文本
  2. 根据传进来的字符生成一个FormattedText对象,用于文本绘制
  1. 计算整个文字的区域大小(这个区域大小是最后实现的实际文本区域大小)

 

WPF Label控件文字竖排及控制字间距_第6张图片

ASCII码常用对照表

在ASCII上不存在的,基本就是非数字英文标点及空格

第二个方法就不说了

直接进行第三个方法的说明:

 

WPF Label控件文字竖排及控制字间距_第7张图片

红色区域一的作用:

调用自定义方法二,为每个字符生成一个FormattedText对象,累计计算它们变换操作后的区域大小。

红色区域二的作用:

WPF Label控件文字竖排及控制字间距_第8张图片

 

 

文字绘制步骤及原理说明

要看懂代码得先看链接一下的Method系列了解各个方法函数的作用

要看懂代码得先看链接一下的Method系列了解各个方法函数的作用

要看懂代码得先看链接一下的Method系列了解各个方法函数的作用

WPF Label控件文字竖排及控制字间距_第9张图片

原理:在原有Label的基础上,添加依赖属性,通过依赖属性来触发Onrender重绘文本内容

Content 和 Background这两个属性必须设置为string.Empty 和 透明

难点:旋转等变换操作后文本的位置控制(需要考虑边距 单个字符的宽高差 整体变换的原点...)

WPF Label控件文字竖排及控制字间距_第10张图片

关键代码

        #region override method

        protected override void OnRender(DrawingContext drawingContext)
        {
            base.OnRender(drawingContext);

            //background
            drawingContext.DrawRectangle(BackColor, null, new Rect(new Point(0, 0), new Size(ActualWidth, ActualHeight)));

            //获取整个文本区域的大小
            Size size = getTextRange();

            double offsetx = BorderThickness.Left, offsety = BorderThickness.Top;



            //控制文本超出控件范围则剪去
            drawingContext.PushClip(new RectangleGeometry(new Rect(new Point(BorderThickness.Left, BorderThickness.Top), new Point(Width - BorderThickness.Right, Height - BorderThickness.Bottom))));

            //根据文字排布位置计算需要移动的位置
            if (HorizontalContentAlignment == HorizontalAlignment.Center)
                offsetx = (Width - size.Width - BorderThickness.Left - BorderThickness.Right) / 2;
            if (HorizontalContentAlignment == HorizontalAlignment.Right)
                offsetx = Width - size.Width - BorderThickness.Right;
            if (VerticalContentAlignment == VerticalAlignment.Center)
                offsety = (Height - size.Height - BorderThickness.Top - BorderThickness.Bottom) / 2;
            if (VerticalContentAlignment == VerticalAlignment.Bottom)
                offsety = Height - size.Height - BorderThickness.Bottom;
            drawingContext.PushTransform(new TranslateTransform(offsetx, offsety));


                       #region DrawText
            //textOrientation
            if (TextOrientation == Direction.Horizontal)
            {
                double w = BorderThickness.Left;
                foreach (char str in Text)
                {
                    var fmttxt = MeasureFont(str.ToString());
                    drawingContext.DrawText(fmttxt, new Point(w, BorderThickness.Top));
                    w += fmttxt.WidthIncludingTrailingWhitespace + FontSpace;
                }
            }
            else if (TextOrientation == Direction.Vertical)
            {
                drawingContext.PushTransform(new TranslateTransform(-Math.Abs(size.Width - size.Height) / 2, Math.Abs(size.Width - size.Height) / 2));

                double w = BorderThickness.Top;
                drawingContext.PushTransform(new RotateTransform(90, size.Height / 2, size.Width / 2 + BorderThickness.Top / 2));
                foreach (char str in Text)
                {
                    var fmttxt = MeasureFont(str.ToString());
                    if (IsASCIIChar(str))
                    {
                        drawingContext.DrawText(fmttxt, new Point(w, BorderThickness.Top));
                        w += fmttxt.WidthIncludingTrailingWhitespace + FontSpace;
                    }
                    else
                    {
                        drawingContext.PushTransform(new TranslateTransform(0, 0));
                        drawingContext.PushTransform(new RotateTransform(-90, w + fmttxt.WidthIncludingTrailingWhitespace / 2, fmttxt.Height / 2));
                        if (FontStyle == FontStyles.Italic || FontStyle == FontStyles.Oblique)
                        {
                            fmttxt.SetFontStyle(FontStyles.Normal);
                            drawingContext.PushTransform(new TranslateTransform(0, 5));
                            drawingContext.PushTransform(new SkewTransform(0, 15, w + fmttxt.Height / 2, fmttxt.WidthIncludingTrailingWhitespace / 2));
                            drawingContext.DrawText(fmttxt, new Point(w, BorderThickness.Top));
                            drawingContext.Pop();
                            drawingContext.Pop();
                        }
                        else
                        {
                            drawingContext.DrawText(fmttxt, new Point(w, BorderThickness.Top));
                        }
                        drawingContext.Pop();
                        drawingContext.Pop();
                        w += fmttxt.WidthIncludingTrailingWhitespace / 2 + fmttxt.Height / 2 + FontSpace;
                    }
                }
                drawingContext.Pop();
                drawingContext.Pop();
            }
            else if (TextOrientation == Direction.ClockwiseVertical)
            {
                drawingContext.PushTransform(new TranslateTransform(-Math.Abs(size.Width - size.Height) / 2, Math.Abs(size.Width - size.Height) / 2));
                double w = BorderThickness.Top;
                drawingContext.PushTransform(new RotateTransform(90, size.Height / 2, size.Width / 2 ));
                foreach (char str in Text)
                {
                    var fmttxt = MeasureFont(str.ToString());
                    drawingContext.DrawText(fmttxt, new Point(w, BorderThickness.Top));
                    w += fmttxt.WidthIncludingTrailingWhitespace + FontSpace;
                }
                drawingContext.Pop();
                drawingContext.Pop();
            }
            else
            {
                drawingContext.PushTransform(new TranslateTransform(-Math.Abs(size.Width - size.Height) / 2, Math.Abs(size.Width - size.Height) / 2));
                double w = BorderThickness.Top;
                drawingContext.PushTransform(new RotateTransform(270, size.Height / 2, size.Width / 2 ));
                foreach (char str in Text)
                {
                    var fmttxt = MeasureFont(str.ToString());
                    drawingContext.DrawText(fmttxt, new Point(w, BorderThickness.Top));
                    w += fmttxt.WidthIncludingTrailingWhitespace + FontSpace;
                }
                drawingContext.Pop();
                drawingContext.Pop();
            }


            drawingContext.Pop();
            drawingContext.Pop();

            //若Height和Width没设置 则为它加上值
            if (double.IsNaN(Height))
                Height = size.Height + BorderThickness.Top + BorderThickness.Bottom;
            if (double.IsNaN(Width))
                Width = size.Width + BorderThickness.Left + BorderThickness.Right;
        }

        #endregion
        #region other method 

        private bool IsASCIIChar(char c)
        {
            // 判断字符串中第一位字符是否是ASCII字符( 0–127),ASCII字符占一个字节	
            return c / 0x80 == 0 ? true : false;
        }

        private FormattedText MeasureFont(string str)
        {
            var ftxt = new FormattedText(
                        str,
                        CultureInfo.CurrentUICulture,
                        FlowDirection.LeftToRight,
                        new Typeface(FontFamily, FontStyle, FontWeight, FontStretch),
                        FontSize,
                        Foreground
                        );
            ftxt.TextAlignment = TextAlignment.Justify;
            return ftxt;
        }

        /// 
        /// get all Text area size
        /// 
        /// 
        private Size getTextRange()
        {
            Size size = new Size();
            if (TextOrientation == Direction.Horizontal)
                foreach (var v in Text)
                {
                    var ft = MeasureFont(v.ToString());
                    size.Width += ft.WidthIncludingTrailingWhitespace + FontSpace;
                    size.Height = size.Height > ft.Height ? size.Height : ft.Height;
                }
            else if (TextOrientation == Direction.Vertical)
            {
                foreach (var v in Text)
                {
                    var ft = MeasureFont(v.ToString());
                    if (IsASCIIChar(v))
                        size.Height += ft.Width + FontSpace;
                    else
                        size.Height += ft.Height / 2 + ft.WidthIncludingTrailingWhitespace / 2 + FontSpace + 2.5;
                    size.Width = size.Width > ft.Height ? size.Width : ft.Height;
                }
            }
            else
            {
                foreach (var v in Text)
                {
                    var ft = MeasureFont(v.ToString());
                    size.Height += ft.WidthIncludingTrailingWhitespace + FontSpace;
                    size.Width = size.Width > ft.Height ? size.Width : ft.Height;
                }
            }

            //tan15/180=2-Math.Sqrt(3.0)
            if (FontStyle == FontStyles.Italic || FontStyle == FontStyles.Oblique)
            {
                if (TextOrientation == Direction.Horizontal)
                {
                    size.Width += size.Height * (2 - Math.Sqrt(3.0));
                }
                else
                {
                    size.Height += size.Width * (2 - Math.Sqrt(3.0));
                }
            }
            return size;
        }
        #endregion

 

不想思考或者想支持一下的同学:下载链接 链接里的代码是旧版的懒得改了 方法以这里的为准

参照这个 你还可以自己玩一下其他的button textbox什么的 还有文字描边的原理其实比这个更简单 就懒得写了。。。

你可能感兴趣的:(customControl)