c# – 无法计算所有者绘制文本中的位置

c# – 无法计算所有者绘制文本中的位置,第1张

概述我正在尝试使用Visual Studio 2012创建一个 Windows窗体应用程序,可以将插入符号放在所有者绘制的字符串中的当前位置.但是,我一直无法找到准确计算该位置的方法. I’ve done this successfully before in C++.我在C#中尝试了很多方法,但还没有准确定位插入符号.最初,我尝试使用.NET类来确定正确的位置,但后来我尝试直接访问Windows A 我正在尝试使用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
这是我的windows API声明.

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("\r\n",sizes));

对我的小“实验”的结果不满意,我决定看看文本是如何呈现在表单上的.下面是表格的屏幕截图(放大8倍).

经过仔细检查,我发现了这一点

>角色之间存在一定的分离.这使得文本块(1234567890)的长度为74像素长.
>即使左边填充为0,也会在绘制文本前面留出一些空格(3px).

这对你意味着什么?

>如果使用代码计算字体字符的宽度,则无法考虑两个字符之间的分隔空间.
>使用TextRenderer.DrawText可以为您提供不同的字符宽度,使其变得毫无用处.

你剩下的选择是什么?

>我能看到的最好的方法是硬编码文本的位置.这样你就可以知道每个角色的位置,并且可以准确地将光标放在任何所需的位置.
毋庸置疑,这可能需要大量的代码.
>您的第二个选择是像我一样运行测试以查找文本块的长度,然后除以块的长度以找到平均字符宽度.
这样做的问题是您的代码不太可能正确扩展.例如,@R_322_6502@大小或用户屏幕DPI可能会给程序带来很多麻烦.

我观察到的其他事情

>插入文本前面的空格相当于插入符号的宽度(在我的情况下为2px)加上1px(总共3px).>将每个字符的宽度硬编码为7.4可以完美地工作.

总结

以上是内存溢出为你收集整理的c# – 无法计算所有者绘制文本中的位置全部内容,希望文章能够帮你解决c# – 无法计算所有者绘制文本中的位置所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/langs/1244270.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-06-06
下一篇 2022-06-06

发表评论

登录后才能评论

评论列表(0条)

保存