Gtk# NodeView控件Tooltip显示错位至错误行问题咨询
Gtk# NodeView控件Tooltip行错位问题
问题描述
在Gtk#的NodeView控件使用过程中,Tooltip存在异常错位现象:具体原因为GetPathAtPos方法返回TreePath时未将表头行的高度纳入计算,最终导致Tooltip显示在错误的行位置。
复现代码
using Gtk; [TreeNode(ListOnly = true)] public class MyTreeNode : TreeNode { [TreeNodeValue(Column = 0)] public string RowNumber { get; set; } = ""; [TreeNodeValue(Column = 1)] public string Data { get; set; } = ""; } public partial class MainWindow : Gtk.Window { public MainWindow() : base(Gtk.WindowType.Toplevel) { Build(); nodeview1.AppendColumn("Row Number", new CellRendererText(), "text", 0); nodeview1.AppendColumn("Data", new CellRendererText(), "text", 1); NodeStore nodeStore = new NodeStore(typeof(MyTreeNode)); nodeStore.AddNode(new MyTreeNode { RowNumber = "One", Data = "foo" }); nodeStore.AddNode(new MyTreeNode { RowNumber = "Two", Data = "bar" }); nodeStore.AddNode(new MyTreeNode { RowNumber = "Three", Data = "baaz" }); nodeStore.AddNode(new MyTreeNode { RowNumber = "Four", Data = "quux" }); nodeview1.NodeStore = nodeStore; nodeview1.HasTooltip = true; nodeview1.QueryTooltip += Nodeview1_QueryTooltip; } private void Nodeview1_QueryTooltip(object o, QueryTooltipArgs args) { bool result = false; if (nodeview1.GetPathAtPos(args.X, args.Y, out TreePath path)) { TreeModel model = nodeview1.Model; if (model.GetIter(out TreeIter iter, path)) { string rowNumber = model.GetValue(iter, 0) as string; args.Tooltip.Text = "This is the tooltip for Row Number " + rowNumber; result = true; } } args.RetVal = result; } protected void OnDeleteEvent(object sender, DeleteEventArgs a) { Application.Quit(); a.RetVal = true; } }
复现表现
运行上述代码时会出现固定偏移:
- 鼠标悬停在
RowNumber属性为"Three"的行上,Tooltip显示内容为 "This is the tooltip for Row Number Four" - 鼠标悬停在表头区域时,Tooltip会显示
RowNumber为"One"的第一行提示内容 - 所有行的Tooltip均存在等距偏移问题
临时方案与诉求
目前临时规避方案为调用GetPathAtPos时传入Y轴参数为args.Y - 23,测试环境下表头行高度约为23像素,但硬编码固定像素值适配性差,不符合开发规范。
需要寻找该问题的正式修复方案,或是具备动态适配性的规避方法(例如运行时动态获取表头行高度的实现方式)。
测试环境
测试代码在Ubuntu 22.04 LTS环境下通过MonoDevelop 7.8.4 (build 2)编译,环境说明如下:
- 当前MonoDevelop仅支持GTK 2版本,若有支持更高版本GTK的配置方案也可参考
- 测试所用Ubuntu系统运行于VirtualBox虚拟机中,宿主操作系统为Windows 10
解决方案
这个偏移本质是坐标参考系不匹配导致的,不属于需要补丁修复的源码bug:
QueryTooltip事件回调中传入的args.X、args.Y是相对于整个NodeView控件左上角的坐标,包含表头区域GetPathAtPos方法要求传入的坐标是相对于NodeView内容区域(BinWindow)左上角的坐标,不包含表头区域
硬编码减去固定23像素仅能匹配当前测试环境的表头高度,更换系统主题、修改字体、调整DPI缩放后都会失效。
动态适配实现
直接调用ConvertWidgetToBinWindowCoords方法做坐标转换即可自动扣除表头高度,不需要手动获取表头像素值,适配所有场景。将原回调逻辑修改为如下代码即可:
private void Nodeview1_QueryTooltip(object o, QueryTooltipArgs args) { bool result = false; // 把控件全局坐标转换为内容区域相对坐标,自动适配任意高度的表头 nodeview1.ConvertWidgetToBinWindowCoords(args.X, args.Y, out int binX, out int binY); // 传入转换后的坐标匹配行路径 if (nodeview1.GetPathAtPos(binX, binY, out TreePath path)) { TreeModel model = nodeview1.Model; if (model.GetIter(out TreeIter iter, path)) { string rowNumber = model.GetValue(iter, 0) as string; args.Tooltip.Text = "This is the tooltip for Row Number " + rowNumber; result = true; } } args.RetVal = result; }
补充说明
- 该写法在GTK2、GTK3版本的Gtk#下均可正常使用,无版本兼容问题
- 即使后续设置
HeadersVisible = false隐藏表头,转换逻辑也能自动适配,不需要加额外判断分支 - 关于GTK版本支持:原生旧版MonoDevelop确实仅支持GTK2栈,若要使用GTK3开发,可直接引用Gtk# 3.x版本的绑定库,搭配支持GTK3的.NET开发环境即可,不需要依赖旧版MonoDevelop。
内容的提问来源于stack exchange,提问作者CSquared
相关产品推荐
相关产品推荐

