【Grasshopper基础9】添加右键菜单

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

其实经过【基础2】~【基础6】、以及【基础8】的内容,几乎所有插件的后台数据处理流程都可以实现了(往往也是最关键的业务核心内容):

  • RegisterInputParamRegisterOutputParam中注册数据的入口和出口
  • SolveInstance中处理及传递数据
  • ReadWrite中进行数据的序列化与反序列化

这也是为什么很多时候,目前大部分的GH教程也是大致到这个节点步骤就结束了。因为依靠这些内容,一套完整的业务逻辑可以完成,无非就是电池样子长得平平无奇一点嘛。

不过,很多时候我们也不仅仅满足于“实现流程”,还要 实现花里胡哨的功能 增强用户体验,此时就需要在前端交互这个部分内容上下很大的功夫了。

既然目前【基础】系列的后端数据流程已经基本收尾完成。接下来所有的【基础】类教程大部分时间都会聚焦于如何处理和应对 GH特有的前端逻辑框架 —— 将自己想实现的功能在这个框架下搭建完成。

前段时间实在是太忙了,断更了好几个礼拜,接下来的一段时间工作上仍然会有很多事情,要详细地将GH的前端框架从细节开始讲起可能真得写到猴年马月才能写完整了,于是还是按照老方法,以需求为导向,一点一点地接近框架的底层。

今天这篇就先划个水,讲讲怎么做右键菜单的内容,逐步向着底层前进吧。

右键菜单的本质

GH本质上是一个 Windows Form 框架下的窗体应用程序,其右键菜单的原型是一个ToolStripDropDown。之前有过 Windows Form 前端开发经验的话,对于GH的框架逻辑理解肯定可以更加地迅速。

不过这个ToolStripDropDown并非是独立与每一个GH_Component绑定的,而是统一绑定构造方法于GH画布,在右键点击的一瞬间为GH_Comoponent构造的。至于为什么要这么设计,它与GH的前端框架实现有关,总而言之,要实现鼠标右键点击我们自定义的GH_Component来弹出自己想要的菜单,还是得借助 override GH_Component 相关的函数才可以 —— 这些函数是负责构造ToolStripDropDown的,这相当于重写ToolStripDropDown的构造函数,于是就可以创作特定的菜单项。

制作右键菜单时,可以用到的重载项有下面几个:

  • AppendMenuItems
  • AppendAdditionalMenuItems
  • AppendAdditionalComponentMenuItems

其中,最短的那个是用来从头创建一个啥也没有的右键菜单;最长的那个是用来给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;
}

此时编译运行,这个电池就能弹出我们刚刚制作的菜单了:

WeChat Screenshot_20210331153624

但是此时点击它并没有什么反应。要实现点击能够执行某些功能,需要用到“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)的话,可以通过代码提示找到答案:

WeChat Screenshot_20210331160049

EventHandler类型。

但是这EventHandler能接受几个参数,参数类型分别是什么,又是啥返回值?

在微软官方开发文档中可以找到答案:
https://docs.microsoft.com/en-us/dotnet/api/system.eventhandler?view=netframework-4.8

public delegate void EventHandler(object sender, 
评论 21
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值