This project is read-only.

Language-integrated ICommand

Topics: C# Language Design
Apr 4, 2014 at 1:55 PM
Recently I've been wondering again how I could implement ICommand methods for a WPF application in the cleanest and most concise way. I came up with a language integration idea that could look like this:
class MyViewModel
{
    public command AddItemCommand
    {
        test { return selectedItems.Count == 0; }
        action { items.Add(new Item()); }
    }
}
This allows defining the command name only once (as opposed to at least 3 times) and puts the implementation to the same place (and not somewhere else in the file out of view).

I understand that this is probably a too specific thing to put it in the language. But it's an interesting idea anyway. Maybe it could be useful for other situations as well? Maybe there could be some extension mechanism that allows projects to define such extensions and their members through some C# classes, project configuration or whatever. WPF projects could be created with this readily set-up.
Apr 5, 2014 at 8:58 PM
I'm not familiar with WPF. Can you post an example that would be simplified by this proposal?
Apr 6, 2014 at 11:56 AM
Edited Apr 6, 2014 at 11:57 AM
Well you could use RelayCommand for similar purpose:

ICommand AddItemCommand { get; set; }
...
AddItemCommand = new RelayCommand<Item>(AddItem, CanAddItem);
...
private void AddItem(Item item)
{ ... }
private bool CanAddItem(Item item)
{ ... }
Apr 6, 2014 at 1:16 PM
Okay, so here's a short introduction of the problem domain, the solution designed by Microsoft, more practical common solutions and a number of way to write it.

In WPF, most GUIs are implemented with the MVVM design pattern. A major feature of it is data binding to a View Model instance to display data to the user and automatically update it when changed (INotifyPropertyChanged). A way to provide feedback from the UI the the model behind is through commands. A command is an instance of the ICommand interface that is provided to the GUI through a property on the View Model. ICommand has three members: Execute(), CanExecute(), and CanExecuteChanged. WPF will call the methods when the user clicks on a button, for example, to invoke an action in the view model instance. The event is used to notify about changed availability of the command.

The original design from Microsoft is creating a class that implements ICommand for each command of an application. Each class then provides these three members and the view model class provides an instance of each class through a public property. This would lead to many classes and fragmented code (the command implementeation is far away from the view model data).

A more practical solution is to use a helper class often called RelayCommand or DelegateCommand. That is a class that implements the ICommand interface, but does not provide any action logic on its own but rather accepts delegates to both method implementations. The event can be fired with a method. This helper class is kind of boilerplate code that a developer carries around from one project to another, if they're not using a more extensive MVVM framework.

I used to write the code like this in earlier projects:
class MyViewModel
{
    private DelegateCommand addItemCommand;
    public DelegateCommand AddItemCommand
    {
        get
        {
            if (addItemCommand == null)
            {
                addItemCommand = new DelegateCommand(OnAddItem[, CanAddItem]);
                // You could also inline the command code here with delegate() or
                // a lambda expression if it's short enough.
            }
            return addItemCommand;
        }
    }
}
This involves writing the command name 5 times. Plus the derived names of the actual command implementation methods. I have changed that style in newer projects:
class MyViewModel
{
    public MyViewModel()
    {
        InitializeCommands();
    }

    public DelegateCommand AddItemCommand { get; private set; }

    private void InitializeCommands()
    {
        AddItemCommand = new DelegateCommand(OnAddItem[, CanAddItem]);
    }
}
This gets rid of the backing variable but still requires writing the command's name 2 times plus the method names and needs more boilerplate code to be copied around or written each time.

My suggested language integration could provide a much shorter and more readable way to provide commands. The new code from my first posting could then be resolved to: (Using actual naming from the C# compiler, this is not valid C# source syntax.)
class MyViewModel
{
    private DelegateCommand <AddItemCommand>k_BackingField;
    public DelegateCommand AddItemCommand
    {
        get
        {
            if (<AddItemCommand>k_BackingField == null)
            {
                <AddItemCommand>k_BackingField = new DelegateCommand(execute_AddItemCommand, test_AddItemCommand);
            }
            return <AddItemCommand>k_BackingField;
        }
    }

    private bool test_AddItemCommand() { return selectedItems.Count == 0; }
    private void execute_AddItemCommand() { items.Add(new Item()); }
}
It would just need a way to fire the command's CanExecuteChanged event, which I haven't thought of yet.
Apr 7, 2014 at 12:59 AM
ICommand is framework level thing, I don't think it should be a part of the language itself.

Considering expected C# property initializers, I think issue is solved by something like couple of lambdas:
class MyViewModel
{
    public DelegateCommand AddItemCommand { get; } = new DelegateCommand(parameter =>
        {
            // code to execute
        },
        ()=> !IsReadOnly);
 }
Apr 7, 2014 at 6:56 AM
If these property initialisers will work the same as field initialsers, they can only be assigned static members, not instance members. That won't help much at all. It's only good for constant numbers and strings mostly.
Apr 7, 2014 at 7:10 AM
Edited Apr 7, 2014 at 7:25 AM
I see your point, it's not possible to access instance members from initializer. Perhaps this should be addressed instead of extending language for specific interface implementation?

It's quite possible this is going to be part of "Expression-bodied members" according to https://roslyn.codeplex.com/wikipage?title=Language%20Feature%20Status&referringTitle=Documentation
Apr 7, 2014 at 7:30 AM
Yes, that would be good. It's still not as elegant as my proposal, as it involves more arrows and stuff, but it's still better than PowerShell or even Perl and it would solve the issue of selecting the actual class (e. g. DelegateCommand) for this command.