切换启用时文本框的文本略有移动:字体和字体大小更改

Posted

技术标签:

【中文标题】切换启用时文本框的文本略有移动:字体和字体大小更改【英文标题】:TextBox's Text is moving slightly when toggling Enabled: Font and Font size changes 【发布时间】:2021-09-23 05:14:17 【问题描述】:

在我的应用程序中,我有自定义文本框控件。当我更改 Enabled 属性时,它的文本似乎在移动。

    我不明白这是什么原因。

    我将更改 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 和 e.Graphics.DrawString

↓↓ Enabled ↓↓

↓↓ Disabled ↓↓

comparison

MS ゴシック、10pt 和 TextAlign Center 和 TextRenderer.DrawText

↓↓ Enabled ↓↓

↓↓ Disabled ↓↓

comparison (text seems to be slightly elongated when disabled)

我的代码的原始帖子 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 的情况下,文本略微向下移动。我的应用程序使用 MS ゴシック 7pt~12pt。所以我必须正确显示 10pt。 我还没有测试过其他字体。 请帮忙。

对不起,我之前没有提供 .Net 框架 我正在使用

 .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 And TextAlign Center and BorderStyle None andTextRenderer.DrawText

MS UI Gothic、10pt 和 TextAlign Center 和 BorderStyle None 和 TextRenderer.DrawText

更新代码如下: 我在 .Net Framework 3.5 和 4.8 中都对其进行了测试

CustomEnabledStateTextBox:

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;
        
    


【问题讨论】:

【参考方案1】:

对文本渲染的一些更改应该可以让您接近您的预期。

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

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


请注意,设置不支持设置控件文本时使用的所有 CodePoint(简化:文本中的 Unicode 字符)的字体可能会导致字体回退。 在实践中,系统将选择一个映射的代理字体来替换控件的字体。这发生在透明且没有通知。 不同的图形渲染器在类似情况下的行为方式可能不同。

在此处查看注释:How can a Label control display Japanese characters properly when the Font used doesn't support this language?

RichTextBox 控件有些不同(.Net 基类相同,但 Win32 控件完全不同):Some Alt keys changes my RichTextBox font

此外,最近,所有亚洲字体渲染和 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);
    

【讨论】:

感谢您的回答。它解决了一些问题,但 MS ゴシック,10pt 问题仍然存在。我已经更新了我的帖子。 MS UI Gothic 是我的语言(en-US)中的日语后备字体之一:它通常是隐藏的,仅用作后备字体代理,如此处所述。我已经测试过这个字体,它和我测试过的所有其他字体完全一样(是的,我已经测试过 10pt)。如前所述,您需要 .Net Framework 4.8 或 .Net Core 3.1 或 .Net 5 才能进行正确测试。 是的,我已经在 .NetFramework 4.8 中安装并测试过了。我也更改了 AutoScaleMode。问题仍然存在于我的环境中。虽然我的显示语言是日语。我也检查了注册表。它有 MS UI 哥特式。 HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\FontLink\SystemLink 也许我应该避免 10pt 。谢谢吉米 是的,当然,如果您的系统语言是日语,那么 MS UI Gothic 可以设置为 visible(不隐藏,因为在我的中用作备用字体)。 -- 您应该允许使用 10pt。我不确定你为什么对那个尺寸有问题,因为如前所述,我已经对其进行了测试(取消隐藏字体)并且 10pt 与其他尺寸具有相同的结果(10pt 实际上是 9.75pt)。也许让我知道这段代码在什么系统和版本中测试过,这样我就可以尝试复制这个问题。 我将 MS ゴシック, 10pt 更改为新字体(control.FontFamily, 10.2f),问题就解决了。谢谢。

以上是关于切换启用时文本框的文本略有移动:字体和字体大小更改的主要内容,如果未能解决你的问题,请参考以下文章

PDF文本框更改字体大小

如何使用 css 更改一个选择框选项文本(select2)的字体大小?

如何更改导航栏中按钮文本的字体大小?

Libre office Draw如何更改pdf表单可填写文本框的字体颜色

防止在更改字体粗细时移动文本[重复]

如何更改以前在 textBrowser 中编写的文本的字体大小?