切换启用时,TextBox 的文本略有移动:字体和字体大小发生变化

TextBox's Text is moving slightly when toggling Enabled: Font and Font size changes

在我的应用程序中,我有自定义文本框控件。当我更改 Enabled 属性.

时,它的文本似乎在移动
  1. 我不明白这是什么原因。

  2. 我将更改 TextAlignAutoSizeEnabledFont 属性

我试过使用TextRenderer.DrawText():它似乎解决了一些问题,但是当我更改为特定字体和字体大小时,文本再次移动(例如,设置MS コシック,10pt)。

我搜索了网站并阅读了以下内容,但似乎没有正确理解:

Using Graphics.DrawString to Simulate TextBox rendering

How do I use DrawString without trimming?

Graphics.DrawString vs TextRenderer.DrawText?Which can Deliver Better Quality


以下是部分截图:

MS コシック, 10pt 和 TextAlign Center and e.Graphics.DrawString

↓↓ Enabled ↓↓

↓↓ Disabled ↓↓

comparison

MS コシック, 10pt 和 TextAlign Center and TextRenderer.DrawText

↓↓ Enabled ↓↓

↓↓ Disabled ↓↓

comparison (text seems to be slightly elongated when disabled)

我的代码的原始 post https://devlights.hatenablog.com/entry/20070523/p1

这是我的自定义控件代码:

using System.Drawing;
using System.Windows.Forms;

public partial class CustomEnabledStateTextBox : TextBox
{
    public CustomEnabledStateTextBox()
    {
        InitializeComponent();
        AutoSize = false;
    }

    public override bool AutoSize
    {
        get { return base.AutoSize; }
        set { base.AutoSize = value; }
    }

    protected override void OnEnabledChanged(EventArgs e)
    {
        this.SetStyle(ControlStyles.UserPaint, !this.Enabled);
        this.Invalidate();
        base.OnEnabledChanged(e);
    }

    protected override void OnPaint(PaintEventArgs e)
    {
        base.OnPaint(e);

        using (StringFormat sf = new StringFormat())
        {
            if (this.TextAlign == HorizontalAlignment.Center)
            {
                sf.Alignment = StringAlignment.Center;
            }
            else if (this.TextAlign == HorizontalAlignment.Left)
            {
                sf.Alignment = StringAlignment.Near;
            }
            else
            {
                sf.Alignment = StringAlignment.Far;
            }

            using (SolidBrush brush = new SolidBrush(ForeColor))
            {
                //MS UI Gothic, 8pt --> not OK
                //MS UI Gothic, 9pt --> OK
                //MS UI Gothic, 10pt --> OK
                //MS UI Gothic, 11pt --> OK
                //MS UI Gothic, 12pt --> not OK
                e.Graphics.DrawString(
                    base.Text, base.Font, brush, new Rectangle(-1, 1, base.Width, base.Height), sf);

                //MS ゴシック, 8pt --> not OK
                //MS ゴシック, 9pt --> not OK
                //MS ゴシック, 10pt --> not OK
                //MS ゴシック, 12pt --> not OK
                //e.Graphics.DrawString(
                //    base.Text, base.Font, brush, new Rectangle(-1, 1, base.Width, base.Height), sf);

                //MS ゴシック, 10pt --> not OK
                //TextRenderer.DrawText(e.Graphics, Text, Font, new Rectangle(0, 0, Width, Height), ForeColor, TextFormatFlags.HorizontalCenter | TextFormatFlags.NoPadding | TextFormatFlags.Internal);
            }

            if (this.BorderStyle == BorderStyle.FixedSingle)
            {
                e.Graphics.DrawRectangle(new Pen(Color.Black), 0, 0, this.Width - 1, this.Height - 1);
            }
        }
    }
}

测试表:

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();

        customEnabledStateTextBox1.Text = "2021/04/04";
        customEnabledStateTextBox1.Size = new Size(88, 20);
        customEnabledStateTextBox1.BorderStyle = BorderStyle.None;
        customEnabledStateTextBox1.TextAlign = HorizontalAlignment.Center;
    }

    private void button1_Click(object sender, EventArgs e)
    {
        customEnabledStateTextBox1.Enabled = true;
    }

    private void button2_Click(object sender, EventArgs e)
    {
        customEnabledStateTextBox1.Enabled = false;
    }
}

更新 1

我已经根据 Jimi 提供的答案更改了我的代码 并对其进行了测试,它解决了一些问题,但是当我使用
时问题仍然存在 MS コシック, 10pt 并且发现在两个 MS UI Gothic, 10pt 和 MS コシック, 10pt 的情况下,文本在轻微移动 downward.My 应用程序使用 MS コシック 7pt~12pt.So 我必须展示10pt 正确。 我还没有测试过其他字体。 请帮助。

抱歉之前没有提供.Net framework 我正在使用

 .NET Framework 3.5
 BorderStyle None

测试用例

MS UI Gothic, 8pt-- > OK
MS UI Gothic, 9pt-- > OK
MS UI Gothic, 10pt-- > not OK
MS UI Gothic, 11pt-- > OK
MS UI Gothic, 12pt-- > OK

MS ゴシック, 8pt-- > OK
MS ゴシック, 9pt-- > OK
MS ゴシック, 10pt-- > not OK
MS ゴシック, 12pt-- > OK

以下是

的屏幕截图

MS コシック, 10pt 和 TextAlign Center and BorderStyle None andTextRenderer.DrawText

MS UI 哥特式、10pt 和 TextAlign Center 和 BorderStyle None 和 TextRenderer.DrawText

更新了以下代码: 我已经在 .Net Framework 3.5 和 4.8

中对其进行了测试

自定义启用状态文本框:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
//using System.Threading.Tasks;
using System.Windows.Forms;

namespace WinFormTest1
{
    public partial class CustomEnabledStateTextBox : TextBox
    {
        public override bool AutoSize
        {
            get { return base.AutoSize; }
            set { base.AutoSize = value; }
        }

        public CustomEnabledStateTextBox()
        {
            InitializeComponent();

            AutoSize = false;
        }

        
        protected override void OnEnabledChanged(EventArgs e)
        {
            //>>>Change 1
            SetStyle(ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint, !Enabled);
            UpdateStyles();
            //>>>Change 1
            base.OnEnabledChanged(e);
        }

        //>>>Change 1
        protected override void OnBorderStyleChanged(EventArgs e)
        {
            base.OnBorderStyleChanged(e);
            if (BorderStyle == BorderStyle.FixedSingle)
            {
                BorderStyle = BorderStyle.Fixed3D;
            }
        }
        //>>>Change 1



        protected override void OnPaint(PaintEventArgs e)
        {

            using (StringFormat sf = new StringFormat())
            {

                if (this.TextAlign == HorizontalAlignment.Center)
                {
                    sf.Alignment = StringAlignment.Center;
                }
                else if (this.TextAlign == HorizontalAlignment.Left)
                {
                    sf.Alignment = StringAlignment.Near;
                }
                else
                {
                    sf.Alignment = StringAlignment.Far;
                }

                //>>>Change 1
                var rect = ClientRectangle;
                if (BorderStyle != BorderStyle.None) rect.Inflate(-1, -1);

                var alignment = TextAlign == HorizontalAlignment.Left
                              ? TextFormatFlags.Left : (TextFormatFlags)((int)TextAlign ^ 3);

                var flags = TextFormatFlags.TextBoxControl | TextFormatFlags.NoPadding | alignment;
                //>>>Change 1

                using (SolidBrush brush = new SolidBrush(ForeColor))
                {
                    //MS UI Gothic, 8pt --> not OK
                    //MS UI Gothic, 9pt --> OK
                    //MS UI Gothic, 10pt --> OK
                    //MS UI Gothic, 11pt --> OK
                    //MS UI Gothic, 12pt --> not OK
                    //e.Graphics.DrawString(
                    //    base.Text, base.Font, brush, new Rectangle(-1, 1, base.Width, base.Height), sf);

                    //MS ゴシック, 8pt --> not OK
                    //MS ゴシック, 9pt --> not OK
                    //MS ゴシック, 10pt --> not OK
                    //MS ゴシック, 12pt --> not OK
                    //e.Graphics.DrawString(
                    //    base.Text, base.Font, brush, new Rectangle(-1, 1, base.Width, base.Height), sf);

                    //MS ゴシック, 10pt --> not OK
                    //TextRenderer.DrawText(e.Graphics, Text, Font, new Rectangle(0, 0, Width, Height), ForeColor, TextFormatFlags.HorizontalCenter | TextFormatFlags.NoPadding | TextFormatFlags.Internal);

                    //TextRenderer.DrawText(e.Graphics, Text, Font, new Rectangle(0, 0, Width, Height), ForeColor, flags);

                    //>>>Change 1
                    //MS UI Gothic, 8pt --> OK
                    //MS UI Gothic, 9pt --> OK
                    //MS UI Gothic, 10pt --> not OK
                    //MS UI Gothic, 11pt --> OK
                    //MS UI Gothic, 12pt --> OK

                    //MS ゴシック, 8pt --> OK
                    //MS ゴシック, 9pt --> OK
                    //MS ゴシック, 10pt --> not OK
                    //MS ゴシック, 12pt --> OK

                    TextRenderer.DrawText(e.Graphics, Text, Font, rect, ForeColor, flags);
                    //>>>Change 1

                }

                if (this.BorderStyle == BorderStyle.FixedSingle)
                {
                    e.Graphics.DrawRectangle(new Pen(Color.Black), 0, 0, this.Width - 1, this.Height - 1);
                }

                //>>>Change 1
                base.OnPaint(e);
                //>>>Change 1
            }
        }


 
    }
}

CustomEnabledStateTextBoxSO :

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
//using System.Threading.Tasks;
using System.Windows.Forms;

namespace WinFormTest1
{
    public partial class CustomEnabledStateTextBoxSO : TextBox
    {
        public override bool AutoSize
        {
            get { return base.AutoSize; }
            set { base.AutoSize = value; }
        }

        public CustomEnabledStateTextBoxSO()
        {
            InitializeComponent();

            AutoSize = false;
        }

        protected override void OnEnabledChanged(EventArgs e)
        {
            SetStyle(ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint, !Enabled);
            UpdateStyles();
            base.OnEnabledChanged(e);
        }

        protected override void OnBorderStyleChanged(EventArgs e)
        {
            base.OnBorderStyleChanged(e);
            if (BorderStyle == BorderStyle.FixedSingle)
            {
                BorderStyle = BorderStyle.Fixed3D;
            }
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            var rect = ClientRectangle;
            if (BorderStyle != BorderStyle.None) rect.Inflate(-1, -1);

            var alignment = TextAlign == HorizontalAlignment.Left
                          ? TextFormatFlags.Left : (TextFormatFlags)((int)TextAlign ^ 3);

            var flags = TextFormatFlags.TextBoxControl | TextFormatFlags.NoPadding | alignment;

            //MS UI Gothic, 8pt-- > OK
            //MS UI Gothic, 9pt-- > OK
            //MS UI Gothic, 10pt-- > not OK
            //MS UI Gothic, 11pt-- > OK
            //MS UI Gothic, 12pt-- > OK

            //MS ゴシック, 8pt-- > OK
            //MS ゴシック, 9pt-- > OK
            //MS ゴシック, 10pt-- > not OK
            //MS ゴシック, 12pt-- > OK
            TextRenderer.DrawText(e.Graphics, Text, Font, rect, ForeColor, flags);
            base.OnPaint(e);
        }
    }
}


测试表:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
//using System.Threading.Tasks;
using System.Windows.Forms;

namespace WinFormTest1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();

            //Left Side
            customEnabledStateTextBox1.Text = "2021/04/04";
            customEnabledStateTextBox1.Size = new Size(88, 20); //size will change if i set Autosize true
            customEnabledStateTextBox1.BorderStyle = BorderStyle.None;
            customEnabledStateTextBox1.TextAlign = HorizontalAlignment.Center;

            //Right Side
            customEnabledStateTextBoxSO1.Text = "2021/04/04";
            customEnabledStateTextBoxSO1.Size = new Size(88, 20); //size will change if i set Autosize true
            customEnabledStateTextBoxSO1.BorderStyle = BorderStyle.None;
            customEnabledStateTextBoxSO1.TextAlign = HorizontalAlignment.Center;
        }

        private void button1_Click(object sender, EventArgs e)
        {
            customEnabledStateTextBox1.Enabled = true;
            customEnabledStateTextBoxSO1.Enabled = true;
        }

        private void button2_Click(object sender, EventArgs e)
        {
            customEnabledStateTextBox1.Enabled = false;
            customEnabledStateTextBoxSO1.Enabled = false;
        }
    }
}

对文本呈现进行一些更改应该可以让您接近预期。

  • 使用TextRenderer绘制文字
  • TextFormatFlags.TextBoxControlTextFormatFlags.NoPadding 添加到 TextRenderer.DrawText() 中使用的 TextFormatFlags (请参阅有关这两个的文档)
  • 覆盖 OnBorderStyleChanged() 以在自定义绘制控件时用 Fixed3D 替换 FixedSingle 样式。这就是基础 class (TextBoxBase) 所做的,否则您将获得缩小 Win32 控件的客户区域的默认内部边框。
  • BorderStyle = BorderStyle.Fixed3D 时,将控件的 ClientRectangle 扩大 -1 像素。

我在 SetStyle() 调用之后添加了 UpdateStyles():这会强制使用新样式并导致控件重新绘制自身。


请注意,在设置控件的文本时设置不支持所有代码点(简化:文本中的 Unicode 字符)的字体可能会导致字体回退。
实际上,系统将 select 替换控件字体的映射代理字体。这种情况透明地发生并且没有通知。
在类似情况下,不同的图形渲染器可能会以不同的方式运行。

在此处查看注释:

在 RichTextBox 控件中有些不同(.Net 基础 class 相同,但 Win32 控件完全不同):

此外,最近,所有亚洲字体渲染和 UI 表示都已修改,以更好地处理其特性。所以使用的 .Net 版本也可以改变行为。
这里的代码是用 .Net Framework 4.8.

测试的
public partial class CustomEnabledStateTextBox : TextBox
{
    public CustomEnabledStateTextBox() => this.AutoSize = false;

    protected override void OnEnabledChanged(EventArgs e)
    {
        SetStyle(ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint, !Enabled);
        UpdateStyles();
        base.OnEnabledChanged(e);
    }

    protected override void OnBorderStyleChanged(EventArgs e)
    {
        base.OnBorderStyleChanged(e);
        if (BorderStyle == BorderStyle.FixedSingle) BorderStyle = BorderStyle.Fixed3D;
    }

    private TextFormatFlags flags = TextFormatFlags.TextBoxControl | 
                                    TextFormatFlags.NoPadding;
    protected override void OnPaint(PaintEventArgs e)
    {
        var rect = ClientRectangle;
        if (BorderStyle != BorderStyle.None) rect.Inflate(-1, -1);

        var alignment = TextAlign == HorizontalAlignment.Left
                      ? TextFormatFlags.Left : (TextFormatFlags)((int)TextAlign^ 3);

        flags |= alignment;
        TextRenderer.DrawText(e.Graphics, Text, Font, rect, ForeColor, flags);
        base.OnPaint(e);
    }
}