Click here to Skip to main content
15,884,629 members
Articles / Desktop Programming / WPF
Article

TCommand: A WPF Compatible Command Pattern

Rate me:
Please Sign up or sign in to vote.
4.78/5 (7 votes)
6 May 2011CPOL3 min read 23.5K   318   20   3
TCommand allows to define and implement Commands and Arguments strictly at the business logic level, implementing a transparent connection with WPF as with RoutedCommand, but without the glue code.

Introduction

In a project of mine, I encountered a must requirement for a strong separation between the Business Logic and the User Interface (written in WPF). While this is always a best-practice, it can be difficult to reach at the purest level given the following facts:

  • RoutedCommand can be intrusive at the core level but
  • RoutedCommand is a “first class” citizen in the WPF world (consider the behaviour of CommandTarget but requires some (too-much for me) glue code)
  • ICommand offers a good interface... but a lot of work to do to implement the logic required to call the famous CanExecuteChanged EventHandler

Here you’ll find a possible solution with the 70 lines of code below. Let’s start...

Command Pattern

Well, the first step is obvious:

C#
public interface IArgument { }

We need an argument for our command and we know nothing about it, so an empty interface is the best way to model the nothing. The next step is a little trickier:

C#
public interface ICommand<in /> where T : IArgument
{
    bool CanExecute(T arg);
    void Execute(T arg);
}

While this interface can resemble the classic Command pattern implemented in C#, the tricky part is the keyword “in” that enables contravariant on the type argument. This design aspect is used to model the bridge between the abstract business logic and WPF.

Mind the gap

Now that we have our business login on one side and WPF in the other, it’s time to define a bridge. During the core design, the first defined interface was argument related; for the the bridge, we start defining an interface as a User Command (IUCommand):

C#
public interface IUCommand : System.Windows.Input.ICommand
{
    void NotifyCanExecutedChanged();
}

We need to extend ICommand with IUCommand to add a method definition. This method will be used to invoke CanExecuteChanged (that has been declared as an event in the WPF interface). Modeling the argument concept in the bridge is a little more complex:

C#
public class UArguments : DependencyObject
{
    public IUCommand Parent { get; set; }
    private static void PropertyChangedCallback(DependencyObject d,
                                 DependencyPropertyChangedEventArgs e)
    {
        UArguments ua = d as UArguments;
        if ((ua != null) && (ua.Parent != null))
            ua.Parent.NotifyCanExecutedChanged();
    }
    protected static DependencyProperty Register<Owner, T>(
              string name, T defaultValue)
    {
        return DependencyProperty.Register(name, typeof(T), 
          typeof(Owner), new UIPropertyMetadata(
          defaultValue, PropertyChangedCallback));
    }
}

UArguments inherits from DependencyObject (as expected for a class that will contain our bindable property). A static callback is declared to forward the property changed notification to the command parent, with a static wrapper to the dependency property registration. The protected and private modifiers on the callback and wrapper give a stronger assumption about the callback parameter type (the condition ua != null is redundant by design).

Fill the gap

And now, the last class in the bridge, UCommand which will implement the IUCommand interface... and something more:

C#
public abstract class UCommand<CmdType, ArgType> : IUCommand
        where CmdType : ICommand<ArgType>, new()
        where ArgType : UArguments, IArgument, new()
{
    public CmdType Command { get; set; }
    public ArgType Arg { get; set; }
    public UCommand()
    {
        Command = new CmdType();
        Arg = new ArgType() { Parent = this };
    }
    public bool CanExecute(object parameter)
    {
        return Command.CanExecute(Arg);
    }

    public event EventHandler CanExecuteChanged;

    public void Execute(object parameter)
    {
        Command.Execute(Arg);
    }

    public void NotifyCanExecutedChanged()
    {
        if (CanExecuteChanged != null)
            CanExecuteChanged.Invoke(Arg, null);
    }

UCommand will contain an instance of the Argument concept (with UCommand as base) and of an Argument compatible command; besides that, command implementations are trivial.

Try it

Business logic

With these bricks, we can construct a sample login command:

C#
public class Login : ICommand<Login.Args>
{
    public interface Args : IArgument
    {
        string Username { get; set; }
        string Password { get; set; }
    }
    public bool CanExecute(Args arg)
    {
        try
        {
            return (arg != null) && (arg.Username != "") && (arg.Password != "");
        }
        catch (Exception)
        {
            return false;
        }
    }
    public void Execute(Args arg)
    {
        if (CanExecute(arg))
        {
        // Login Core logic
        }
    }

Nothing special in this class; the key point is that it contains only the core logic (argument validation and login) and that the argument is defined only as an interface.

Presentation

From the user interface, we implement an UILogin class where will implement the Login.Args interface, and nothing else:

C#
public class UILogin : TCommand.UCommand<TCommand.Login, UILogin.Args>
{
    public class Args : TCommand.UArguments, TCommand.Login.Args
    {
        public string Username
        {
            get { return (string)GetValue(UsernameProperty); }
            set { SetValue(UsernameProperty, value); }
        }
        public static readonly DependencyProperty UsernameProperty =
            Register<Args, string>("Username", "asd");

        public string Password
        {
            get { return (string)GetValue(PasswordProperty); }
            set { SetValue(PasswordProperty, value); }
        }
        public static readonly DependencyProperty PasswordProperty =
            Register<Args, string>("Password", "dsa");
    }
}

This class shows the expressive power of the approach mixing contravariance with generic and multiple inheritance: this class is reduced to only dependency property declaration that is required for WFP binding... binding shown in the following XAML:

XML
<Window.Resources>
    <local:UILogin x:Key="cmdLogin"></local:UILogin>
</Window.Resources>
<StackPanel Margin="5">
    <TextBlock>Username</TextBlock>
    <TextBox Text="{Binding Source={StaticResource ResourceKey=cmdLogin}, 
        Path=Arg.Username,UpdateSourceTrigger=PropertyChanged}"/>
    <TextBlock>Password</TextBlock>
    <TextBox Text="{Binding Source={StaticResource ResourceKey=cmdLogin}, 
        Path=Arg.Password,UpdateSourceTrigger=PropertyChanged}"/>
    <DockPanel LastChildFill="False">
        <Button DockPanel.Dock="Right"
                Command="{StaticResource ResourceKey=cmdLogin}">Login</Button>
    </DockPanel>
</StackPanel>

Conclusion

TCommand allows to define and implement Commands and Arguments strictly at the business logic level, implementing a transparent connection with WPF as with RoutedCommand, but without the glue code.

History

  • V1: First issue.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Italy Italy
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralMy vote of 5 Pin
agorby8-May-11 10:03
agorby8-May-11 10:03 
GeneralNice Work Pin
daveyboy10337-May-11 6:31
daveyboy10337-May-11 6:31 
GeneralMy vote of 5 Pin
daveyboy10337-May-11 6:31
daveyboy10337-May-11 6:31 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.