In my application, I have Custom TextBox Control. Its Text seems to be moving when I change the Enabled
property.
I don't understand what cause this.
I will be changing TextAlign
, AutoSize
, Enabled
, and Font
properties
I've tried using TextRenderer.DrawText()
: it seems to fix some issues but when I change to certain Font and Font size, the Text is moving again (for example, setting MS ゴシック, 10pt).
I have searched the site and read the following but didn't seem to understand properly:
Using Graphics.DrawString to Simulate TextBox rendering
How do I use DrawString without trimming?
Graphics.DrawString vs TextRenderer.DrawText?Which can Deliver Better Quality
Here are some screenshots:
MS ゴシック, 10pt And TextAlign Center and e.Graphics.DrawString
MS ゴシック, 10pt And TextAlign Center and TextRenderer.DrawText
comparison (text seems to be slightly elongated when disabled)
Original post of my code https://devlights.hatenablog.com/entry/20070523/p1
This is my Custom Control code do far:
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);
}
}
}
}
Test Form:
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;
}
}
Update 1
I have Changed my code according to Answer provided by Jimi
And tested it and it fix some issues but the problem still exits when I used
MS ゴシック, 10pt And fount out that in both MS UI Gothic, 10pt AND MS ゴシック, 10pt cases , Text is moving slightly downward.My application use MS ゴシック 7pt~12pt.So I have to show 10pt correctly.
I haven't tested with other Fonts though.
Please Help.
Sorry that I didn`t provide .Net framework before I am be using
.NET Framework 3.5
BorderStyle None
Test Case
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
Here are Screenshots for
MS ゴシック, 10pt And TextAlign Center and BorderStyle None andTextRenderer.DrawText
MS UI Gothic, 10pt And TextAlign Center and BorderStyle None and TextRenderer.DrawText
Updated Code below : I have tested it in both .Net Framework 3.5 And 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);
}
}
}
Test Form:
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;
}
}
}
A few changes to the text rendering should get you close to what you're expecting.
TextFormatFlags.TextBoxControl
and TextFormatFlags.NoPadding
to the TextFormatFlags used in TextRenderer.DrawText()
(see the Docs about these two)OnBorderStyleChanged()
to replace the FixedSingle
style with Fixed3D
when the Control is custom-drawn. This is what the base class (TextBoxBase) does, otherwise you get the default internal border that shrinks the Win32 Control's Client area.-1
pixel when BorderStyle = BorderStyle.Fixed3D
.I added UpdateStyles() after the SetStyle()
call: this forces the new Styles and also causes the Control to repaint itself.
Note that setting a Font that doesn't support all CodePoint (simplified: the Unicode chars in your Text) used when setting the Text of a control, may cause a Font Fallback.
In practice, the System will select a mapped surrogate Font that replaces the Control's Font. This happens transparently and without notice.
A different Graphic renderer can behave in a different manner in similar situations.
See the notes here:
How can a Label control display Japanese characters properly when the Font used doesn't support this language?
Somewhat different in a RichTextBox Control (the .Net base class is the same, but the Win32 Control is quite different):
Some Alt keys changes my RichTextBox font
Also, quite recently, all Asian Font rendering and UI presentation has been modified to better handle its characteristics. So the .Net version in use can also change the behavior.
The code here is tested with .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);
}
}