现在的位置: 首页 > 综合 > 正文

SCSF – Part 10 Commands in the CAB

2013年12月13日 ⁄ 综合 ⁄ 共 12876字 ⁄ 字号 评论关闭

Introduction

Part 9 of this series of articles discussed the Command design pattern. Commands in the CAB are a neat way of implementing this pattern. This article will examine them in some detail.

Commands and Events

As already discussed in part 9, commands in the CAB are closely related to events. In fact one of the ways that commands are intended to be used in the CAB is to hook up menus and toolbars in your application to the underlying code. This, of course, is something that is normally done in .NET using events. To make matters somewhat confusing, the CAB also has its own way of handling events. Part 11 of this series of articles will discuss CAB events, and how they relate to commands. In a later article I will discuss the Action Catalog (which is part of the SCSF code) and how that relates to both commands and events.

正如在part9讨论过的,CAB里的commands 和events 很接近,事实上在CAB里面,command也是用来和menus以及toolbars挂钩的。当然就是在.net里用events所做的事情,CAB拥有自己handle时间的方法,这让我们觉得困惑,Part11将会讨论CAB的events。以及他们是怎么和commands联系上的。在后面的文章,我将会讨论Action Catalog (which is part of the SCSF code) 是怎么和commands和events挂上的。

Basic Example

The easiest way to understand what we can do with CAB commands is to look at a simple example. The code for this is available.

This is a normal CAB application as described earlier in this series of articles. The main Shell Form has a standard ToolStrip with a button labelled ‘Call Command’ on it. When we click this button the application will display a message box saying ‘Hello World using CAB commands’.

看一个简单的sample 是最简单的办法帮助我们理解CAB里commands能做什么。这是一个我们之前讨论过的普通的CAB 应用程序。main Shell form里有一个标准的 Toolstrip。 当我们click这个button的时候, 将会展示一个消息框:‘Hello World using CAB commands’

Normally to do this in .NET we’d set up a standard event handler in the code behind the Shell Form, probably by just double-clicking the button in the designer, and then put our message box code in the handler. An example of this is also in the simple example code. We have a second button labelled ‘Call Event’. When it’s clicked it uses .NET events to display a message box saying ‘Hello World using .NET events’.

通常情况下,在.net里的做法是,我们建立一个标准的 event handler 在Shell Form, 很可能只是double-clicking 这个button.然后在handler里放上我们的message box的代码。这个example里有同样有这一示例。我们有第二个 button: ‘Call Event’,当我们按它的时候,它会使用.net的event 去显示这个message box. 说: ‘Hello World using .NET events’

To use CAB commands we assign a named command to the button’s click event. We do this by referring to a named command in a WorkItem’s Commands collection:

我们分配一个命名了的command到 button click 事件,我们通过指向一个命名了的command 在 WorkItem’s Commands collection:

Command helloCommand = RootWorkItem.Commands["HelloCommand"];

When the CAB sees this code it lazy-initializes the Command. That is, if a Command called ‘HelloCommand’ already exists in the collection it returns it, if it doesn’t exist it creates it and returns it.

当CAB看到这个代码的时候它 lazy-initializes 这个Command, 那就是 如果已经有一个叫  ‘HelloCommand’  Command 存在于这个collection里,它会将其返回,如果不存在,则创建一个新的,并且返回它。

(In the sample application the Command object actually gets created before this code, because the CAB scans for the CommandHandler attribute and creates commands based on that.)

在这个sample里, 这个Command object 实际上在这个代码被执行之前早就被创建好了,因为CAB 扫描了CommandHandler 属性,并且在它的基础上创建了command.

Next we obtain a reference to our button (a ToolStripItem), and assign an ‘Invoker’ to the Command object:

接下来,我们得到了一个我们ToolStripItem的引用, 并且assign 它到我们的Command object

ToolStripItem toolStripItem = this.Shell.mainToolStrip.Items["toolStripButton1"];              helloCommand.AddInvoker(toolStripItem, "Click");

In our Command design pattern discussion above we saw that an invoker is any piece of code that calls the Execute method on a Command object. Here we are saying that the toolStripItem’s Click event is an invoker for the helloCommand object. That is, when the event fires the Execute method on the Command will get called.

在我们的之前的关于Command design pattern 的讨论里,我们看见任何call  Command object上Execute方法的代码都被叫做invoker ,现在我要说的是 toolStripItem’s Click event  是 helloCommand object 的invoker 。那就是, 当这个event  被激发的时候,Command 上的Execute 方法会被调用。

 

In simple terms we are just hooking up the Click event to our Command object.

简单的说,我们只是将Click 事件挂在 Command object上。

Now we want to set up a receiver for the Command. Remember that a Receiver, if we have one, is code that actually does the work for the command. In the CAB we always have a Receiver: we never put the underlying functionality into the Command object. Note that this means the Command class itself is just plumbing code, and as developers we don’t need to change it.

现在,我们想建立为Command一个receiver ,记住,如果我们有一个Receiver,它事实上是为command服务的,在CAB里我们总是拥有一个Receiver, 我们永远不会把底层的代码放置在Command Object里,记住,这意味着Command class 它只是一个管道代码。作为一个开发者,我们没必要去修改它。

We do this by simply applying the CommandHandler attribute to a method with an appropriate signature as below:

我们通过简单的放置一个CommandHandler  attribute 到一个有着合适签名的方法上。

[CommandHandler("HelloCommand")]          
public void HelloHandler(object sender, EventArgs e)          
{              
     MessageBox.Show("Hello world using CAB commands");          
}

For this to work the object the code is in must be in a WorkItem collection of some kind. Of course, this is true of most of the CAB functionality as we’ve seen before. Here we really are using WorkItems as Inversion of Control containers. We set up the code as above and the framework calls us back as appropriate.

为了使这个起到作用,这个对象这个代码必须放置到一个WorkItem collection 里, 当然,这个我们之前看到的其他的CAB 的功能一样。现在我们正使用WorkItems作为反转控制的容器,我们设置的以上代码,framework会去适当的调用它。

In the example code the command handler is in the code behind the Shell Form, which as we’ve seen before is in the WorkItem’s Items collection.

That’s all there is to it. Now when we click our toolstrip button the handler gets called and ‘Hello world’ gets displayed.

Points to Note

  1. The command handler’s method signature has to be as shown. It has to be public, and it has to have object and EventArgs parameters. I’ll discuss the parameters further below.
  2. There’s no ICommand interface in the CAB’s implementation. The interface to a Command object is just the default public interface on the Command class.
  3. As we’d expect, the Command object also has a RemoveInvoker method that lets us detach a command from its invoker.

 1. handler 方法的签名必须如上,它必须是公开的,它必须拥有object 和EventArgs  参数 我将在后面讨论parameters。

2.  这里并没有ICommand 借口, 一个Command object 的接口是Command class里默认的public interface 。

3. 正如我们期待的那样,一个Command object 也拥有一个RemoveInvoker 方法,让我们从 invoker detach  command 。

 

Why Are We Doing This?

An obvious question at this point is ‘why we would want to do this?’ After all, we already have a perfectly good way of handling menu and toolstrip click events in .NET. Using a Gang of Four design pattern is nice, but is it giving us any real advantages?

一个明显的问题:“为什么我们要这样做?"毕竟,我们早就拥有了一个完美的方法去handle menu和toolstrip. 但是它给了我们任何好处了吗?

We discussed one advantage of the Command pattern approach above. We can easily swap one command for another. This is particularly useful, for example, if we set up a standard toolbar that is going to be used with slightly different functionality for multiple screens in an application.

我们讨论了Command pattern 的一个优势, 我们可以轻易地转换一个Command为另一个,这特别有用,例如,我们为application里不同screens设置了一个标准的toolbar 。

Another advantage is that our command handlers don’t have to be in the same class as the toolbar or menu they are supporting. This can make the code a lot cleaner. For example, suppose you have a Help/About menu item that shows a dialog, and you want the same functionality on all your menus. With .NET events you’d have to mess around setting up all the handlers to work correctly, probably with some singleton class to actually accept the calls. With the CAB command approach you can just set up a class with command handlers in it, add it to a WorkItem collection (Items) and then just call AddInvoker for every menu you want hooked up.

另一个好处是,我们的command handlers 没必要非在一个toolbar 或者menu supporting的类里  ╮(╯▽╰)╭

In fact the Command pattern lets us write incredibly simple and powerful menu systems for enterprise applications. We can do this in a way that is difficult to do with the standard event approach. I will write specifically about this at a later date, as there seems to be a lot of confusion about how you’re meant to set up menus using the CAB/SCSF.

事实上,Command pattern 允许我们自己写难以置信简单而又给力的菜单系统。╮(╯▽╰)╭

 

Command Status

One other useful thing that you can do with CAB commands is to enable, disable or hide the ToolStrip buttons or menu items associated with a command simply by setting the status property on the associated command (the one that will be invoked when you click the button).

另一件有用的事是 通过设置 status 属性来 enable, disable or hide于command有关的 the ToolStrip buttons or menu items .

An example of this is available. This has two buttons on a ToolStrip on its Shell Form. If you click the ‘Enable/Disable CAB Command’ button the command handler below will run:

[CommandHandler("EnableDisableHelloCommand")]
public void EnableDisableHelloCommandHandler(object sender, EventArgs e)
{
    Command helloCommand = _workItem.Commands["HelloCommand"];
    if (helloCommand.Status == CommandStatus.Enabled)
        helloCommand.Status = CommandStatus.Disabled;
    // Change this to the line below if you want to hide the cabCommandToolStripButton
    //helloCommand.Status = CommandStatus.Unavailable;
    else
        helloCommand.Status = CommandStatus.Enabled;
}

As you can see this gets a reference to the helloCommand command in the Commands collection. This is the command associated with another button on the ToolStrip. The handler just flips the Status associated with the command from CommandStatus.Enabled to CommandStatus.Disabled or vice-versa. If you run this code you will see that this disables or re-enables the associated button.

 

 

 

Command Handler Parameters

Note that our command handler has the usual .NET event parameters. These are sender (an object) and e (EventArgs). However, if you put a breakpoint in the command handler you’ll see that sender is the Command object, and e is set to EventArgs.Empty. It isn’t possible to pass other values to these parameters if you are using commands.

注意,我们的Command handler 拥有普通的.Net 事件的参数, 它们是sender 和e,然而,如果你在command handler 放置一个断点,你将会见到sender 就是Command 对象,e被设置成了EventArgs.Empty, 如果你用commands的话,传递其他参数是不行的。

In the discussion on the Command design pattern above we saw that we don’t pass any parameters to our Execute method. This is true of the Execute method in the CAB framework as well. However, behind the scenes the CAB uses normal .NET events to implement the Command pattern, and this allows it to pass more normal parameters to the command handler.

我们在以上Command design pattern 的讨论中我们知道Execute函数没有任何参数,在CAB framework里 的确是这样,

Note also that this is not significantly different from the .NET events we would normally use with a ToolStripItem. If we set up a normal event handler this has the same parameters, but again the EventArgs parameter is always set to EventArgs.Empty, and the object parameter is set to the ToolStripItem. Again, there’s no easy way of passing other values to these parameters.

注意 这不是值得注意的与 .NET events 的不同,

 

When you are using .NET events in this way it can be useful to have access to the ToolStripItem that raised the event, which you can do via the object parameter. This is more difficult with commands. We can access a list of invokers for the command, and get the associated ToolStripItems from the list (although even this is difficult). However, we don’t necessarily know which of the invokers called the command handler.

当你在使用.net的event时候, 能够访问到激发事件的ToolStripItem  是有用的,你可以通过 object参数来访问它, 但是在commands里就很困难, 我们可以得到一个激发command的invokers, 然后访问到ToolStripitems。(虽然这也是很难的)。然而,我们没必要知道哪个invokers 调用了 command handler。

 

Where’s the Execute Method?

In the examples here we haven’t seen any reference to an Execute method, in spite of this seeming to be a key part of the Command design pattern as described above.

在这个例子里,我们没有见到Execute 方法, 尽管他是the Command design pattern 的一个重要部分。

Rest assured that behind the scenes in the CAB code the Command object DOES have an Execute method that gets called by the invoker. However, all we needed to do in our example was to set up the invoker with some simple syntax to get it to call the Execute method. We didn’t need to call it ourselves.

我们确信在CAB背后的代码里,Command object 确实含有一个Execute方法被invoker调用,然而,所有我们需要在我们的example里做的是建立一个invoker,我们不需要自己去调用它。

We can call the Execute method on our Command object directly from code, as this example shows. This lets us use commands in other ways than the standard invoker example seen above.

我们可以直接call 这个Command object的Execute 方法, 如示例, 这允许我们通过其他方法使用commands。

Which Events can we use as Invokers?

We’ve seen in our example that a ToolStrip button ‘Click’ event can be an invoker for a command. As mentioned above, the key use of commands is intended to be for menu systems, where they are very powerful.

我们已经看见 ToolStrip button 的click 事件 可以作为command的invoker, 正如以上提到的, commands 的主要用途是为了 menu systems。它们很给力。

However, we can only use the syntax in the examples above for standard .NET events on ToolStripItems and anything that derives from the Control class. If we want to hook anything else up as an invoker we need to do a little more work. Behind the scenes the CAB is using something called a CommandAdapter to hook up these events as invokers to our commands. The only CommandAdapters that are registered by default are those for ToolStripItem and Control.

然而, 我们只能使用以上例子中的语法,作为标准的ToolStripItems 的.NET events 。以及任何继承与Control 的cllass.如果我们想挂起任何其他的东西作为invoker, 我们需要做更多的工作,在CAB内部,它使用一个叫做CommandAdapter 的东西区将这些事件作为invokers挂在我们的commands上。 CommandAdapters 唯一默认注册的 是ToolStripItem 和Control。 

 

We can use standard .NET events on one of our own classes as an invoker. However, to do this we need to create a CommandAdapter and tell the CommandAdapterMapService that it relates to our own class. Fortunately there is a generic EventCommandAdapter<> class that we can use (with our class type as the generic), rather than having to write our own CommandAdapter class.

 我们可以使用标准的.Net的时间作为我们一个class作为invoker,然而,我们需要创建一个CommandAdapter ,并且告诉CommandAdapterMapService  它和我们自己的类有关联,幸运的是我们有一个泛型的EventCommandAdapter<> 类给我们可以使用。

A full code example of how to do this is available.

We would have to write our own CommandAdapter class if we wanted to use something other than a .NET event as an invoker (and still wanted to use the CommandHandler etc pattern). To do this we inherit the abstract base class CommandAdapter and override its abstact methods (which, predictably, include AddInvoker and RemoveInvoker).

Conclusion

CAB commands provide a powerful way of setting up flexible menus. However, using the Command pattern in a more general way can be a little confusing. Writing your own CommandAdapter class, or indeed using your own events as invokers as illustrated above, isn’t necessarily straightforward. Also, as we shall see in the part 11, in fact CAB events are probably better suited for this sort of thing. It may be better to think of CAB commands as primarily something you use to get powerful menu systems, and to move on.

抱歉!评论已关闭.