I have the following scenario:
public class MyCommand : ICommand
{
MyViewModel _viewModel;
public MyCommand(MyViewModel viewModel)
{
_viewModel = viewModel;
}
public bool CanExecute(object parameter)
{
return true;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
_viewModel.SomeMethod();
}
}
Essentially, this command will simply call a method in my ViewModel when a button is clicked. CanExecute always returns true.
The issue is that even though this is generally considered to be the best way of doing things, it isn't very elegant. It begs the question of why I need a command to do this very simple process.
Why can't I just skip the command, and call the method directly?
I've had a think about this, and the only way that I can see to achieve this would be to create a command where I can specify the method name that I want to call in the CommandParameter property on my button.
I hope someone else can offer an elegant solution to this problem.
You can use the "CallMethodAction" action from Blend SDK to call a method on the view model from your view. It will look something like this:
<Button ...>
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<ei:CallMethodAction MethodName="MyMethod" TargetObject="{Binding}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
However it is still considered the best practice to use commands for that. As Ayyappan mentioned, you could use a library like MVVM Light Toolkit so you don't have to write your own implementation of ICommand.
PS: See also WPF: MVVM: Command vs CallMethodAction?
You can use RelayCommand from MVVMLight or DelegateCommand from PRISM. They both will give a generic way of implemeting Commands.
Or even you can create your own Common command class and use it. Refer the sample below.
public class ViewModel : INotifyPropertyChanged
{
public BaseCommand GetDataCommand { get; set; }
public ViewModel()
{
GetDataCommand = new BaseCommand(GetData);
}
private void GetData(object param)
{
}
}
public class BaseCommand : ICommand
{
private Predicate<object> _canExecute;
private Action<object> _method;
public event EventHandler CanExecuteChanged
{
add
{
CommandManager.RequerySuggested += value;
}
remove
{
CommandManager.RequerySuggested -= value;
}
}
public BaseCommand(Action<object> method, Predicate<object> canExecute=null)
{
_method = method;
_canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
if (_canExecute == null)
{
return true;
}
return _canExecute(parameter);
}
public void Execute(object parameter)
{
_method.Invoke(parameter);
}
}
Related
I'm still learning about wpf, however I'm familiar withe how to setup mvvm in wpf c#. However when it comes to the ICommand/RelayCommand stuff, its a bit of a confusing area for me. Over the past few months I've compiled a few implementations of the ICommand classes in order to create my tools. However I'm at the point now where I've read a few articles and I've looked at the code long enough, I'm looking for someone to help me out and put into simple terms what is going on here and if so, how can I combine/clean up these classes. At the moment the code seems redundant and I'm not sure how to go about optimizing it. Hope this isn't asking for to much. Thanks.
The two important things I want to maintain in this, is the ability to pass arguments to the commands as seen in this first usage example of RelayCommand. Secondly the ability to enable/disable a command as seen in the second command.
So in my tool i have this helper class.
1. I don't get the differences of use between the two classes inside this RelayCommand.cs. There is a public and an internal class.
2. Is there a need for both or can they be combine?
RelayCommand.cs
using System;
using System.Windows.Input;
namespace WpfApplication1.Helper
{
public class RelayCommand<T> : ICommand
{
private readonly Action<T> execute;
private readonly Predicate<T> canExecute;
public RelayCommand(Action<T> execute, Predicate<T> canExecute = null)
{
if (execute == null)
throw new ArgumentNullException("execute");
this.execute = execute;
this.canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
if (parameter == null)
{
return true;
}
else
{
return canExecute == null || canExecute((T)parameter);
}
}
public void Execute(object parameter)
{
execute((T)parameter);
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
}
// added
internal class RelayCommand : ICommand
{
private readonly Predicate<object> canExecute;
private readonly Action<object> execute;
public RelayCommand(Action<object> execute, Predicate<object> canExecute = null)
{
if (execute == null)
throw new ArgumentNullException("execute");
this.execute = execute;
this.canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
return canExecute == null || canExecute(parameter);
}
public void Execute(object parameter)
{
execute(parameter);
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
}
}
An example of me using the RelayCommand.cs in my class object called Customer.cs
private ICommand addNewLicense_Command;
public ICommand AddNewLicense_Command
{
get
{
return addNewLicense_Command ?? (addNewLicense_Command = new RelayCommand<Customer>(n =>
{
AddNewLicense_Execute(n);
}));
}
}
So then in my MainViewModel.cs i have another ICommand Class in the same project my Helper class mentioned above is part of. Is this class necessary? It seems so similar to the RelayCommand class.
public class CommandHandler : ICommand
{
private Action _action;
private bool _canExecute;
public CommandHandler(Action action, bool canExecute)
{
_action = action;
_canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
return _canExecute;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
_action();
}
}
An example of me using CommandHandler in my MainViewModel.cs
private ICommand addNewUser_Command;
public ICommand AddNewUser_Command
{
get
{
return addNewUser_Command ?? (addNewUser_Command = new CommandHandler(() => AddNewUser_Execute(), true));
}
}
If you use a library like MVVM Lite then it will provide the RelayCommand implementations for you. Either way use the non-generic when you don't need a parameter passed in e.g. an "Ok" button:
public ICommand OkCommand { get { return new RelayCommand(Ok); } }
protected virtual void Ok()
{
// ... do something ...
}
The associated XAML is something like:
<Button Content="Ok" Command="{Binding OkCommand}" IsDefault="True" />
Use the generic when you want to pass a parameter:
public ICommand OpenClientCommand { get { return new RelayCommand<Client>(OnOpenClient); } }
private void OnOpenClient(Client client)
{
// ... do something with client ...
}
In this case you need to pass in a Client object via the command parameter:
<Button Content="Open" Command="{Binding OpenClientCommand}" CommandParameter="{Binding SelectedClient}"/>
Passing parameters is also handy when used with event triggers, e.g. you can add something like this to intercept your MainWindow's Closing event:
<i:Interaction.Triggers>
<i:EventTrigger EventName="Closing">
<cmd:EventToCommand Command="{Binding ClosingCommand}" PassEventArgsToCommand="True" />
</i:EventTrigger>
</i:Interaction.Triggers>
This trigger passes the message arguments into your handler which gives you the opportunity to cancel it in the event that the user hasn't saved their changes:
public ICommand ClosingCommand { get { return new RelayCommand<CancelEventArgs>(OnClosing); } }
private void OnClosing(CancelEventArgs args)
{
if (!PromptUserForClose())
args.Cancel = true;
}
The RelayCommand you have is all you need. If you want to disable the command you can pass a method in the constructor do do so:
return addNewLicense_Command ?? (addNewLicense_Command = new RelayCommand<Customer>(n =>
{
AddNewLicense_Execute(n);
},AllowAddNeLicense));
...
bool AllowAddNewLicense()
{
return _allowAddEnabled;
}
The second class called CommandHandler is just another implementation of ICommand. The difference is that you can pass the "enabled" boolean inside the constructor, which means it will remain the same unless you create a new instance of it. While in the RelayCommand you can pass a function that gets executed everytime* so you can influence the outcome.
I have a command parameter set on a button as follows:
<hw:ActionButton Content="MC" Command="{Binding ActionCommand}" CommandParameter="{x:Static hw:Action.MemoryClear}" Grid.Row="2" Grid.Column="0" />
The command is in the class:
public class ActionCommand : ICommand
{
private readonly CalculatorViewModel _viewModel;
public ActionCommand(CalculatorViewModel viewModel)
{
_viewModel = viewModel;
}
public bool CanExecute(object parameter)
{
return _viewModel != null && parameter is Action;
}
public void Execute(object parameter)
{
_viewModel.ProcessAction((Action)parameter);
}
public event EventHandler CanExecuteChanged;
}
Now I know commands must be in the ViewModel, so how do I couple this command to the ViewModel? Do I merely have an instance of it in the ViewModel called ActionCommand, which doesn't seem to work, or what must I do?
here is a sample for the same
class CalculatorViewModel
{
public CalculatorViewModel()
{
ActionCommand = new ActionCommand(this);
...
}
public ActionCommand ActionCommand { get; private set; }
...
}
above is a simple sample demonstrating a property for command which can be bound to button in the UI
point to note here is that the binding works only with public properties, public variables does not work in the same way.
as a suggestion you may use some DelegateCommand if you are going to create more similar commands.
What is CanExecuteChanged?
from Allowing CommandManager to query your ICommand objects
Because by default WPF has no idea that your custom ICommand objects exist. How would it?
Fortunately there is an easy solution to this problem. In your ICommand implementation, you make the CanExecuteChanged event hook the CommandManager’s RequerySuggested event.
sample
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
I am relatively new to WPF Binding, and having a difficulty in binding a ICommand implemented Class which is to detect user keyboard input in the window? At first I think I need to bind it to the Window on XAML, but, it appears that Window doesn't have Command.
This is my ViewModel Class
public class NoteBoxViewModel
{
public MusicalNotation MusicalNotation { get; set; }
public ICommand KeyboardHotkeyCommand { get; private set; }
public bool IsSelected { get; set; }
public NoteBoxViewModel()
{
MusicalNotation = new MusicalNotation();
KeyboardHotkeyCommand = new KeyboardHotkeyCommand(this);
IsSelected = true;
}
}
This is my KeyboardHotkeyCommand Class
public class KeyboardHotkeyCommand : ICommand
{
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
private NoteBoxViewModel _vm;
public KeyboardHotkeyCommand(NoteBoxViewModel vm)
{
_vm = vm;
}
public bool CanExecute(object parameter)
{
return _vm.IsSelected;
}
public void Execute(object parameter)
{
if (Keyboard.IsKeyDown(Key.OemPeriod))
{
_vm.MusicalNotation.Note = Notes.Blank;
}
else if (Keyboard.IsKeyDown(Key.D0))
{
_vm.MusicalNotation.Note = Notes.Rest;
}
else if (Keyboard.IsKeyDown(Key.D1))
{
_vm.MusicalNotation.Note = Notes.N1;
}
else if (Keyboard.IsKeyDown(Key.D2))
{
_vm.MusicalNotation.Note = Notes.N2;
}
else if (Keyboard.IsKeyDown(Key.D3))
{
_vm.MusicalNotation.Note = Notes.N3;
}
else if (Keyboard.IsKeyDown(Key.D4))
{
_vm.MusicalNotation.Note = Notes.N4;
}
else if (Keyboard.IsKeyDown(Key.D5))
{
_vm.MusicalNotation.Note = Notes.N5;
}
else if (Keyboard.IsKeyDown(Key.D6))
{
_vm.MusicalNotation.Note = Notes.N6;
}
else if (Keyboard.IsKeyDown(Key.D7))
{
_vm.MusicalNotation.Note = Notes.N7;
}
else if (Keyboard.Modifiers == ModifierKeys.Control && Keyboard.IsKeyDown(Key.OemPlus))
{
_vm.MusicalNotation.Octave++;
}
else if (Keyboard.Modifiers == ModifierKeys.Control && Keyboard.IsKeyDown(Key.OemMinus))
{
_vm.MusicalNotation.Octave--;
}
}
}
This is my Notes Enumeration
public enum Notes
{
N1,
N2,
N3,
N4,
N5,
N6,
N7,
Rest,
Blank
}
Questions :
How to bind KeyboardHotkeyCommand to my XAML Class so I can detect user input (input does not going to Textbox or any sort of text editor first)? I also tried to bind it to Window_IsKeyDown (it's a bad practice, I know) but it failed afterall. Is it possible to achieve it without event?
In my KeyboardHotkeyCommand, there's an event named CanExecuteChanged. I filled it with exact direction with what is told here : http://blog.lab49.com/archives/2650. But I have no idea why it's filled that way. Can anyone explain to me?
I also studied the tutorial here : http://reedcopsey.com/series/windows-forms-to-mvvm/ and it seems that he can directly instantiate the ICommand with ActionCommand, but I can't find any in my Visual Studio. Anyone knows why?
Note :
Even though I'm new to MVVM (just knew it yesterday), but I want to use as much as MVVM pattern as possible, so, I would like to avoid to use events (if that's possible, of course), since I read that it's not a good MVVM practice.
I think, whatever my MusicalNotation class look like, it'll not affect the solution of this problem at all, since it's just a 'Model'. But if it's needed, I am 100% willing to show you as well.
Using the MVVM pattern, you can bind keystrokes to your VM commands by using the InputBindings on the Main Window. A sample Xaml snippet looks like this...
<Window.InputBindings>
<KeyBinding Key="Right" Command="{Binding NavigateCommand}" CommandParameter="f"/>
<KeyBinding Key="Left" Command="{Binding NavigateCommand}" CommandParameter="b"/>
<KeyBinding Key="Delete" Command="{Binding NavigateCommand}" CommandParameter="delete"/>
<KeyBinding Key="F5" Command="{Binding GetAllCommand}"/>
</Window.InputBindings>
The KeyBinding class takes several parameters: the key itself, which is an enumeration, along with any modifiers like CTRL, ALT, and SHIFT.
The two parameters of immediate interest are the Command parameter, which gets binded to the VM, and the CommandParameter, which appears as the argument in your command delegates.
In this example, the Xaml is binding three different keystrokes to the NavigateCommand and using the CommandParameter to let the VM know which key was pressed.
The docs for InputBindings are here http://msdn.microsoft.com/en-us/library/system.windows.input.inputbinding(v=vs.110).aspx with more samples of usage.
Note: the usage assumes that the DataContext of the Window has been set to the VM instance that implements these commands, otherwise there will be a data binding error.
Your implementation would look something like this...
<Window.InputBindings>
<KeyBinding Key="A" Command="{Binding KeyBoardHotKeyCommand}" CommandParameter="N1"/>
</Window.InputBindings>
With the ICommand delegates like...
private void ExecuteKeyboardHotKeyCommand(object obj)
{
Notes note;
if (obj!=null && Enum.TryParse(obj.ToString(), out note))
{
Console.WriteLine(#"User pressed {0}", note);
}
}
private bool CanExecuteKeyboardHotKeyCommand(object obj)
{
return true;
}
Your 'Notes' enumeration is fine. Your ICommand delegates are along the right track for MVVM, but need to be abstracted away from Keyboard.IsKeyDown because these references cannot be easily tested. Well written View Models are hardware agnostic, and do not really care if the event occurred on the keyboard or some other device (like a touch screen).
For your last question, I use the Josh Smith's RelayCommand. It looks like this...
public class RelayCommand : ICommand
{ //http://msdn.microsoft.com/en-us/magazine/dd419663.aspx
public RelayCommand(Action<object> execute, Predicate<object> canExecute)
{
_execute = execute;
_canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
return _canExecute(parameter);
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public void Execute(object parameter)
{
_execute(parameter);
}
private readonly Action<object> _execute;
private readonly Predicate<object> _canExecute;
}
CanExecuteChanged is an event that the WPF binding engine (or some other exogenous class) can subscribe to. An implementation looks like this...
public ICommand KeyBoardHotKeyCommand { get; set; }
public ViewModel()
{
KeyBoardHotKeyCommand = new RelayCommand(ExecuteKeyboardHotKeyCommand, CanExecuteKeyboardHotKeyCommand);
}
private void ExecuteKeyboardHotKeyCommand(object obj)
{
Notes note;
if (obj!=null && Enum.TryParse(obj.ToString(), out note))
{
Console.WriteLine(#"User pressed {0}", note);
}
}
private bool CanExecuteKeyboardHotKeyCommand(object obj)
{
return true;
}
Lastly, for your question about Reed's implementation, Reed will probably be along when members in the US wake up. But for the moment, his ActionCommand (now part of Expression Studio http://msdn.microsoft.com/en-us/library/microsoft.expression.interactivity.core.actioncommand(v=expression.40).aspx
) is fundamentally the same as Josh Smith's RelayCommand. They are all using the same principles to implement the ICommand interface.
I'm trying to understand a little better the MVVM design pattern for WPF developing and this is a basic question in this subject.
Let's say I got the model implemented and it got a method which does the main action. In the View i want to create a button which will activate this action when pressing it. In order to do so I need to associate the Click event with an event handler, which actually is only suppose to call the model method.
The problem is that as I understand, the view doesn't even suppose to know the model. So how can I make the button in the View do the action I want?
That's where the view model comes in. First you should consider using Commands instead of event handlers. With Commands you can bind the "action" to the button instead of hard coding an event to the Click event. Like this:
<Button Command="{Binding Path=ActionCommand}"/>
Now your view model have to have a property that implements ICommand. There are a lot of implementations for this, e g RelayCommand in MVVM Light Toolkit. Through this property you call your models action. This is done by a reference to the model that your view model has. The reference to the model could be set through dependency injection or just supplying it at the creation of the view model.
Simple View model class:
public class ViewModel : INotifyPropertyChanged
{
private Model _model;
private ICommand _actionCommand;
public ViewModel(Model model)
{
_model = model;
_actionCommand = new RelayCommand(ExecuteAction);
}
public ICommand ActionCommand
{
get { return _actionCommand; }
}
private void ExecuteAction()
{
_model.Action();
}
}
This means that your view doesn't really know the type of the ViewModel, just that it has a Command-property called ActionCommand. To set the Views view model you use the View.Datacontext. This can be done in several different ways. Dependency injection could be used here too. Another solution is to use a ViewModelLocator, that uses the Locator pattern to connect the view to its ViewModel.
In your Model you have your function:
class MainWindowModel
{
public void MyAction()
{...}
}
In the constructor of your ViewModel you create an instance of your model like:
class MainWindowViewModel
{
private readonly MainWindowModel mainWindowModel;
public MainWindowViewModel()
{
this.mainWindowModel = new MainWindowModel();
}
Then you have an implementation of ICommand like RelayCommand:
public class RelayCommand : ICommand
{
private readonly Action<object> execute;
private readonly Predicate<object> canExecute;
public RelayCommand(Action<object> exectue, Predicate<object> canExecute = null)
{
if (exectue == null)
throw new ArgumentNullException("exectue");
this.execute = exectue;
this.canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
return this.canExecute == null || this.canExecute(parameter);
}
public void Execute(object parameter)
{
this.execute(parameter);
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
}
so. now you have a property in your ViewModel:
private ICommand myCommand;
public ICommand MyCommand
{
get { return this.myCommand; }
set
{
this.myCommand = value;
OnPropertyChanged();
}
}
The OnPropertyChanged-Event you get, when you implement the INotifyPropertyChanged-Interface
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
In your constructor of your ViewModel you instanziate the MyCommand like
this.MyCommand = new RelayCommand(MyCommandExecute);
Then you have to create a method in your viewmodel where you call the MyAction-Method of your model:
public void MyCommandExecute(object parameter)
{
this.mainWindowModel.MyAction();
}
In your xaml you have to set the DataContext like:
<Window.DataContext>
<viewModel:MainWindowViewModel/>
</Window.DataContext>
The small viewModel is the Namespace of your ViewModel. This you have to add in the Window-Definition like:
xmlns:viewModel="clr-namespace:TestApplication.ViewModel"
Now you can bind your button-Command to the ICommand-Property of your ViewModel like:
You need to use the ICommand interface, and supply an implementation, for which I suggest that you read this article on MSDN.
In your ViewModel, you would create an instance of ICommand, e.g. ButtonClick, which would look something like (based on RelayCommand):
public class ViewModel
{
public ViewModel()
{
this.ButtonClick = new RelayCommand(_ => this.DoSomething());
}
public ICommand ButtonClick { get; set; }
public void DoSomething()
{
// Something...
}
}
Then in your xaml you would bind to ButtonClick:
<Button Text="Click" Command="{Binding ButtonClick}" />
I am new to C# and I need to create simple binding command for one button. I have been reading a lot of articles for last hours but it simply got me more confused.
I have an WPF window (let's say Window1) where I have button "AddCustomer". What is the simplest way to create command for it? (By simplest I mean easy to understand)
In every article they were doing it differently. I need you to show me also how to bind it in xaml, more detailed the better... like I said, I am new.
Thank you for your help.
below is the complete solution for commands in WPF.
first create a class for execute the command.
public class RelayCommand : ICommand
{
readonly Action<object> _execute;
readonly Predicate<object> _canExecute;
private Action<object> _action;
private bool _canSave;
public RelayCommand(Action<object> execute) : this(execute, null) { }
public RelayCommand(Action<object> execute, Predicate<object> canExecute)
{
if (execute == null) throw new ArgumentNullException("execute");
_execute = execute;
_canExecute = canExecute;
}
public RelayCommand(Action<object> action, bool CanSave)
{
this._action = action;
this._canSave = CanSave;
}
[DebuggerStepThrough]
public bool CanExecute(object parameter)
{
return _canExecute == null || _canExecute(parameter);
}
public event EventHandler CanExecuteChanged
{
add
{
CommandManager.RequerySuggested += value;
}
remove
{
CommandManager.RequerySuggested -= value;
}
}
public void Execute(object parameter)
{
_execute(parameter);
}
}
below is the ViewModel
public FilterViewModel()
{
private RelayCommand _commandSave;
public ICommand Save
{
get {
return _commandSave ?? (_commandSave =
new RelayCommand(param => SaveMethod(param), CanSave));
}
}
private void SaveMethod
{
//code for save
}
private Predicate<object> CanSave
{
get { return o => true; }
}
}
and finally using the command in XAML.
<Button x:Name="btnSave" Content="Save" Command="{Binding Save}" CommandParameter="PASS-PARAMETER-HERE" ></Button>
Here's my take on it, the following is 'simplest' because you are leveraging the Prism library and so the amount of code you write is small. Use the nuget manager to add Prism to your project if you're not already using it ...
The xaml:
<Button Command="{Binding AddCustomerCommand}" Content="Add Customer"/>
In your viewmodel:
(1) Declare your command:
public ICommand AddCustomerCommand{ get; private set; }
(2) Define your command:
AddCustomerCommand= new DelegateCommand(AddCustomer);
(3) And create the method:
private void AddCustomer()
{
//do your logic here
}
Extended version:
You can pass a parameter from the xaml:
<Button Command="{Binding AddCustomerCommand}" CommandParameter={Binding SelectedItem, ElementName=MySelectorThingy} Content="Add Customer"/>
Remember to change the signature of your delegate and method:
AddCustomerCommand= new DelegateCommand<WhateverMyTypeIs>(AddCustomer);
private void AddCustomer(WhateverMyTypeIs selectedThing)
{
//do your logic here
}
You can also define in the DelegateCommand when the button should be available (CanExecute), like the following:
public DelegateCommand AddCustomerCommand{ get; private set; }
AddCustomerCommand = new DelegateCommand(AddCustomer, AddCustomer_CanExecute);
And then define the method for deciding whether you can execute or not:
private bool AddCustomer_CanExecute()
{
if (DateTime.Now.DayOfWeek.Equals(DayOfWeek.Monday))
return true;
else
return false;
}