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; }
}
Related
The case is that I try to disable a button in the window form when it was clicked and after some time (some seconds) it should be enabled again.
But this didn't work. After a click on the button the command set the enabled to false and after some seconds the command set it back to true (I tested it, the order is right and it set it to true again) but the button is still not enabled on the window form.
For that case I use a RelayCommmand. The RelayCommand is a standard class you find on Internet and will be shown in the end.
To organise the command I wrote a class called Testclass:
class Testclass
{
private bool _testValueCanExecute;
public bool TestValueCanExecute
{
get { return _testValueCanExecute; }
set
{
_testValueCanExecute = value;
OnPropertyChanged();
}
}
public ICommand TestValueCommand { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
public Testclass()
{
TestValueCommand = new RelayCommand(TestMethod, param => _testValueCanExecute);
TestValueCanExecute = true;
}
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private async void TestMethod(object obj)
{
TestValueCanExecute = false;
await Task.Delay(3000);
TestValueCanExecute = true;
}
}
In the XAML File I added a button as followed:
<Button x:Name="TestButton" Command="{Binding TestValueCommand}" Content="Test Button" HorizontalAlignment="Left" Margin="149,96,0,0" VerticalAlignment="Top" Width="75"/>
The MainWindow code looks as followed:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new Testclass();
}
}
So the RelayCommand use the TestMethod method set the command enable variable to false, wait 3 seconds and set them back to true. But as I wrote above the button on the window form still not enabled.
It would be nice to understand what happens here and how I can solve this.
Update:
I use the following Code for the RelayCommand:
public class RelayCommand : ICommand
{
private Action<object> execute;
private Func<object, bool> canExecute;
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public RelayCommand(Action<object> execute, Func<object, bool> canExecute = null)
{
this.execute = execute;
this.canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
return this.canExecute == null || this.canExecute(parameter);
}
public void Execute(object parameter)
{
this.execute(parameter);
}
}
The RelayCommand is a standard class you find on Internet ...
There is no such thing as a "standard class you find on Internet". In fact there are several different implementations of the RelayCommand available "on the Internet".
A good implementation should contain a method for raising the CanExecuteChanged event. MvvmLight's implementation has a RaiseCanExecuteChanged() method that does this. You need to call this one to "refresh" the status of the command:
private async void TestMethod(object obj)
{
RelayCommand cmd = TestValueCommand as RelayCommand;
TestValueCanExecute = false;
cmd.RaiseCanExecuteChanged();
await Task.Delay(3000);
TestValueCanExecute = true;
cmd.RaiseCanExecuteChanged();
}
The event is not raised automatically when you set the TestValueCanExecute property and raise the PropertyChanged event for the view model.
Edit: Your implementation doesn't have any RaiseCanExecuteChanged() method. Add one to your RelayCommand class and call it as per above:
public void RaiseCanExecuteChanged()
{
CommandManager.InvalidateRequerySuggested();
}
I strongly recommend using existing frameworks, instead of inveting the wheel once again.
Take a look at ReactiveUI ReactiveCommand
In your case, it would do all the work by itself:
TestValueCommand = ReactiveCommand.CreateFromTask(async () => await Task.Delay(500));
You bind that command to a button in xaml and the button is disabled until command is done.
You can easily add another condition for disabling the command, and then, binding will disable button
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);
}
}
I'm creating a small WPF application. I have to follow MVVM pattern to which I'm new.
I have two views(A,B) and two viewmodels.
This is my scenario, I have a radiobutton in window A. After checking the radiobutton, I click next in window A. The event will close Window A and open window B.
In the constructor of viewmodel of window B, I need to know if Radiobutton in window A is checked or not. How do I do this?
If you "have to follow MVVM pattern", you "have to" do the following.
First, you need create two ViewModels, for view A and for view B (but if you want - you can left only one ViewModel). For ViewModelA you should create ICommand property, that you bind with next button, and bool property IsChecked. ViewModelB should either contain a constructor, which will accept the bool parameter, or a property, which should be updated before view B will show.
Here the example of how it can be accomplished:
public class ViewModelA : ViewModelBase
{
public ViewModelA()
{
_nextCommand = new Cmd(this);
}
public ICommand NextCommand { get { return _nextCommand; } }
public bool IsChecked
{
get { return _isChecked; }
set
{
_isChecked = value;
RaisePropertyChanged("IsChecked");
}
}
private ICommand _nextCommand;
private bool _isChecked;
}
In the *.xaml view for A you should have the following:
<RadioButton IsChecked="{Binding Path=IsChecked}" />
<Button Content="Next" Command="{Binding Path=NextCommand}" />
Please note: DataContext for that view should be of type ViewModelA.
For ViewModelB:
public class ViewModelB : ViewModelBase
{
private bool _isAChecked;
public bool IsAChecked
{
get
{
return _isAChecked;
}
set
{
_isAChecked = value;
RaisePropertyChanged("IsAChecked");
}
}
}
public class Cmd : ICommand
{
ViewModelA _vmA;
public Cmd(ViewModelA vmA)
{
_vmA = vmA;
}
public bool CanExecute(object parameter)
{
return true;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
var vmB = new ViewModelB();
vmB.IsAChecked = _vmA.IsChecked;
// after that create ViewB, and set its DataContext to vmB
}
}
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
Instead of creating for each command its class, you can see the examples of the RelayCommand, or DelegateCommand, which are approximately do the same things.
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}" />