如何处理*许多*上下文菜单
我正在用 C#(使用 Winforms)重写一个旧的 VB6 应用程序,该应用程序使用单个上下文菜单,其中包含多个项目,这些项目基于名为“InitControls”的整体函数更改其标题、可见性和已启用特征
该函数长 500 行主要由一个 switch 语句组成,该语句根据所选项目的标签决定启用哪些控件(有一个树视图和列表视图;它从活动项目中选择所选项目并获取其标签)。然后,它启用、禁用和修改可见项目的文本,并清除任何无用的分隔符。原始版本使用 ActiveBar(一种自定义控件),它允许在一个位置更改文本并同时在菜单、上下文菜单和工具栏中显示该项目。
我目前只是在 C# 中重新实现了 line for line 的逻辑行,但我讨厌它,因为我并没有真正修复任何东西,只是将问题放入一种新语言中(并且可能会在这个过程中搞砸)。我创建了一个类,它允许我在一个地方更改任何“订阅”菜单项的文本、启用和可见属性,甚至添加/删除所有订阅菜单项的事件处理程序。它有效,甚至看起来似乎是正确的,但我很确定一定有更好的方法。我的 MainForm 非常大。
处理复杂的上下文菜单和工具栏逻辑的标准 .NET 方法是什么?
I'm re-writing in C# (with Winforms) an old VB6 app that uses a single context menu with multiple Items that change their Caption, Visible, and Enabled traits based on a monolithic function called "InitControls"
The function is 500 lines long and consists primarily of a switch statement that decides what controls to enable based on the selected item's tag (there's a tree view and list view; it selects the selected item from the active one and gets its tag). It then enables, disables, and modifies the text of the visible items, and clears any useless Separators. The original uses ActiveBar (a custom control) which allows it to change the text in one place and display the item in menus, context menus, and toolbars all at once.
I'm currently just re-implementing the logic line for line in C#, but I hate it because I'm not really fixing anything, just putting the problem into a new language (and possibly screwing it up in the process). I created a class that allowed me to change the text, enabled and visible properties of any "subscribed" Menu Items in one place and even add/remove event handlers for all subscriBed menu items. It works, and even seems apparently correct, but I'm pretty sure there's got to be a better way. My MainForm is ENORMOUS.
What is the standard .NET way of handling complex Context Menu and Toolbar logic?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
据我了解,您基本上想要重构一个大型 switch-case 方法。谷歌搜索“switch case refactoring”应该会给你几个例子,你可以查看以找到最适合你的东西。
通常,当您重构
switch
case 时,这意味着您希望将每个case
块中的逻辑提取到一个新类中,该类可能是所有通用接口的实现案例。类的正确实现将取决于单个case
语句的条件:这称为 策略模式,因为每种情况需要不同的策略。在您的情况下,您需要稍微扩展该模式:上下文菜单有许多候选,每个候选者都能够处理特定的节点类型。在这种情况下,您的右键单击处理程序需要让它们决定它们是否可以为特定节点提供功能。
[编辑]
为了澄清一点,我将提供一个简单的示例。
我提到过,各个实现应该被提取到实现相同接口的类中,这些类应该负责根据当前条件更改菜单项的外观和状态。
我们的
IMenuStateManager
接口的第一个基本实现将只调用其他管理器的实现。这称为复合对象模式,因为它允许我们将一组对象视为单个对象对象:现在,您仍然有一个巨大的可能的菜单候选列表,但现在它们的逻辑被分离到不同的类中,然后包装在单个复合对象中。
我猜想当选择节点时菜单状态会更新,但这是您应该轻松适应您的应用程序的内容。该特定事件处理程序将全部责任委托给我们的复合菜单管理器:
由于您可能有三个菜单项同时执行相同的工作(主菜单、上下文菜单、工具栏),因此您可能希望每个
IMenuStateManager
实现同时更新所有三个。最简单的方法应该是传递一个ToolStripItem
对象数组,它是几个不同菜单元素的抽象基类:在创建
PrintMenuManager
实例时,您可以传递所有相关的按钮和菜单项:哇,最后结果是一篇很长的文章。 :)
好的,这就是开始。
From what I understand, you basically want to refactor a large switch-case method. Googling for "switch case refactoring" should give you several examples you can check out to find something that suits you best.
Usually, when you are refactoring a
switch
case, this means that you want to extract logic from eachcase
block into a new class, possibly an implementation of an interface common to all cases. The right implentation of your class will depend on the condition of an individualcase
statement: this is called a Strategy pattern, because each condition demands a different strategy.In your case, you need to slightly extend the pattern: you have a number of candidates for the context menu, each of them being able to handle a certain node type. In that case, your right-click handler needs to let them decide if they can provide functionality for a certain node.
[Edit]
To clarify a bit, I will provide a simple example.
I mentioned that individual implementations should be extracted into classes which implement the same interface, which should be responsible for changing menu items' appearance and state, based on the current condition.
Our first, basic implementation of the
IMenuStateManager
interface will do nothing more that simply call other managers' implementations. This is called a Composite object pattern, because it allows us to treat a group of objects as a single object:Now, you still have an enormous list of possible menu candidates, but now their logic is separated into different classes, and then wrapped in a single composite object.
I guess that menu states get updated when a node is selected, but this is something you should easily adapt to your app. That particular event handler delegates the whole responsibility to our composite menu manager:
Since you will probably have three menu items doing the same job at the same time (main menu, context menu, toolbar), you will probably want to make each
IMenuStateManager
implementation update all three of them at the same time. The simplest way should be to to pass an array ofToolStripItem
objects, which is the base abstract class for several different menu elements:When creating the
PrintMenuManager
instance, you can pass all buttons and menu items which are related:Whew, this turned out to be a lengthy one at the end. :)
Ok, that's about it for a start.