我可以为我在面板上绘制的节点显示工具提示吗?
Can I show ToolTip for the nodes I am painting on a panel?
我有一个 MMO 网状系统,它使用 A* 来寻找路径。有时它会失败,因为我的节点放置不当。为了解决这个问题,我制作了一个网格可视化工具。它工作正常 - 我可以看到一些节点放置不当。但是我看不到哪些节点。
这是我显示节点的代码:
foreach (var node in FormMap.Nodes)
{
var x1 = (node.Point.X * sideX);
var y1 = (node.Point.Y * sideY);
var x = x1 - nodeWidth / 2;
var y = y1 - nodeWidth / 2;
var brs = Brushes.Black;
//if (node.Visited)
// brs = Brushes.Red;
if (node == FormMap.StartNode)
brs = Brushes.DarkOrange;
if (node == FormMap.EndNode)
brs = Brushes.Green;
g.FillEllipse(brs, (float)x, (float)y, nodeWidth, nodeWidth);
我知道我可以重做这个并制作数千个小按钮并为它们添加事件,但这似乎有点过分了。
有什么方法可以向我在面板上绘制的节点添加工具提示吗?
是的,您可以为在绘图表面上绘制的节点显示工具提示。为此,您需要执行以下操作:
- 为你的节点实现hit-testing,这样你就可以得到鼠标位置下的节点。
- 创建一个计时器并在绘图表面的鼠标移动事件处理程序中,执行hit-testing 查找热门项目。如果热点节点与当前热点节点不同,则停止定时器,否则,如果有新的热点节点,则启动定时器。
- 在计时器滴答事件处理程序中,检查是否有热门项目,显示工具提示并停止计时。
- 在绘图面的鼠标离开事件中,停止定时器。
结果如下,显示了绘图中某些点的工具提示:
上述算法正在 used in internal logic of ToolStrip control 显示工具条项(不是控件)的工具提示。因此,在不浪费大量 windows 句柄的情况下,使用单个父控件和单个工具提示,您可以为任意数量的节点显示工具提示。
代码示例 - 显示绘图中某些点的工具提示
这里是绘图面:
using System.ComponentModel;
using System.Drawing.Drawing2D;
public class DrawingSurface : Control
{
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[Browsable(false)]
public List<Node> Nodes { get; }
public DrawingSurface()
{
Nodes = new List<Node>();
ResizeRedraw = true;
DoubleBuffered = true;
toolTip = new ToolTip();
mouseHoverTimer = new System.Windows.Forms.Timer();
mouseHoverTimer.Enabled = false;
mouseHoverTimer.Interval = SystemInformation.MouseHoverTime;
mouseHoverTimer.Tick += mouseHoverTimer_Tick;
}
private void mouseHoverTimer_Tick(object sender, EventArgs e)
{
mouseHoverTimer.Enabled = false;
if (hotNode != null)
{
var p = hotNode.Location;
p.Offset(16, 16);
toolTip.Show(hotNode.Name, this, p, 2000);
}
}
private System.Windows.Forms.Timer mouseHoverTimer;
private ToolTip toolTip;
Node hotNode;
protected override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
var node = Nodes.Where(x => x.HitTest(e.Location)).FirstOrDefault();
if (node != hotNode)
{
mouseHoverTimer.Enabled = false;
toolTip.Hide(this);
}
hotNode = node;
if (node != null)
mouseHoverTimer.Enabled = true;
Invalidate();
}
protected override void OnMouseLeave(EventArgs e)
{
base.OnMouseLeave(e);
hotNode = null;
mouseHoverTimer.Enabled = false;
Invalidate();
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
if (Nodes.Count >= 2)
e.Graphics.DrawLines(Pens.Black,
Nodes.Select(x => x.Location).ToArray());
foreach (var node in Nodes)
node.Draw(e.Graphics, node == hotNode);
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
if (mouseHoverTimer != null)
{
mouseHoverTimer.Enabled = false;
mouseHoverTimer.Dispose();
}
if (toolTip != null)
{
toolTip.Dispose();
}
}
base.Dispose(disposing);
}
}
这里是节点class:
using System.Drawing.Drawing2D;
public class Node
{
int NodeWidth = 16;
Color NodeColor = Color.Blue;
Color HotColor = Color.Red;
public string Name { get; set; }
public Point Location { get; set; }
private GraphicsPath GetShape()
{
GraphicsPath shape = new GraphicsPath();
shape.AddEllipse(Location.X - NodeWidth / 2, Location.Y - NodeWidth / 2,
NodeWidth, NodeWidth);
return shape;
}
public void Draw(Graphics g, bool isHot = false)
{
using (var brush = new SolidBrush(isHot ? HotColor : NodeColor))
using (var shape = GetShape())
{
g.FillPath(brush, shape);
}
}
public bool HitTest(Point p)
{
using (var shape = GetShape())
return shape.IsVisible(p);
}
}
下面是示例窗体,上面有一个绘图界面控件:
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
drawingSurface1.Nodes.Add(new Node() {
Name = "Node1", Location = new Point(100, 100) });
drawingSurface1.Nodes.Add(new Node() {
Name = "Node2", Location = new Point(150, 70) });
drawingSurface1.Nodes.Add(new Node() {
Name = "Node3", Location = new Point(170, 140) });
drawingSurface1.Nodes.Add(new Node() {
Name = "Node4", Location = new Point(200, 50) });
drawingSurface1.Nodes.Add(new Node() {
Name = "Node5", Location = new Point(90, 160) });
drawingSurface1.Invalidate();
}
我有一个 MMO 网状系统,它使用 A* 来寻找路径。有时它会失败,因为我的节点放置不当。为了解决这个问题,我制作了一个网格可视化工具。它工作正常 - 我可以看到一些节点放置不当。但是我看不到哪些节点。
这是我显示节点的代码:
foreach (var node in FormMap.Nodes)
{
var x1 = (node.Point.X * sideX);
var y1 = (node.Point.Y * sideY);
var x = x1 - nodeWidth / 2;
var y = y1 - nodeWidth / 2;
var brs = Brushes.Black;
//if (node.Visited)
// brs = Brushes.Red;
if (node == FormMap.StartNode)
brs = Brushes.DarkOrange;
if (node == FormMap.EndNode)
brs = Brushes.Green;
g.FillEllipse(brs, (float)x, (float)y, nodeWidth, nodeWidth);
我知道我可以重做这个并制作数千个小按钮并为它们添加事件,但这似乎有点过分了。
有什么方法可以向我在面板上绘制的节点添加工具提示吗?
是的,您可以为在绘图表面上绘制的节点显示工具提示。为此,您需要执行以下操作:
- 为你的节点实现hit-testing,这样你就可以得到鼠标位置下的节点。
- 创建一个计时器并在绘图表面的鼠标移动事件处理程序中,执行hit-testing 查找热门项目。如果热点节点与当前热点节点不同,则停止定时器,否则,如果有新的热点节点,则启动定时器。
- 在计时器滴答事件处理程序中,检查是否有热门项目,显示工具提示并停止计时。
- 在绘图面的鼠标离开事件中,停止定时器。
结果如下,显示了绘图中某些点的工具提示:
上述算法正在 used in internal logic of ToolStrip control 显示工具条项(不是控件)的工具提示。因此,在不浪费大量 windows 句柄的情况下,使用单个父控件和单个工具提示,您可以为任意数量的节点显示工具提示。
代码示例 - 显示绘图中某些点的工具提示
这里是绘图面:
using System.ComponentModel;
using System.Drawing.Drawing2D;
public class DrawingSurface : Control
{
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[Browsable(false)]
public List<Node> Nodes { get; }
public DrawingSurface()
{
Nodes = new List<Node>();
ResizeRedraw = true;
DoubleBuffered = true;
toolTip = new ToolTip();
mouseHoverTimer = new System.Windows.Forms.Timer();
mouseHoverTimer.Enabled = false;
mouseHoverTimer.Interval = SystemInformation.MouseHoverTime;
mouseHoverTimer.Tick += mouseHoverTimer_Tick;
}
private void mouseHoverTimer_Tick(object sender, EventArgs e)
{
mouseHoverTimer.Enabled = false;
if (hotNode != null)
{
var p = hotNode.Location;
p.Offset(16, 16);
toolTip.Show(hotNode.Name, this, p, 2000);
}
}
private System.Windows.Forms.Timer mouseHoverTimer;
private ToolTip toolTip;
Node hotNode;
protected override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
var node = Nodes.Where(x => x.HitTest(e.Location)).FirstOrDefault();
if (node != hotNode)
{
mouseHoverTimer.Enabled = false;
toolTip.Hide(this);
}
hotNode = node;
if (node != null)
mouseHoverTimer.Enabled = true;
Invalidate();
}
protected override void OnMouseLeave(EventArgs e)
{
base.OnMouseLeave(e);
hotNode = null;
mouseHoverTimer.Enabled = false;
Invalidate();
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
if (Nodes.Count >= 2)
e.Graphics.DrawLines(Pens.Black,
Nodes.Select(x => x.Location).ToArray());
foreach (var node in Nodes)
node.Draw(e.Graphics, node == hotNode);
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
if (mouseHoverTimer != null)
{
mouseHoverTimer.Enabled = false;
mouseHoverTimer.Dispose();
}
if (toolTip != null)
{
toolTip.Dispose();
}
}
base.Dispose(disposing);
}
}
这里是节点class:
using System.Drawing.Drawing2D;
public class Node
{
int NodeWidth = 16;
Color NodeColor = Color.Blue;
Color HotColor = Color.Red;
public string Name { get; set; }
public Point Location { get; set; }
private GraphicsPath GetShape()
{
GraphicsPath shape = new GraphicsPath();
shape.AddEllipse(Location.X - NodeWidth / 2, Location.Y - NodeWidth / 2,
NodeWidth, NodeWidth);
return shape;
}
public void Draw(Graphics g, bool isHot = false)
{
using (var brush = new SolidBrush(isHot ? HotColor : NodeColor))
using (var shape = GetShape())
{
g.FillPath(brush, shape);
}
}
public bool HitTest(Point p)
{
using (var shape = GetShape())
return shape.IsVisible(p);
}
}
下面是示例窗体,上面有一个绘图界面控件:
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
drawingSurface1.Nodes.Add(new Node() {
Name = "Node1", Location = new Point(100, 100) });
drawingSurface1.Nodes.Add(new Node() {
Name = "Node2", Location = new Point(150, 70) });
drawingSurface1.Nodes.Add(new Node() {
Name = "Node3", Location = new Point(170, 140) });
drawingSurface1.Nodes.Add(new Node() {
Name = "Node4", Location = new Point(200, 50) });
drawingSurface1.Nodes.Add(new Node() {
Name = "Node5", Location = new Point(90, 160) });
drawingSurface1.Invalidate();
}