ICommand implementations in a separate classes, using only MVVM Light? - c#

We have a large-ish app with a ribbon. The ribbon buttons are all bound to commands in my main view model (the data context of the main app window).
The constructor for MainViewModel is starting to grow as we create lots of RelayCommands (bound to the various ribbon commands). It looks something like this:
public MainWindowViewModel()
{
this.OpenProjectCommand = new RelayCommand(() =>
{
// buncha code
});
this.ProjectTypesCommand = new RelayCommand(() =>
{
// more code
});
this.NewSectionCommand = new RelayCommand(() =>
{
// code code code...
});
// ... only three ribbon buttons down, this is gonna get huge...
}
I'd prefer to have separate classes implementing each of the commands, rather than tons of inline code in MainViewModel's constructor. (Or creating lots of delegates in the MainViewModel, e.g. OpenProject, CanOpenProject, and then passing in references to them to the RelayCommand constructors).
Why don't I simply implement ICommand in a CommandBase and then create separate commands? Because I want to be "standard friendly" as per this question.
Is there a standard ICommand implementation I can use so that my commands are in separate classes?
I'd prefer not to add more MVVM frameworks into the mix since I'm already using MVVM Light. But I also don't want to reinvent the wheel.
Update: MainViewModel.cs doesn't need to be cluttered with scores of #regions or command methods. Extension methods aren't a good fit either IMHO.

The way I do is that I have "sub-viewmodels". For example, in the case of the MainViewModel, let's imagine that you have a PrintCommand and a CancelPrintCommand. You can have a new class called PrinterViewModel, and expose an instance of this class in the MainViewModel. Have the PrintCommand and the CancelPrintCommand in this PrinterViewModel (this also allows modular unit testing, which is neat).
Then in XAML:
Command="{Binding Main.Printer.PrintCommand}"
Alternatively, you could do
new RelayCommand(() => Printer.DoSomething())
Does that make sense?
Cheers
Laurent

You could at least create them in the getter.
You can use the ?? operator.
http://msdn.microsoft.com/en-us/library/ms173224.aspx
This basically says: Return _testCommand, but create it first if it's still null.
This way, the command is not created until it's needed!
public class TestViewModel : ViewModelBase
{
#region OpenCommand
private RelayCommand _testCommand;
public RelayCommand TestCommand {
get {
return _testCommand = _testCommand
?? new RelayCommand(
this.ExecuteOpenCommand,
this.CanOpenCommandExecute);
}
}
private void ExecuteOpenCommand()
{
// do stuff
}
private bool CanOpenCommandExecute()
{
return true;
}
#endregion
}
If your goal is to organize, you can use #region and #endregion. And like we said, if your goal is to shrink the constructing process, use the ?? operator in getters. If you just hate inline code, create private methods in combination with RelayCommand, in getters.

Related

Am I using ICommand correctly?

I have simple 2 buttons. Should I create 1 ICommand or is it ok now?
internal class MainPageViewModel : INotifyPropertyChanged
{
public ICommand RegisterBtnClickedCommand {get; }
public ICommand LoginBtnClickedCommand {get; }
public MainPageViewModel()
{
RegisterBtnClickedCommand = new Command(RegisterButtonPressed);
LoginBtnClickedCommand = new Command(LoginButtonPressed);
}
private void LoginButtonPressed()
{
Application.Current.MainPage.DisplayAlert("Login", "Login", "ok");
}
private void RegisterButtonPressed()
{
Application.Current.MainPage.DisplayAlert("Reg", "Reg", "ok");
}
public event PropertyChangedEventHandler PropertyChanged;
}
And for example, if I had 10 buttons, then I should have 10 commands?
And for example, if I had 10 buttons, then I should have 10 commands?
Yes.
Note that a single command might be invoked in different ways (button, menu entry, keyboard shortcut, unit test code), and that each command might have a different CanExecute logic. Thus, commands provide a convenient separation of concerns between the command itself (= your ICommand) and the way it is invoked (bindings in XAML).
If you are worried about too much boilerplate code, you can shorten your code slightly by initializing the property right at the point of declaration (rather than inside the constructor):
public ICommand RegisterBtnClickedCommand { get; } = new Command(RegisterButtonPressed);
...
Keep two separate commands for this. "Login" and "Register" are distinct enough actions. You can reuse commands for something like deleting an item from a list and pass in parameters to identify the item, but this is not the case here.
Though I wouldn't include Btn, Button, and Pressed in their names as they should be separated from any UI ideas (imagine a link being used later on instead of a button). I'd go with LoginCommand and LoginExecute for example.

How to make ViewModel invoke method from component used in View - WPF Prism

In my View I'm using a component (custom control), which provides some functions. I want to invoke one of them when my ViewModel receives an event it is subscribed to.
I want to do this as cleanly as possible, since there might be more functions I would be using this way.
I know I can create a variable like "InvokeFunctionA", bind to this variable and create OnChange method in my View which will invoke the corresponding function. But it's quite a lot of code required just to invoke a single function. And an extra variable, which seems quite unnesessary, too.
Is there a better way to do this? Like, maybe a View can pass some kind of a handler function to ViewModel which will do the work? I've made quite a lot of research but haven't yet found anything that suits my problem. Or maybe I'm missing something obvious?
[ edit ]
Haukinger solution works for now (done this way: https://blog.machinezoo.com/expose-wpf-control-to-view-model-iii ), but I don't think it's the cleanest solution (Instead of providing access to a few functions, I'm exposing whole control to the ViewModel).
In a perfect MVVM-world (as you are asking for a clean solution), the ViewModel does not call anything that is located in the view (neither directly nor indirectly). I'd approach the problem like this:
If 'component' is NOT a usercontrol, try moving it to the ViewModel and use bindings or commands in the view to operate your 'component'.
If 'component' is a usercontrol, give 'component' a dependency property and fill it via a binding with your property of the ViewModel. Inside of 'compontent' you can register value change callback of your dependency property to start your work. <local:UserControlComponent MyDependencyProperty="{Binding PropertyInViewModel}" />
As a last resort:
You could add a C# event to the viewmodel and handle it in your code-behind inside the view.
Instead of an event, you could alternatively use IObservable pattern (https://learn.microsoft.com/en-us/dotnet/api/system.iobservable-1?view=netframework-4.8, https://github.com/dotnet/reactive)
For completeness sake a no-go option: Prism has an EventAggregator that can be used for loose communication. I've had to remove the usage of EventAggregator from a rather big App, because it was not maintainable any more.
Expose a dependency property in your view whose type is the provided interface, bind it to a property on your view model, then call the method on the interface on the view model property from the view model.
To clarify, I don't mean to expose the component itself, rather an interface that contains exactly one method. The view has to have a private class that implements the interface and routes to the actual component, as well as converting arguments and results so that types belonging to the components need not be present in the interface.
But I'm with sa.he in that this whole situation should be avoided in the first place. It may not be possible, depending on the third party components used, though.
Yes, invoking view's methods from VM is very much against pure MVVM and there's not going to be a 'clean' solution.
But it can be done at least half decently. You would need to create a special attached property (or behavior, but property seems to be a better choice in this scenario) and an ICommand property in VM, then bind the AP to the property with OneWayToSource binding and use command invocation in VM. It would still be a lot of code, but once it's done, you would only need to create new properties in the VM.
Below is some code that I wrote, consider it as a starting point, you can add support for command parameters and converters.
public class MethodDelegation : DependencyObject
{
public static readonly DependencyProperty CommandDelegatesProperty =
DependencyProperty.RegisterAttached("CommandDelegatesInternal", typeof(CommandDelegatesCollection), typeof(MethodDelegation), new PropertyMetadata(null));
private MethodDelegation() { }
public static CommandDelegatesCollection GetCommandDelegates(DependencyObject obj)
{
if (obj.GetValue(CommandDelegatesProperty) is null)
{
SetCommandDelegates(obj, new CommandDelegatesCollection(obj));
}
return (CommandDelegatesCollection)obj.GetValue(CommandDelegatesProperty);
}
public static void SetCommandDelegates(DependencyObject obj, CommandDelegatesCollection value)
{
obj.SetValue(CommandDelegatesProperty, value);
}
}
public class CommandDelegatesCollection : FreezableCollection<CommandDelegate>
{
public CommandDelegatesCollection()
{
}
public CommandDelegatesCollection(DependencyObject targetObject)
{
TargetObject = targetObject;
((INotifyCollectionChanged)this).CollectionChanged += UpdateDelegatesTargetObjects;
}
public DependencyObject TargetObject { get; }
protected override Freezable CreateInstanceCore()
{
return new CommandDelegatesCollection();
}
private void UpdateDelegatesTargetObjects(object sender, NotifyCollectionChangedEventArgs e)
{
foreach (CommandDelegate commandDelegate in e?.NewItems ?? Array.Empty<CommandDelegate>())
{
commandDelegate.TargetObject = TargetObject;
}
}
}
public class CommandDelegate : Freezable
{
public static readonly DependencyProperty MethodNameProperty =
DependencyProperty.Register("MethodName", typeof(string), typeof(CommandDelegate), new PropertyMetadata(string.Empty, MethodName_Changed));
public static readonly DependencyProperty CommandProperty =
DependencyProperty.Register("Command", typeof(ICommand), typeof(CommandDelegate), new PropertyMetadata(null));
public static readonly DependencyProperty TargetObjectProperty =
DependencyProperty.Register("TargetObject", typeof(DependencyObject), typeof(CommandDelegate), new PropertyMetadata(null, TargetObject_Changed));
private MethodInfo _method;
public string MethodName
{
get { return (string)GetValue(MethodNameProperty); }
set { SetValue(MethodNameProperty, value); }
}
public ICommand Command
{
get { return (ICommand)GetValue(CommandProperty); }
set { SetValue(CommandProperty, value); }
}
public DependencyObject TargetObject
{
get { return (DependencyObject)GetValue(TargetObjectProperty); }
set { SetValue(TargetObjectProperty, value); }
}
protected override Freezable CreateInstanceCore()
{
return new CommandDelegate();
}
private static void MethodName_Changed(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var del = (CommandDelegate)d;
del.UpdateMethod();
del.UpdateCommand();
}
private static void TargetObject_Changed(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var del = (CommandDelegate)d;
del.UpdateMethod();
del.UpdateCommand();
}
private void UpdateMethod()
{
_method = TargetObject?.GetType()?.GetMethod(MethodName);
}
private void UpdateCommand()
{
Command = new RelayCommand(() => _method.Invoke(TargetObject, Array.Empty<object>()));
}
}
The XAML usage is as follows:
<TextBox>
<l:MethodDelegation.CommandDelegates>
<l:CommandDelegate MethodName="Focus"
Command="{Binding TestCommand, Mode=OneWayToSource}" />
</l:MethodDelegation.CommandDelegates>
</TextBox>
Bubble your event upwards. Have your VM publish some event of its own. Your V can subscribe to it (if it wishes).
The downside is that you'll need codebehind, where ideally a V should be XAML-only as far as possible. The upside is that your VM remains quite aloof (i.e. it's not dependent on any specific controls used by the V). It says "something has happened worthy of note", but it doesn't assume either that (a) anyone is particularly listening, or (b) it leaves it to the listener (in your case, the V) to decide exactly what to action to take (i.e. how to change the UI).
It's a perennial problem - how does a VM cause a V to update somehow, and as far as I can tell it is still something to be debated.
The mechanism above, I've got a vague recollection that Prism itself might include something similar. I'm fairly sure it uses something akin to INotifyPropertyChanged (i.e. some interface or other) rather than an "event" as we might understand it just from a working knowledge of .net. You might even be able to use this mechanism to dispense with codebehind altogether. The downside of using Prism in the first place is its bulk, but if you're already using it anyway...
It's for you to decide how clean this is. I decided that a bit of codebehind was preferable to the VM meddling directly with the UI.

Is this the right way to use the Command Pattern?

Related to this other question: How to inject an action into a command using Ninject?
Based on the comments on the above-referenced question, I take it that I would just need to create some command classes and inject them in my view model so that the view's controls just need to bind to them. I conceptually agree and understand the benefits. Besides, I wish to be as clean as possible using Ninject, DI and Constructor Injection.
Following these important rules, here's what I've come with so far.
CreateCategoryCommand
public class CreateCategoryCommand : ICommand {
public CreateCategoryCommand(CreateCategoryView view) {
if(view == null) throw new ArgumentNullException("view");
this.view = view;
}
public bool CanExecute(object parameter) { return true; }
public event EventHandler CanExecuteChanged;
public void Execute(object parameter) { view.Show(); }
private readonly CreateCategoryView view;
}
CategoriesManagementViewModel
public class CategoriesManagementViewModel {
public CategoriesManagementViewModel(ICommand createCommand) {
if (createCommand == null) throw new ArgumentNullException("createCommand");
this.createCommand = createCommand;
}
public ICommand CreateCommand { get { return createCommand; } }
private readonly ICommand createCommand;
}
So now when the CategoriesManagementView is initialized, it is constructor-injected with the CategoriesManagementViewModel, which in turn is constructor-injected with the CreateCategoryCommand, which in turn is constructor-injected with the CreateCategoryView, so no redundant dependency, neither any cycle-dependency.
Now, when I the CategoriesManagementView.CreateButton, it shall trigger the bound CategoriesManagementViewModel.CreateCommand, which will show the CreateCategoryView to the user, and this view shall have its own proper commands as well injected the same way.
Finally, this would render the RelayCommand class as useless...
Is that it?
First, I agree that RelayCommand and DelegateCommand and the like are ways of implementing commands that violate SOLID principles, so your solution here to replace them with a separate class is the correct one. Doing so also keeps your ViewModels much cleaner.
That said, you're violating MVVM pretty badly by having a class in your ViewModels layer (the CreateCategoryCommand) have knowledge of a concrete that is in your Views layer (CreateCategoryView). Nothing in your ViewModels layer should have a direct reference to anything in your Views layer.
Imagine it this way - you've separated your layers out into different dlls - Views.dll, ViewModels.dll, Models.dll, DataLayer.dll. If something in your ViewModels has a reference to a concrete in your Views, and obviously your Views will have a reference to ViewModels (as is necessary), then you have a circular reference problem.
The solution is to have your View object implement an interface (Interface Segregation Principle) like IDialog or IUiDisplay (choose the name depending on how abstract you want to be), and have your command have a dependency on that interface, NOT the direct concrete type, like so:
In Views:
public class CreateCategoryView : ..., IUiDisplay
{
...
}
In ViewModels:
public interface IUiDisplay
{
void Show();
}
public class CreateCategoryCommand : ICommand
{
public CreateCategoryCommand(IUiDisplay uiDisplay) {
if(display == null) throw new ArgumentNullException("uiDisplay");
this.display = uiDisplay;
}
private readonly IUiDisplay display;
...
}
Now, your Command no longer has a direct dependency on a concrete (so it is now mockable and testable!) from a higher layer. Now you can have your DI/IOC resolve the command dependency to the specific view class you want to inject. (I'd personally inject a view factory into the command instead, and only create the view lazily, but that's a different discussion).
One related note - if you implement commands by directly having them implement ICommand, then you're going to repeat yourself a lot (DRY). My suggestion is to create an abstract base class (CommandBase or something) that implements the requirements of ICommand. You'll find that all your commands that derive from it will only override Execute() and sometimes CanExecute(). This saves you from having to implement the event (and code to raise the event) in every command, and in many cases saves you from having to implement CanExecute since most commands just return true.

Open a new Window in MVVM

Lets say I have a MainWindow and a MainViewModel, I'm not using MVVM Light or Prism in this example.
In this MainWindow I want to click a MenuItem or Button to open a NewWindow.xaml not a UserControl.
I know how to use this with UserControl to open a new UserControl in my existing Window in a ContrntControl or a Frame.
<ContentControl Content="{Binding Path=DisplayUserControl,UpdateSourceTrigger=PropertyChanged}" />
Code
public ViewModelBase DisplayUserControl
{
get
{
if (displayUserControl == null)
{
displayUserControl = new ViewModels.UC1iewModel();
}
return displayUserControl;
}
set
{
if (displayUserControl == value)
{
return;
}
else
{
displayUserControl = value;
OnPropertyChanged("DisplayUserControl");
}
}
}
In the ResourceDitionary for MainWindow I have :
<DataTemplate DataType="{x:Type localViewModels:UC1ViewModel}">
<localViews:UC1 />
</DataTemplate>
<DataTemplate DataType="{x:Type localViewModels:UC2ViewModel}">
<localViews:UC2 />
</DataTemplate>
The thing is that I want to open a new Window, not a UserControl. So I use some code like this :
private ICommand openNewWindow;
public ICommand OpenNewWindow
{
get { return openNewWindow; }
}
public void DoOpenNewWindow()
{
View.NewWindowWindow validationWindow = new View.NewWindow();
NewWindowViewModel newWindowViewModel = new NewWindowViewModel();
newWindow.DataContext = ewWindowViewModel;
newWindow.Show();
}
and then a bind OpenNewWindow to a MenuItem or Button.
I know this is not the right way, but what is the right way to do this ?
Thanks!
There are two problems you need to solve with this type of application.
Firstly, you do not want to have the View-Model creating and displaying UI components directly. One of the motivations for using MVVM is to introduce test-ability in to your View-Model, and having this class pop up new windows makes this class harder to test.
The second problem you need to solve is how to resolve the dependencies in your application, or in this instance – how to you “hook up” the View-Model to the corresponding View? A maintainable solution to this latter problem is given by the use of a DI container. A very good reference to this subject is given by Mark Seemann’s Dependency Injection in .NET. He actually also discusses how to solve the first problem too!
To solve the former problem, you need to introduce a layer of indirection to your code, to make the View-Model not dependent on a concrete implementation of creating a new window. A very simple example is given in the code below:
public class ViewModel
{
private readonly IWindowFactory m_windowFactory;
private ICommand m_openNewWindow;
public ViewModel(IWindowFactory windowFactory)
{
m_windowFactory = windowFactory;
/**
* Would need to assign value to m_openNewWindow here, and associate the DoOpenWindow method
* to the execution of the command.
* */
m_openNewWindow = null;
}
public void DoOpenNewWindow()
{
m_windowFactory.CreateNewWindow();
}
public ICommand OpenNewWindow { get { return m_openNewWindow; } }
}
public interface IWindowFactory
{
void CreateNewWindow();
}
public class ProductionWindowFactory: IWindowFactory
{
#region Implementation of INewWindowFactory
public void CreateNewWindow()
{
NewWindow window = new NewWindow
{
DataContext = new NewWindowViewModel()
};
window.Show();
}
#endregion
}
Note that you take an implementation of IWindowFactory in the constructor of your View-Model, and it is to this object that the creation of the new window is delegated to. This allows you to substitute the production implementation for a different one during testing.

Is it okay to use ICommand in view-model

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?
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.
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.
Also note, your injection is done at initial application start. Your ViewModel is decoupled from whatever IoC container you choose.
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.

Categories