12

Most of the WPF mvvm applications, we are using ICommand in the view-model. But it is referring to System.Windows.Input. so the view-model is now tightly couple with System.Windows.Input namespace. according to my understanding view-model should be able to use in normal C# winform application or asp.net application.

Normally we are using following code lines to the command with RelayCommand implementation.

private RelayCommand testCommand;// or private ICommand testCommand;

public ICommand TestCommand
{
    get
    {
        return testCommand ?? 
            (testCommand = new RelayCommand(param => Test()));
    }
}

public void Test()
{

}

What i feel is we need to remove all the ICommand and use RelayCommand instead. So we can eliminate the System.Windows namespace from the view-model. so final code will looks like this,

private RelayCommand testCommand;

public RelayCommand TestCommand
{
    get
    {
        return testCommand ?? 
            (testCommand = new RelayCommand(param => Test()));
    }
}

public void Test()
{

}

Any suggestions on this approach? or is there any way to eliminate the System.Windows namespace from the view-model?

Chamika Sandamal
  • 23,565
  • 5
  • 63
  • 86

3 Answers3

6

Any suggestions on this approach?

This still doesn't decouple you from System.Windows.Input as RelayCommand still must implement ICommand, even if it's indirectly implementing it.

Implementing ICommand within the ViewModel is one of those things that tends to be required in order to be pragmatic. Ideally, ICommand (or a similar interface) would have been implemented in a namespace that wasn't XAML specific. That being said, it is supported directly within the Portable Class Libraries, so it is not tied to a specific framework (WPF, Silverlight, Phone, etc) as much as XAML in general.

Reed Copsey
  • 554,122
  • 78
  • 1,158
  • 1,373
6

Pretty simple to avoid coupling your ViewModel to ICommand, if you want to. Probably not a bad idea, WPF will probably go the way of MFC one day. Overkill? maybe, but here is a how:

In your view:

<StackPanel>
    <Button Command="{Binding Path=MyCommand}"> Do it! Kill me Now!</Button>
    <TextBlock Text="{Binding Path=Message}"></TextBlock>
</StackPanel>

Inject your ViewModel into your DataContext, Take the responsibility for the native commands, out of your view model:

public class ViewModel : INotifyPropertyChanged
{
    public string Message { get; set; }
    public object MyCommand { get; set; }


    public void OnMyCommand(object parameter)
    {
        Message += "I Ran something" + Environment.NewLine;
    }

    public bool CanMyCommand(object parameter)
    {
        return true;
    }

    // Injected Native Command handler
    public ViewModel(ICommandFactory factory)
    {
        MyCommand = factory.CreateInstance(OnMyCommand, CanMyCommand);
    }

    public event PropertyChangedEventHandler PropertyChanged;
}

Note I'm using FODY to weave in the property change handler. INotifyPropertyChanged is System.dll btw.

Now, Bind this contract:

public interface ICommandFactory
{
    object CreateInstance(Action<object> action, Func<object, bool> predicate);
}

... to something that will give you a native Command object;

public class NativeCommand : ICommand
{
    private readonly Action<object> _action;
    private readonly Func<object, bool> _predicate;

    public NativeCommand(Action<object> action, Func<object, bool> predicate)
    {
        _action = action;
        _predicate = predicate;
    }

    public bool CanExecute(object parameter)
    {
        return _predicate(parameter);
    }

    public void Execute(object parameter)
    {
        _action(parameter);
    }

    public event EventHandler CanExecuteChanged;
}


public class NativeCommandFactory : ICommandFactory
{
    public object CreateInstance(Action<object> action, Func<object, bool> predicate)
    {
        return new NativeCommand(action, predicate);
    }
}

Bind<ICommandFactory>().To<NativeCommandFactory>();

Voilà, decoupled commands.

running

Also note, your injection is done at initial application start. Your ViewModel is decoupled from whatever IoC container you choose.

Community
  • 1
  • 1
Meirion Hughes
  • 24,994
  • 12
  • 71
  • 122
  • FODY is a big library and i'm not happy to use it for this. thanks for the suggestion. :) – Chamika Sandamal Mar 13 '13 at 06:03
  • Umm, its only for the sake of brevity. You can implement the property change handlers yourself... Oh and fody isn't a library, its IL Weaving... no coupling, no reference. – Meirion Hughes Mar 13 '13 at 08:43
2

Well, in theory, you are pretty much right. It would if nice of ICommand was completely UI-platform-independent.

But from a practical standpoint, if you are using MVVM in a WPF app, there's a pretty good chance you are fairly dependent on WPF's databinding and datatemplating capabilities anyway. Trying to stick a WinForms UI on top of something like that would likely require a significant amount of extra effort.

I've worked on some fairly large WPF/MVVM projects in the past. We considered MVVM to be a way of separating the specific details of the UI from the code - not so that we could switch to WinForms/ASP.NET/whatever, but so that we could change the look and feel of our UI (i.e. edit the XAML) without having to change the ViewModels. In this respect, MVVM worked perfectly.

If you are really concerned about sharing code across multiple types of projects, it might be better to try and put your common code in a typical 'Business Layer'-type class library, instead of in view model.

Marty
  • 7,464
  • 1
  • 31
  • 52
  • I tend to follow a similar approach. One thing I try to consider is whether a particular 'UI dependency' creates a roadblock to unit testing of a view model. RelayCommand does not create a problem there. Things like injecting a DispatcherTimer, however, potentially could. – Dan Bryant Mar 12 '13 at 21:53