Intelligent CommandHandler

Topics: CAB & Smart Client Software Factory
Jan 14, 2006 at 5:34 AM
originally posted by: msb63

Please consider adding the following method to the Command object in future releases:

public void Execute (EventArgs e);

This makes it possible to write intelligent CommandHandlers, that are able to take extra arguments, without doing any other overriding in the CAB or application, or supplying loads of Invokers. Here's an example:

workItem.Commands"OpenReport".Execute (new MyEventArgs ("file://testreport"));

CommandHandler ("OpenReport")
public void OpenReport (object sender, EventArgs e) {
if (e is MyEventArgs)
DoOpenReport (((MyEventArgs)e).ReportName);
else
DoAskOpenReport();
}
Jan 14, 2006 at 7:47 AM
originally posted by: BradWilsonMSFT

Commands explicit discard event data for architectural reasons. A single command may be attached to any number of event sources, so writing your command handler to expect a specific type of event data is asking for failure. If you have a one-to-one mapping between command and event firing object, and want that event firing data, you should not use commands (directly subscribe instead).
Jan 14, 2006 at 9:39 AM
originally posted by: msb63

Yes, you are right I could call AddInvoker for every Command with the CommandHandlerAttribute that gets registered for me, but I'd have to build a service for the event source objects just to execute the commands.

The default event source in Command object is perfectly suited to fire its events with different EventArgs and you'll recall it already has a public Execute method. All you need is another Execute which take EventArg as an argument.

Good programming practice involves creating an overridable method to fire the event, which is what was implemented in CAB. CAB's implementation of "OnExecuteAction" takes not only the EventArg but also a sender object as an argument. (btw. the sender object never gets passed to the handler, bug!). Unfortunately there's no way to get the Builder to create my own Command objects, because the relevant functions are private.

What I'm trying to say is, that its normal for event sources to pass EventArgs and most command handlers can deal with more than one EventArg type. As you said yourself "a single command can be attached to different event sources", so why is that asking for failure if the same command fires different EventArgs?
Jan 14, 2006 at 10:38 AM
originally posted by: BradWilsonMSFT

I think you're missing the point I'm trying to make.

The fact that commands can use the event infrastructure is an implementation detail. Don't think of them as events with rich data. Think of them as a simple flag that something happened; you don't know any more data than that.

If you want rich eventing semantics, use .NET events or the Event Broker.
Jan 14, 2006 at 3:03 PM
originally posted by: msb63

What you are saying is, that a command handler can be triggered by a menu or button click and that's it.

I find it a pity, because CAB has such a fantastic infrastructure for commands, dynamic loading of commands via modules, etc. Its a waist not to make use of it. I'm not talking about a rich eventing semantics. It's just commands with a string argument. How many Urls do you know, that don't have a query string? That's a command with an argument/parameters. I'm suggesting to add one function (of course I've already done it) to open up a huge potential. I agree with you it's an implementation detail, but an insufficient implementation can spoil a lot.

To me its just a pity, because I find the architecture in CAB really cool. You guys have made a great job and I'm not just saying that. I've been programming for over 24 years.
Jan 16, 2006 at 9:45 AM
originally posted by: BradWilsonMSFT

I didn't say that at all.

What I said was: we don't pass through the event args, because you have no idea what thing caused the command to get fired. The things that cause a command to get fired may have wildly different event args. The point of a command is to isolate you from the many things (and give you the many relationship).

You can already get what you want from EventBroker. I don't understand why you want us to recreate EventBroker a second time. :)
Jan 17, 2006 at 12:29 AM
originally posted by: msb63

I haven't looked at the EventBroker in detail, but I'll do that. Maybe that's what I'm supposed to be using.

Please allow me one more question. Why do you say "you have no idea what thing caused the command to get fired"? By definition all command (event) handlers in Windows have the sender object as the first parameter. Or are you just refering to CAB's architecture, where the command object fires the event, but it is originally caused by a different object, namely a ToolStripMenuItem or tool button? In that case Command doesn't know or care about thing and cannot tell the handler either.
Jan 17, 2006 at 6:51 AM
originally posted by: BradWilsonMSFT

The purpose of commands is to bridge multiple different types of .NET events (presumably from UI controls, but not necessarily), firing a single command event. None of the original event details are preserved, because the command's job is to hide them. A single command could be wired to half a dozen events with different signatures. When you receive the command, you don't know -- nor should you care -- which of those original events ended up firing the command.

EventBroker, on the other hand, requires all publishers and all subscribers to have the same event signature. It does preserve the full fidelity event information.
Jan 17, 2006 at 11:08 AM
originally posted by: msb63

Thanks for the explanation, I understand the thoughts behind the architecture.
Feb 9, 2006 at 4:29 PM
originally posted by: AndrePiwoni

Brad,

I perfectly understand architectural choice to discard any data about events for commands. I said choice because I was hoping to be able to use custom CommandAdapter to convert UI events to application domain events.

For example by creating custom CommandAdapter and overriding InvokerEventHandler() in EventHandlerAdapter:

public event CustomEventHandler ExecuteCommand;

public void InvokerEventHandler(object sender, NavBarLinkEventArgs e)
{
CustomEventArgs customEventArgs = new CustomEventArgs(e.Tag); // as an example
ExecuteCommand(this,customEventArgs);
}

I'm sure there's more to it.

In Eclipse, for example, Command.Execute() takes an ExecutionEvent as a parameter that may contain application context and information about selection etc.

If CommandHandler doesn't have access to this information, commands may have very limited use.

Andre
Feb 10, 2006 at 5:42 AM
originally posted by: msb63

In the meantime I know CAB quite well and I still think it is wrong not to pass the EventHandle to the event handler.

Andre, what you can do is override "CreateCommand" in the root workitem and return a customized command that implements an Execute with Event which calls OnExecuteAction. Unlike Command.Execute, Command.OnExecuteAction correctly passes the EventHandle to the handler.
Here's the code:

protected override Command CreateCommand (Type t, string name)
{
return new ParmCommand();
}

public class ParmCommand : Command
{
public void Execute (CommandEventArgs e)
{
OnExecuteAction (this, e);
}
}

Markus
Feb 10, 2006 at 11:16 AM
originally posted by: AndrePiwoni

Markus,

Thank you for your response!

Overriding CreateCommand would work in the scenario that you have mentioned:

workItem.Commands"OpenReport".Execute (new MyEventArgs ("file://testreport"));

CommandHandler ("OpenReport")
public void OpenReport (object sender, EventArgs e) {
if (e is MyEventArgs)
DoOpenReport (((MyEventArgs)e).ReportName);
else
DoAskOpenReport();
}

I could live with casting EventArgs :-), but it would be good to have something like ExecutionEvent in Eclipse. Otherwise, someone else may do the following:

workItem.Commands"OpenReport".Execute (new IncompatibleEventArgs ("file://testreport"));

In my scenario, I though about using workItem.Commands"OpenReport".AddInvoker(new NavBarItem(),"LinkClicked") and letting CAB framework to do invokation of handler. Since there is no adapter capable of handling NavBarItem I had to create my own where I tried to convert UI event to application level event. I could have used EventCommandAdapter<NavBarItem> but I wanted to convert event.

Something like this:


public class NavBarItemCommandAdapter : EventCommandAdapter<NavBarItem>
{
public NavBarItemCommandAdapter()
: base()
{}

public NavBarItemCommandAdapter(NavBarItem item, string eventName)
: base(item, eventName)
{
}

public void InvokerEventHandler(object sender, EventArgs e)
{
if(e is NavBarLinkEventArgs)
{
ExecutionEvent evt = e.Tag // as an example convert UI event to applicaton event
FireCommand(evt); // FireCommand added to CommandAdapter
}
}
}

There more to it.

Andre
Feb 14, 2006 at 4:26 AM
originally posted by: msb63

Why do you want to convert the event before you fire it, and not when you use the event (ie. in the CommandHandler)?

I've also registered my own Invoker, but to fire the event I can't use InvokeEventHandler, I have to ask CAB to give me the Command object by name(my derived command) and call Execute. The reason is like you noticed EventCommandAdapter.InvokeEventHandler just drops EventArgs when calling FireCommand. Just like Command.Execute (my original request) CommandAdapter.FireCommand should take an EventArg parameter and pass it on when firing the event.

Both your and my scenario would be covered if the EventArgs where just passed on correctly, but the CAB team only have menu/button commands in mind, which don't require EventArg.
Feb 14, 2006 at 8:49 AM
originally posted by: AndrePiwoni

Markus,

Like Brad said,
"A single command may be attached to any number of event sources, so writing your command handler to expect a specific type of event data is asking for failure."

From your original post:

CommandHandler ("OpenReport")
public void OpenReport (object sender, EventArgs e) {
if (e is MyEventArgs)
DoOpenReport (((MyEventArgs)e).ReportName);
else
DoAskOpenReport();
}

If you wire OpenReport command to MenuItemEvent and NavBarItemEvent and other UI control later on, you have to modify your handler. Loose coupling is lost! Best way that I could come up to convert UI event to application event was adapter since it is more reusable than a handler.

Andre
Feb 14, 2006 at 11:18 AM
originally posted by: msb63

Andre, thank for you feedback.

I find it stange that both you and Brad think my code will break with other EventArgs. My handler simply calls a different function if a particular well-known EventArgs is passed to it. In all other cases the EventArg is not used at all.

So contrary to what you say my handler does get called by MenuItemEvent (ToolStripItem) and Toolbar, etc. When the user clicks Menu -> OpenReport he will be asked which report he wants. Additionally I have a CommandAdapter which is used by a customizable embedded HTML page. You can specify links (href) which trigger the OpenReport command and supply the report name, using standard URI syntax. The HTML page is more or less the control center to the whole application. You can trigger several registered commands (with or without arguments). CAB does all the book-keeping for me: loads the available Addins, registers their commands, handles the disposal, etc. Pretty cool I think.
Feb 14, 2006 at 2:52 PM
originally posted by: BradWilsonMSFT

I don't believe I said anything about anything being broken.

I will merely re-state what I said earlier. The architectural purpose of Commands was to isolate you from a variety of UI event sources, so that many event sources with a variety of event args could fire a single handler (who does not receive the event args). It's a very low level sort of isolation from UI elements on the screen.

What you've describe is, again in my opinion, better suited for EventBroker.
Feb 14, 2006 at 3:29 PM
originally posted by: msb63

The fact that "many event sources with a variety of event args could fire a single handler" does not justify throwing away EventArgs. ALL events have a common base, that's what you should be passing on. Anyway, I guess we don't agree on that.

Brad, I'm curious to understand how you would solve my problem using EventBroker. In my opinion EventBroker is exactly the opposite to command handler. In the EventBroker scenario the "thing" that can fire events publishes an event, and the "thing" that handles the event needs to subscribe to the event. There may only be one publisher, but many subscribers.

In my case command handlers would need to subscribe to several events, published from all possible event triggers. I can't see how that would work.

Markus
Feb 15, 2006 at 8:15 AM
originally posted by: BradWilsonMSFT

EventBroker allows multiple publishers and multiple subscribers. It supports 1:1, 1:many, many:1, and many:many.
Jul 12, 2007 at 11:31 AM
Yes, well not wishing to get involved in the tussle, just now I was in the situation where I need to change my Command event into an Event Publication - because I need to pass arguments. Fair enough, but I did a quick search here to make sure I was right.
The only issue I have, is that I frequently (annoyingly) start off with commands, then discover I need to pass arguments and have to convert to events. If I was using SCSF, maybe I'd have some recipe for the conversion, but at the moment it's just very annoying. I'm inclined to only use events to prevent the inconvenience. Which is where we come back to this argument I suppose. If commands are so frequently inconvenient and the alternative always works, then why would I bother with commands.
Jul 12, 2007 at 3:36 PM

PandaWood wrote:
Which is where we come back to this argument I suppose. If commands are so frequently inconvenient and the alternative always works, then why would I bother with commands.


Because Commands are just triggers for menustrip/toolstrip actions.

I think (could be wrong here) part of the problem is that people use Commands for all UI events, meaning events triggered from a View (such as a Button on a view). Then they realize they need to pass data when that event occurs, and they switch to an EventBroker event when that is what they should have been doing all along. Maybe part of this problem also occurs because people are trying to bypass the Presenter and just tie View events directly to a Command? I dunno...

In our shop, we only use CommandHandlers for MenuItems and ToolStripItems. Everything else we do (that is not a direct method call from view-presenter) is an EventBroker event. These rules make it incredibly easy for us to operate as a development team; we are never confused about what form of communication to use when we're building workItems, views, presenters, or menuitem/toolstripitems.

May 3, 2008 at 11:31 PM
Edited May 3, 2008 at 11:31 PM
Long time no post! ;0)

I just read this thread as I have a similar issue. As I thought about it more, I realized I have a bigger question of how to accomplish what I want.

I have an application that is very similar to Outlook, with an inbox and detail view of items in the inbox. I need to support the deletion of items from the InboxView (via Context menu and key events) as well as from my DocumentView (displays an item from the Inbox).

When a user open an item from the Inbox I create a new WorkItem for that document and extend the shell menu with, among many things a delete menu item.

Here is what needs to happen in each "deletion scenario":
From the Inbox:
  • Get confirmation from the user (Yes/No)
  • Call Service method to remove the deleted item from the cached collection (which in turn removes it from the Inbox DataGridView)

From the DocumentView
  • Get confirmation from the user (Yes/No)
  • Terminate the WorkItem
  • Call Service method to remove the deleted item from the cached collection (which in turn removes it from the Inbox DataGridView)

Currently for the InboxView solution my context menu event calls a InboxViewPresenter event which gets the confirmation (via MessageBoxService) and then calls the Service method to delete the item.

This works fine.

Now that I'm trying to add the DocumentView solution I realize that I have a bit of a challenge to share the code and logic between the two contexts. I'm not sure how to accomplish what I need.

From the DocumentView I have a CommandHandler that handles the menu item events. The CommandHandler is on the DocumentViewWorkItem and has a reference to the document it represents. I was thinking I could then raise an EventBroker event "DeleteDocument".

I could change my InboxViewPresenter implementation to raise this same event rather than call the service method directly.

In both scenarios I can raise the event with the Document to be deleted in the EventArgs. Great, I'm done... right?

No, I have an issue of catching the event and the order that it can happen. I will need two subscribers, one in the InboxViewPresenter and another in the DocumentViewPresenter. The reason I need the latter is that I need to terminate the DocumentViewWorkItem BEFORE the deletion happens, otherwise I will run into other problems.

The challenge I'm facing is there is no guarantee which event subscriber will be invoked first, so I run the risk of the InboxViewPresenter handling the event and deleting the item, THEN the DocumentViewPresenter handling it. This causes two problems,;
  1. I'm not sure what will happen if I terminate my DocumentViewWorkItem AFTER the Document it's modeling has been deleted
  2. Based on my described design (which is flawed) I will be deleting the document twice (Inbox, View)

So I'm confused! :0|

How would you all handle a scenario like this? My goals are to reuse as much of the code between the two contexts as possible.

Any input very welcome and appreciated,
Steve
May 5, 2008 at 10:36 PM
Edited May 5, 2008 at 10:37 PM

sklett wrote:
How would you all handle a scenario like this? My goals are to reuse as much of the code between the two contexts as possible.

Any input very welcome and appreciated,
Steve


I would publish an event in the InboxViewPresenter (at the appropriate point in logic) that is subscribed to by the DocumentViewPresenter.