[Action] with parameters

Topics: CAB & Smart Client Software Factory
May 3, 2007 at 3:12 AM
I've been using the action catalog for awhile now and it's worked very well. Lately I've found that I have actions where I would really like to pass parameters in rather that use the State of Services.

I started to modify the source to support a boxed object[] for parameters, but then I realized I needed to modify builder strategies and other CAB stuff that I wasn't ready for. I slowly backed away, commenting out my changes ;0)

I just wanted to see if anyone else has extended the Action system to support parameters and if so, how you did it?
To the CAB/SCSF guys: Any plans to add such functionality to the Action system?

Something like:
void Execute(string action, WorkItem context, object caller, object target, object[] args);
May 3, 2007 at 4:09 AM
Actually, this wasn't that hard at all :0)

I think I might comment out the old, non-parameterized action code and overload the required methods to handle actions without parameters. My current implementation isn't ideal in that it maintains two action registries; parameterized and non

Here are the code changes I made:
IActionCatalogService.cs
public delegate void ParameterizedActionDelegate(object caller, object target, object[] args);
 
void Execute(string action, WorkItem context, object caller, object target, object[] args);
void RegisterActionImplementation(string action, ParameterizedActionDelegate actionDelegate);

ActionCatalogService.cs
private Dictionary<string, ParameterizedActionDelegate> _actionWithArgsImplementations = 
new Dictionary<string, ParameterizedActionDelegate>();
 
public bool CanExecute(string action, WorkItem context, object caller, object target, object[] args)
{
    Guard.ArgumentNotNullOrEmptyString(action, "action");
 
    bool result = true;
    List<IActionCondition> conditions = BuildActionConditionPipeline(action);
    conditions.ForEach(delegate(IActionCondition condition)
    {
        result &= condition.CanExecute(action, context, caller, target);
    });
    return result;
}
 
public void RegisterActionImplementation(string action, ParameterizedActionDelegate actionDelegate)
{
    Guard.ArgumentNotNullOrEmptyString(action, "action");
    Guard.ArgumentNotNull(actionDelegate, "actionDelegate");
 
    _actionWithArgsImplementations[action] = actionDelegate;
}
 
public void RemoveActionImplementation(string action)
{
    Guard.ArgumentNotNullOrEmptyString(action, "action");
 
    //  There are two registries to check
    if (_actionImplementations.ContainsKey(action))
    {
        _actionImplementations.Remove(action);
    }
    else
    {
        _actionWithArgsImplementations.Remove(action);
    }
}
 
public void Execute(string action, WorkItem context, object caller, object target, object[] args)
{
    Guard.ArgumentNotNullOrEmptyString(action, "action");
 
    ParameterizedActionDelegate actionDelegate = null;
    if (CanExecute(action, context, caller, target, args))
    {
        if (_actionWithArgsImplementations.TryGetValue(action, out actionDelegate))
        {
            actionDelegate(caller, target, args);
        }
    }
}

Here is an example of extending a top level menu with an action. I know there are other ways to do this, but it's a good example.
private void ExtendMenu()
{
    ToolStripMenuItem topItem = new ToolStripMenuItem("Shipping and Receiving");
    WorkItem.UIExtensionSites[UIExtensionSiteNames.MainMenu].Add(topItem);
 
    //  Add the command to show the list of Sales Orders
    ActionCatalogService.Execute(
        ActionNames.ShowDisplaySalesOrdersViewCommand, WorkItem, this, null, new object[] { topItem });
 
    //  If non of the Actions added any items, remove the top level node
    if(topItem.DropDownItems.Count == 0)
    {
        WorkItem.UIExtensionSites[UIExtensionSiteNames.MainMenu].Remove(topItem);
    }
}

And here is the Action
[Action(ActionNames.ShowDisplaySalesOrdersViewCommand)]
public void ShowCalibrateHardwareCommand(object caller, object target, object[] parameters)
{
    //  Get the top menu item from the args
    Guard.ReferenceNotNull(parameters[0], "This method requires the top menu item argument");
    ToolStripMenuItem topItem = parameters[0] as ToolStripMenuItem;
 
    ToolStripMenuItem child = new ToolStripMenuItem("Show Sales Orders");
    topItem.DropDownItems.Add(child);
    WorkItem.Commands[CommandNames.ShowSalesOrdersCommand].AddInvoker(child, "Click");
}

Let me know what you think, if you see any ways it could be improved, etc, etc.
I hope some of you find this useful..

-Steve