Commands In The CAB (Introduction To CAB/SCSF Part 10)

来源:互联网 发布:公司网络监控手机qq 编辑:程序博客网 时间:2024/05/20 06:30

Commands In The CAB (Introduction To CAB/SCSF Part 10)

Filed under: .net, beginners guide, c#, CAB, command, Composite Application Block, dotnet, SCSF, Smart Client Software Factory, technology — richnewman @ 10:43 pm

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.

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’.

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’.

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 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.

(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.)

Next we obtain a reference to our button (a ToolStripItem), and assign an ‘Invoker’ to the 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.

In simple terms we are just hooking up the Click event to our 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 alwayshave 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.

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

        [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.

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.

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?

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.

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.

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 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).

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.

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.

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.

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.

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.

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.

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.

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.

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.

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.

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.


0 0
原创粉丝点击