其实经过【基础2】~【基础6】、以及【基础8】的内容,几乎所有插件的后台数据处理流程都可以实现了(往往也是最关键的业务核心内容):
- 从
RegisterInputParam和RegisterOutputParam中注册数据的入口和出口 - 在
SolveInstance中处理及传递数据 - 在
Read和Write中进行数据的序列化与反序列化
这也是为什么很多时候,目前大部分的GH教程也是大致到这个节点步骤就结束了。因为依靠这些内容,一套完整的业务逻辑可以完成,无非就是电池样子长得平平无奇一点嘛。
不过,很多时候我们也不仅仅满足于“实现流程”,还要 实现花里胡哨的功能 增强用户体验,此时就需要在前端交互这个部分内容上下很大的功夫了。
既然目前【基础】系列的后端数据流程已经基本收尾完成。接下来所有的【基础】类教程大部分时间都会聚焦于如何处理和应对 GH特有的前端逻辑框架 —— 将自己想实现的功能在这个框架下搭建完成。
前段时间实在是太忙了,断更了好几个礼拜,接下来的一段时间工作上仍然会有很多事情,要详细地将GH的前端框架从细节开始讲起可能真得写到猴年马月才能写完整了,于是还是按照老方法,以需求为导向,一点一点地接近框架的底层。
今天这篇就先划个水,讲讲怎么做右键菜单的内容,逐步向着底层前进吧。
右键菜单的本质
GH本质上是一个 Windows Form 框架下的窗体应用程序,其右键菜单的原型是一个ToolStripDropDown。之前有过 Windows Form 前端开发经验的话,对于GH的框架逻辑理解肯定可以更加地迅速。
不过这个ToolStripDropDown并非是独立与每一个GH_Component绑定的,而是统一绑定构造方法于GH画布,在右键点击的一瞬间为GH_Comoponent构造的。至于为什么要这么设计,它与GH的前端框架实现有关,总而言之,要实现鼠标右键点击我们自定义的GH_Component来弹出自己想要的菜单,还是得借助 override GH_Component 相关的函数才可以 —— 这些函数是负责构造ToolStripDropDown的,这相当于重写ToolStripDropDown的构造函数,于是就可以创作特定的菜单项。
制作右键菜单时,可以用到的重载项有下面几个:
AppendMenuItemsAppendAdditionalMenuItemsAppendAdditionalComponentMenuItems
其中,最短的那个是用来从头创建一个啥也没有的右键菜单;最长的那个是用来给GH_Component类派生的类的默认右键菜单添加额外菜单项目的(原始的内容仍保留,比如Bake、Preview之类的右键菜单项);不长不短的是给其他非GH_Component的子类的右键菜单添加菜单项时使用。
从头开始创建一个菜单项
如果不想要GH前端框架对于右键菜单的默认实现,第一步要做的就是override AppendMenuItems这个函数,并且删除这个默认实现:
return base.AppendMenuItems(menu);
然后可以把自己想要的菜单项构建进去 —— 创建一个MenuItem类,并添加到这个函数的传入的ToolStripDropDown中去。当然,此时这个传入的ToolStripDropDown还是一个新创建的没有任何内容的一个菜单。
添加如下代码
public override bool AppendMenuItems(ToolStripDropDown menu)
{
ToolStripMenuItem menuItem = new ToolStripMenuItem();
menuItem.Text = "Greetings, New Menu!";
menu.Items.Add(menuItem);
return true;
}
此时编译运行,这个电池就能弹出我们刚刚制作的菜单了:

但是此时点击它并没有什么反应。要实现点击能够执行某些功能,需要用到“Callback Function”,也就是“回调函数”的概念。
回调函数 (Callback Function)
回调函数牵扯到代理函数的概念,初理解起来挺难的,但是希望初学者可以反复琢磨下面的话,它们应该可以帮助从另一个角度和方面理解回调函数、代理、函数指针、事件等等等内容。
回调函数的执行与否并非由 写代码的人(我们) 来决定,而是在运行的时候由用户决定什么时候运行这个函数。
换而言之,回调函数在写好之后,我们不知道它什么时候运行,我们只管它怎么被执行。
从某种程度上来说,回调函数的调用与我们,也就是写代码的人,无关。
于是,“回调函数的执行者不是我们”。
回调函数由“代理者”执行。
回调函数是一个代理类型(delegate),为什么要有代理类型?
我们注意到,但凡是个函数,它必须得有输入/输出类型(尽管可能是void,但必须得有)。代理者最后仍然是要执行这个函数,也就是他必须负责提供输入和处理输出,于是问题就出现了:代理者不可能负责执行所有的函数,因为他没法处理所有的输入和输出。于是代理者想了个招:限定他能“帮忙执行”的函数的输入类型、输入参数个数、以及返回类型。
代理类型就是这三个定义的组合:(下面是一个代理类型定义的例子)
public delegate void MeowMeowMeow(int arg1, double arg2);
上述就是一个“代理类型”,它规定了这个函数必须要有:
- 两个输入,第一个输入是
int,第二个输入是double- 输出类型为
void,也就是没有返回值
这个类型本身叫做MeowMeowMeow
让我们再次强调:代理类型的作用是什么?
因为“作为一个代理者,我没法处理所有的输入和输出组合,我必须限定我能代为处理的函数类型”
ToolStripMenuItem中的回调函数类型
当我们刚刚新鲜出炉制作的ToolStripMenuItem —— 也就是“Greetings, New Menu”这个菜单项 —— 被用户点击的时候,这个ToolStripMenuItem对象实例会“帮我们执行”我们写好的函数。
那么问题来了,这个ToolStripMenuItem对象实例能帮我们执行啥样的函数呢?如果大家使用了一个有代码提示功能的编辑器(比如Visual Studio)的话,可以通过代码提示找到答案:

是EventHandler类型。
但是这EventHandler能接受几个参数,参数类型分别是什么,又是啥返回值?
在微软官方开发文档中可以找到答案:
https://docs.microsoft.com/en-us/dotnet/api/system.eventhandler?view=netframework-4.8
public delegate void EventHandler(object sender,

本文介绍了如何在Grasshopper(GH)中自定义组件的右键菜单,包括菜单的创建、回调函数的使用以及事件处理。通过重写AppendMenuItems函数,可以构建自己的菜单项,并利用EventHandler类型的回调函数实现点击事件。此外,还探讨了菜单项的启用条件和添加额外菜单项到默认菜单的方法,以及如何创建二级菜单。
最低0.47元/天 解锁文章





