My thoughts on RoutedUICommand and how to implement my CustomUICommand



By ganton ~ August 7th, 2008. Filed under: WPF.

Last few days I work on an application of my own. It is a simple application which I’ve started few months ago when Sql CE 3.5 was released. I didn’t find any sql editor at that time. The initial version was enough for me to do some sql statements, execute them and browse the result. Few days ago I remembered it and I’ve started to refactor it and also to add new functionality on it.

The first what I’ve done in my project was to try to use RoutedUICommand in order to be able to have a unique code to be used in different menus and also to have an option to provide an use of shortcut keys to the end user (currently it is only me). I walked trough several samples and build my initial version of my CustomUICmmand abstract class. It should be inherited from my specific commands. Here is it.


public abstract class CustomUICommand
{
public abstract RoutedUICommand Command { get; }
public abstract List<object> Parameters { get; }
public CustomUICommand()
{
}

public abstract void OnCanExecute(object sender, CanExecuteRoutedEventArgs args);
public abstract void OnExecute(object sender, ExecutedRoutedEventArgs args);
}

It is clear that each concrete command should implement the abstract methods. The think that I do not like here but I was used it several times in other projects with Command pattern is Parameters property. Since first time I’ve tried to use Command pattern I always run into how to provide several parameters to my command class. I’ve searched Internet but I didn’t found something that I like. But yesterday I came up with an idea to change the CustomUICommand this way


public abstract class CustomUICommand
{
public abstract RoutedUICommand Command { get; }

public CustomUICommand()
{
}

public abstract void OnCanExecute(object sender, CanExecuteRoutedEventArgs args);
public abstract void OnExecute(object sender, ExecutedRoutedEventArgs args);
public virtual void AddParameter<T>(T parameter) { }
}

The only difference is that now I have a template method AddParameter instead of list of objects. A concrete class implementation can be like


public class RunCommand : CustomUICommand
{
private readonly RoutedUICommand _command =
new RoutedUICommand("Run Query", "RunQueryCommand", typeof(RunCommand), new InputGestureCollection()
{
new KeyGesture(Key.F5)
});

private DbScheme _dbScheme;
private TabControl _resultTabControl;
private FlowDocument _flowDocument;

public override RoutedUICommand Command
{
get { return _command; }
}

public override void OnCanExecute(object sender, CanExecuteRoutedEventArgs args)
{
if (_dbScheme == null || _resultTabControl == null || _flowDocument == null)
{
args.CanExecute = false;
return;
}

TextRange content = new TextRange(_flowDocument.ContentStart, _flowDocument.ContentEnd);
if (content.Text.Trim().Length == 0)
{
args.CanExecute = false;
return;
}

args.CanExecute = true;
}

public override void OnExecute(object sender, ExecutedRoutedEventArgs args)
{
TextRange content = new TextRange(_flowDocument.ContentStart, _flowDocument.ContentEnd);
SqlQueryExecutor.RunQuery(content.Text, _dbScheme.ConnectionString, _resultTabControl);
}

public override void AddParameter<T>(T parameter)
{
Type type = typeof(T);
if (type == typeof(DbScheme))
{
_dbScheme = parameter as DbScheme;
return;
}
if (type == typeof(TabControl))
{
_resultTabControl = parameter as TabControl;
return;
}
if (type == typeof(FlowDocument))
{
_flowDocument = parameter as FlowDocument;
return;
}

throw new ArgumentOutOfRangeException("parameter", "T must be of type DbScheme, TabControl and FlowDocument.");
}

}

It is clear that now provided parameter is checked by type and in the case it is from types expected it is assigned to a specific variable for further use. If the parameter does not belong to any of types expected an exception is thrown. This way in OnCanExecute method I just check if needed parameters are valid and if so the command is possible to be executed.

In the beginning of the class we have a read only variable named _command where you can see how to assign key gestures in order to have shortcut keys assigned to the command.

Here is how I use the concrete command in the loaded event of a WPF window.


RunCommand runCommand = new RunCommand();
runCommand.AddParameter(_dbScheme);
runCommand.AddParameter(_resultsTabControl);
runCommand.AddParameter(document);

this.CommandBindings.Add(new CommandBinding(runCommand.Command,
new ExecutedRoutedEventHandler(runCommand.OnExecute),
new CanExecuteRoutedEventHandler(runCommand.OnCanExecute)));

_runMenuItem.Command = runCommand.Command;

For now, I really like this solution by using template method for adding parameters but I never know what will come into my head tomorrow :).

Leave a Reply