c# – 无法计算所有者绘制文本中的位置
我正在尝试使用Visual Studio 2012创建一个
Windows窗体应用程序,可以将插入符号放在所有者绘制的字符串中的当前位置.但是,我一直无法找到准确计算该位置的方法.
I’ve done this successfully before in C++.我在C#中尝试了很多方法,但还没有准确定位插入符号.最初,我尝试使用.NET类来确定正确的位置,但后来我尝试直接访问Windows API.在某些情况下,我走近了,但过了一段时间后,我仍然无法准确地放置插入符号. 我创建了一个小测试程序,并在下面发布了关键部分.我还发布了整个项目here. 使用的确切字体对我来说并不重要;但是,我的应用程序采用单倍间距字体.任何帮助表示赞赏. Form1.cs的 public partial class Form1 : Form { private string TestString; private int AveCharWidth; private int Position; public Form1() { InitializeComponent(); TestString = "123456789012345678901234567890123456789012345678901234567890"; AveCharWidth = GetFontWidth(); Position = 0; } private void Form1_Load(object sender,EventArgs e) { Font = new Font(FontFamily.GenericMonospace,12,FontStyle.Regular,GraphicsUnit.Pixel); } protected override void OnGotFocus(EventArgs e) { Windows.CreateCaret(Handle,(IntPtr)0,2,(int)Font.Height); Windows.ShowCaret(Handle); UpdateCaretPosition(); base.OnGotFocus(e); } protected void UpdateCaretPosition() { Windows.SetCaretPos(Padding.Left + (Position * AveCharWidth),Padding.Top); } protected override void OnLostFocus(EventArgs e) { Windows.HideCaret(Handle); Windows.DestroyCaret(); base.OnLostFocus(e); } protected override void OnPaint(PaintEventArgs e) { e.Graphics.DrawString(TestString,Font,SystemBrushes.WindowText,new PointF(Padding.Left,Padding.Top)); } protected override bool IsInputKey(Keys keyData) { switch (keyData) { case Keys.Right: case Keys.Left: return true; } return base.IsInputKey(keyData); } protected override void OnKeyDown(KeyEventArgs e) { switch (e.KeyCode) { case Keys.Left: Position = Math.Max(Position - 1,0); UpdateCaretPosition(); break; case Keys.Right: Position = Math.Min(Position + 1,TestString.Length); UpdateCaretPosition(); break; } base.OnKeyDown(e); } protected int GetFontWidth() { int AverageCharWidth = 0; using (var graphics = this.CreateGraphics()) { try { Windows.TEXTMETRIC tm; var hdc = graphics.GetHdc(); IntPtr hFont = this.Font.ToHfont(); IntPtr hOldFont = Windows.SelectObject(hdc,hFont); var a = Windows.GetTextMetrics(hdc,out tm); var b = Windows.SelectObject(hdc,hOldFont); var c = Windows.DeleteObject(hFont); AverageCharWidth = tm.tmAveCharWidth; } catch { } finally { graphics.ReleaseHdc(); } } return AverageCharWidth; } } Windows.cs public static class Windows { [Serializable,StructLayout(LayoutKind.Sequential,CharSet = CharSet.Auto)] public struct TEXTMETRIC { public int tmHeight; public int tmAscent; public int tmDescent; public int tmInternalLeading; public int tmExternalLeading; public int tmAveCharWidth; public int tmMaxCharWidth; public int tmWeight; public int tmOverhang; public int tmDigitizedAspectX; public int tmDigitizedAspectY; public short tmFirstChar; public short tmLastChar; public short tmDefaultChar; public short tmBreakChar; public byte tmItalic; public byte tmUnderlined; public byte tmStruckOut; public byte tmPitchAndFamily; public byte tmCharSet; } [DllImport("user32.dll")] public static extern bool CreateCaret(IntPtr hWnd,IntPtr hBitmap,int nWidth,int nHeight); [DllImport("User32.dll")] public static extern bool SetCaretPos(int x,int y); [DllImport("User32.dll")] public static extern bool DestroyCaret(); [DllImport("User32.dll")] public static extern bool ShowCaret(IntPtr hWnd); [DllImport("User32.dll")] public static extern bool HideCaret(IntPtr hWnd); [DllImport("gdi32.dll",CharSet = CharSet.Auto)] public static extern bool GetTextMetrics(IntPtr hdc,out TEXTMETRIC lptm); [DllImport("gdi32.dll")] public static extern IntPtr SelectObject(IntPtr hdc,IntPtr hgdiobj); [DllImport("GDI32.dll")] public static extern bool DeleteObject(IntPtr hObject); } 编辑 我发布的代码有一个问题,使其更加不准确.这是尝试许多不同方法的结果,有些方法比这更准确.我正在寻找的是一个使其“完全准确”的修复,就像在我的MFC Hex Editor Control in C++中一样. 解决方法
我试用了你的GetFontWidth(),返回的字符宽度为7.
然后,我在不同长度的文本上尝试了TextRenderer.MearureText,对于长度为1到50的文本,其值的范围从14到7.14,平均字符宽度为7.62988874736612. 这是我使用的代码: var text = ""; var sizes = new System.Collections.Generic.List<double>(); for (int i = 1; i <= 50; i++) { text += (i % 10).ToString(); var ts = TextRenderer.MeasureText(text,this.Font); sizes.Add((ts.Width * 1.0) / text.Length); } sizes.Add(sizes.Average()); Clipboard.SetText(string.Join("rn",sizes)); 对我的小“实验”的结果不满意,我决定看看文本是如何呈现在表单上的.下面是表格的屏幕截图(放大8倍). 经过仔细检查,我发现了这一点 >角色之间存在一定的分离.这使得文本块(1234567890)的长度为74像素长. 这对你意味着什么? >如果使用代码计算字体字符的宽度,则无法考虑两个字符之间的分隔空间. 你剩下的选择是什么? >我能看到的最好的方法是硬编码文本的位置.这样你就可以知道每个角色的位置,并且可以准确地将光标放在任何所需的位置. 我观察到的其他事情 >插入文本前面的空格相当于插入符号的宽度(在我的情况下为2px)加上1px(总共3px).>将每个字符的宽度硬编码为7.4可以完美地工作. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |