winform tooltip获取宽度的问题

最近在listbox中使用tooltip提示内容。在listbox中使用在mouseMove事件中通过IndexFromPoint来获取当前索引。 

如图:

1、对于tooltip1来说,比较简单,而且没有问题,代码处理大致如下:

        private void listBox1_MouseMove(object sender, MouseEventArgs e)
        {
            int index = listBox1.IndexFromPoint(e.Location);//获取当前鼠标所在index
            if (index > 0 || index < listBox1.Items.Count - 1)
            {
                if (index != foreIndex)

                {

// + 5只是为了明显分隔开

   toolTip1.Show(listBox1.Items[index].ToString(),listBox1,new Point(listBox1.Width+5,((e.Location.Y / listBox1.ItemHeight) * listBox1.ItemHeight)));
                    foreIndex = index;
                }
            }
        }
        private void listBox1_MouseLeave(object sender, EventArgs e)
        {
            foreIndex = -1;

        }

2、对于tooltip来说,会遮挡住内容,所以暂不考虑。因为在使用在自定义的listbox中时,会导致鼠标落在tooltip中而不触发listbox的mouseMove事件,而现在使用系统listbox时没有出现问题。

3、假定现在左边到屏幕边框的距离不够我们显示tooltip内容,我们想显示到右边,即如tooltip2所示。现在我们需要得到tooltip的宽度。

最开始的时候想通过 graphcs.MeasureString(str,font) 计算tooltip.Text的长度,需要得到tooltip使用的字体样式,无从得知,因为无法正确解读源码。(给大家推荐一下https://referencesource.microsoft.com/ 可以看到内部实现)在OnPaint消息中这样处理:

Font font;
                            IntSecurity.ObjectFromWin32Handle.Assert();
                            try {
                                font = Font.FromHfont(UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.WM_GETFONT, 0, 0));
                            }
                            catch (ArgumentException) {
                                // VSWhidbey 209345 - if the current default tooltip font is a non-TrueType font, then
                                // Font.FromHfont throws this exception, so fall back to the default control font.
                                font = Control.DefaultFont;
                            }

尝试用Control.DefaultFont去获取长度,宽度依旧有问题。

接下来想着获取不到字体,就使用自己的font去显示tooltip,使能tooltip的OwnerDraw属性,并创建一个OnDraw的委托实例。在OnDraw中我们自己定义绘制方式:

        private void toolTip1_Draw(object sender, DrawToolTipEventArgs e)
        {
            e.DrawBackground();
            StringFormat sa = new StringFormat();
            sa.Alignment = StringAlignment.Center;
            sa.LineAlignment = StringAlignment.Center;
            e.DrawBorder();
            using (Font f = new Font(("宋体"),8))
                e.Graphics.DrawString(e.ToolTipText,f,Brushes.Black,e.Bounds,sa);

        }

在调试过程中,发现 text的size和所绘制的e.Bounds不一致。

于是就想着获取e.Bounds的大小,因为只有在OnDraw中才能获取,就得触发这个事件,但是还不能显示。所以这种方法会导致闪烁的问题。

最后还是自己定义一个显示的tooltip来显示内容吧。自定义控件,只能在窗体内使用,不能显示到窗体外面。自定义组件不会。最后决定从新建一个窗体。

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 WindowsFormsApplication1
{
    public partial class TestTooltip : Form
    {
        public TestTooltip()
        {
            Padding = new Padding(5);
            this.Font = Control.DefaultFont;
            InitializeComponent();
        }




        //显示的字符串
        String showStr;


        //Point showPoint;


        /// <param name="str">字符串</param>
        public void SetText(string str)
        {
            showStr = string.Empty;//清空之前的内容
            Invalidate();


            showStr = str;
            //showPoint = pt;


            Graphics g = CreateGraphics();
            SizeF size = g.MeasureString(str, this.Font);


            this.Width = Padding.Left + Padding.Right + (int)size.Width;
            this.Height = Padding.Top + Padding.Bottom + (int)size.Height;
        }


        /// <summary>
        /// 在pt处显示
        /// </summary>
        /// <param name="pt">要显示的点,转换到屏幕上的点</param>
        public void ShowTip(Point pt)
        {
            this.Location = pt;
            base.Show();
        }

        private void TestTooltip_Paint(object sender, PaintEventArgs e)

        {

            //如果绘制整个界面,不需要调用base.OnPaint(),可以避免引起闪烁(MSDN)

            e.Graphics.Clear(Color.Red);//红色显眼
            e.Graphics.DrawString(showStr,this.Font,Brushes.Black,new Point(Padding.Left,Padding.Top));
        }
    }

}

来看一下测试代码:

         int foreIndex = -1;//记录上一个index

        TestTooltip testTip = new TestTooltip();
        private void listBox1_MouseMove(object sender, MouseEventArgs e)
        {
            int index = listBox1.IndexFromPoint(e.Location);//获取当前鼠标所在index
            if (index > 0 || index < listBox1.Items.Count - 1)
            {
                if (index != foreIndex)
                {
                    Graphics g = listBox1.CreateGraphics();
                    testTip.SetText(listBox1.Items[index].ToString());
                    Point screenPt = listBox1.PointToScreen(new Point(-testTip.Width - 5, ((e.Location.Y / listBox1.ItemHeight) * listBox1.ItemHeight)));
                    testTip.ShowTip(screenPt);
                    this.Activate();//需要释放testtip获得的焦点,转移到主窗体中
                    foreIndex = index;
                }
            }
        }
        private void listBox1_MouseLeave(object sender, EventArgs e)
        {
            foreIndex = -1;
            if (testTip.Visible)
                testTip.Visible = false;
        }


        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (!testTip.IsDisposed)
            {
                testTip.Dispose();
            }

        }

1、在showTip之后通过Activate()使主窗体获得焦点

2、在MouseLeave的时候使testtip不可见。

3、在主窗体关闭时,释放testtip。不然某些情况下会出问题。


或许有更简单的方法,上面的语句有不严谨的地方。仅作记录。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值