最近在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。不然某些情况下会出问题。
或许有更简单的方法,上面的语句有不严谨的地方。仅作记录。