How to disable/enable commands using mvvm command pattern - c#

I'm used to developing WPF applications by setting up events in xaml and linking to code behind on the MainWindow. This makes it really easy to enable and disable events as needed. I've recently been learning MVVM pattern and have developed an application which uses commands to handle events. The problem with this is I have no idea how to disable an event command.
My specific scenario is this:
I need to disable some filtering events when data is loading or refreshing so that they don't continuously fire... The data is already being bound so no filtering events should be happening.
In the past, I have done it something like this:
listView.SelectionChangedEvent -= new SelectionChangedEvent(event_method);
//Perform actions...
listView.SelectionChangedEvent += new SelectionChangedEvent(event_method);
My understanding with MVVM is that I can use RelayCommand or DelegateCommand and pass in a predicate which should disable the command based on a boolean value. Then I just need to call RaiseCanExecuteCommandChanged() to update the Command after I have changed the boolean value. However, all of my tests to make this work just plain don't work...
Can anyone post a brief example of how to handle what I have described in MVVM? All the examples I can find are for enabling/disabling controls such as Button ... I haven't been able to find anything to actually disable a Command from executing...

To ensure that a command will not be executed if CanExecute evaluates to false, you can write a decorator:
public class LimitExecuteIfCanCommandDecorator : CommandDecoratorBase
{
public LimitExecuteIfCanCommandDecorator( ICommand command ) : base( command )
{
}
public override void Execute( object parameter )
{
if ( CanExecute(parameter) ) // check if it evaluates to true
{
base.Execute( parameter );
}
}
}
public abstract class CommandDecoratorBase : ICommand
{
protected CommandDecoratorBase(ICommand command)
{
_command = command;
}
private readonly ICommand _command;
public event EventHandler CanExecuteChanged
{
add
{
_command.CanExecuteChanged += value;
}
remove
{
_command.CanExecuteChanged -= value;
}
}
public virtual bool CanExecute( object parameter )
{
return _command.CanExecute( parameter );
}
public virtual void Execute( object parameter )
{
_command.Execute( parameter );
}
}
Now use that decorator to wrap all your commands.

Related

Is a command a sort of event handler or why does my button execute the command?

I'm learning about wpf, delegates, event and I have some clue on what does what, but I'm a little lost when it comes to implementing ICommand
I have a class the implements the ICommand interface like this
class RelayCommand : ICommand
{
private Action<object> _execute;
private Func<object, bool> _canExecute;
public RelayCommand(Action<object> execute) : this (execute, null)
{
}
public RelayCommand(Action<object> execute, Func<object,bool> canExecute)
{
this._execute = execute;
this._canExecute = canExecute;
}
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter)
{
//throw new NotImplementedException();
return true;
}
public void Execute(object parameter)
{
//throw new NotImplementedException();
this._execute(parameter);
}
public void OnCanExecute()
{
if (CanExecuteChanged != null)
{
CanExecuteChanged(this, EventArgs.Empty);
}
}
And then my ViewModel that uses i.
class PersonViewModel
{
public ICommand ICommandPresenter { get; set; }
public PersonModel PM;
private string _btnString = "";
#region Propertys
public string ButtonString
{
get {return _btnString; }
set
{
if (_btnString.Equals(value))
{
return;
}
_btnString = value;
}
}
public string Name { get { return PM.Name; } set
{
PM.Name = value;
}
}
#endregion Propertys
public PersonViewModel()
{
PM = new PersonModel();
ICommandPresenter = new RelayCommand(ChangeName);
}
public void ChangeName(object a)
{
string s = a as string;
MessageBox.Show("Hello from PersonViewModel Commander: " + s);
}
}
This is where it gets confusing for me. In the RelayCommand class I have an event CanExecuteChanged but that event is never executed/fired. From what I've understood from my reading on events is that you don't "need" to have subscribers to an event, but if you're going to have an event you should atleast have somewhere in the code that executes it. But I don't have that in my code but for some reason my button still does what I command it to do. I still understand that I've clicked the button but I don't have anything in my code that is subscribed to that button.
Why is my button able to execute my code even though I don't have an event connected to it?
Since I don't have any subscribers connected to CanExecuteChanged event does it becomes useless?
Is the command acting like an event? if so, please describe the whole process from clicking the button to executing the command.
CanExecuteChanged is member of ICommand class and, simplifying the things, is used by wpf framework to enable/disable your button depending on result of CanExecute() method. CanExecute is not tight to the code you want to execute when you click the button, but to the condition, when it's legal to be done.
Command executes your code, because you send pointer to your method(ChangeName method) here:
ICommandPresenter = new RelayCommand(ChangeName);
So you are not using CanExecuteChange at all, because you are invoking this constructor:
public RelayCommand(Action<object> execute).
To have CanExecute you have to invoke overloaded constructor that accepts CanExecute predicate:
public RelayCommand(Action<object> execute, Func<object,bool> canExecute)
And to invoke it just pass some function that returns a bool as second parameter:
ICommandPresenter = new RelayCommand(ChangeName, ()=>MyCustomLogicWhenButtonIsActive());
Based on what I saw in .Net's source code, the command assigned to the button's Command property is executed in the following way:
Clicking the button calls the button's OnClick() method (located in the button's ButtonBase base class).
The OnClick() method (see source here) calls the Commands.CommandHelpers.ExecuteCommandSource() method, passing this as the command source parameter.
The ExecuteCommandSource() (see source here) further calls the CriticalExecuteCommandSource() method, passing the same command source as a parameter.
Finally, the CriticalExecuteCommandSource() method (see source here) accesses the command source's Command member, and:
checks if the command's CanExecute() method returns true,
if it's true, it calls the command's Execute() method.
(If you use a RelayCommand implementation, then obviously, the RelayCommand class relays the call to the specific method you passed to its constructor when you instantiated it.)
So, to answer your question whether commands are fired through an event:
The source indicates that the OnClick() method executes the command directly, not through event handling. As you can see in the source, this method does raise the Click event, but it does so separately from the command execution.
Though, it remains unanswered how the OnClick() method is being called in the first place by the framework; I have no idea about that.

Windows Store App (8.1): INotifyPropertyChanged Must Be Top Level?

Why does this:
public class ViewModel : INotifyPropertyChanged
{
private string _bp;
public event PropertyChangedEventHandler PropertyChanged;
public string BindableProperty
{
get { return _bp; }
set
{
_bp = value;
NotifyPropertyChanged();
}
}
protected void NotifyPropertyChanged( [CallerMemberName] string caller = "" )
{
var eventHandler = PropertyChanged;
if( eventHandler != null )
eventHandler( this, new PropertyChangedEventArgs( caller ) );
}
}
bind correctly in a Windows Store app Page (i.e., the binding target is listening to the PropertyChanged event, as can be verified by breaking in NotifyPropertyChanged and seeing that eventHandler is not null). But this:
public interface IViewModel : INotifyPropertyChanged
{
string BindableProperty { get; set; }
}
public class ViewModel : IViewModel
{
private string _bp;
public event PropertyChangedEventHandler PropertyChanged;
public string BindableProperty
{
get { return _bp; }
set
{
_bp = value;
NotifyPropertyChanged();
}
}
protected void NotifyPropertyChanged( [CallerMemberName] string caller = "" )
{
var eventHandler = PropertyChanged;
if( eventHandler != null )
eventHandler( this, new PropertyChangedEventArgs( caller ) );
}
}
fails to bind (i.e., eventHandler is always null, and the binding target never reflects updates to BindableProperty).
I read someplace that since WinRT isn't managed code it doesn't support reflection. So instead some component of the OS builds a hidden object, when a managed app starts, storing type information which would otherwise be obtained through reflection. The behavior I'm noticing looks like whatever type scanning process is used isn't smart enough to trace back through a chain of derived interface definitions.
So is my theory correct? Or am I missing something else which is causing the problem?
Edit
Thanks, Rob, for replying. To provide a bit more context, I am updating the properties within an asynchronous method called from within a couple of asynchronous event handlers (e.g., private async void navigationHelper_LoadState( object sender, LoadStateEventArgs e ), private async void btnSeek_Click( object sender, RoutedEventArgs e ).
More importantly, when I went back and re-ran the what I thought was the same code this morning everything worked as expected, even with the "dervied" interface definition. So obviously I changed something along the way that I don't remember.
Given my recent experience I now suspect I'm having a problem with the way I'm writing and calling various asynchronous methods. It's a new area to me, and I've noticed all sorts of run-time difficulties if, for example, one forgets to "await" an asynchronous operation. The app will still compile, but the run-time behavior is very different.

ICommand CanExecute not triggering after PropertyChanged?

I got a WPF application that shows a button bound to a command like that:
<Button Command="{Binding Path=TestrunStartCommand}" Content="GO!">
The command is defined like that:
public ICommand TestrunStartCommand
{
get { return new RelayCommand(TestrunStartExecute, () => !IsTestrunInProgress); }
}
public bool IsTestrunInProgress
{
get{
return _isTestrunInProgress;
}
set{
_isTestrunInProgress = value;
RaisePropertyChanged(IsTestrunInProgressPropertyName);
}
}
The problem is, the button won't be enabled immediately after I set IsTestrunInProgress to false, but only after I click inside the application window.
Could you help me understand this behaviour and show me how to fix this?
Further reading:
wpf command pattern - when does it query canexecute
The ICommand interface exposes an event ICommand.CanExecuteChanged which is used to inform the UI when to re-determine the IsEnabled state of command driven UI components.
Depending upon the implementation of the RelayCommand you are using, you may need to raise this event; Many implementations expose a method such as RelayCommand.RaiseCanExecuteChanged() which you can invoke to force the UI to refresh.
Some implementations of the RelayCommand make use of CommandManager.RequerySuggested, in which case you will need to call CommandManager.InvalidateRequerySuggested() to force the UI to refresh.
Long story short, you will need to call one of these methods from your property setter.
Update
As the state of the button is being determined when the active focus is changing, I believe the CommandManager is being used. So in the setter of your property, after assigning the backing field, invoke CommandManager.InvalidateRequerySuggested().
Update 2
The RelayCommand implementation is from the MVVM light toolkit. When consumed from WPF/.NET, the implementation wraps the methods and events exposed from the CommandManager. This will mean that these commands work automagically in the majority of situations (where the UI is altered, or the focused element is changed). But in a few cases, such as this one, you will need to manually force the command to re-query. The proper way to do this using this library would be to call the RaiseCanExecuteChanged() method on the RelayCommand.
This is so important and easy to miss, I am repeating what #Samir said in a comment. Mr Laurent Bugnion wrote in his blog:
In WPF 4 and WPF 4.5, however, there is a catch: The CommandManager will stop working after you upgrade MVVM Light to V5. What you will observe is that your UI elements (buttons, etc) will stop getting disabled/enabled when the RelayCommand’s CanExecute delegate returns false.
If you are in a hurry, here is the fix: In any class that uses the RelayCommand, replace the line saying:
using GalaSoft.MvvmLight.Command;
with:
using GalaSoft.MvvmLight.CommandWpf;
You can try with CommandManager.InvalidateRequerySuggested.
Anyway this did not help me sometimes in the past. For me the best solution turned out to be to bind the boolean property to the Button.IsEnabled dependency property.
In your case something like
IsEnabled={Binding IsTestrunInProgress}
The issue is, the ICommand Property TestrunStartCommand is always returning a new command object whenever it is accessed.
A simple fix is to create the ICommand object once and use it again and again.
private ICommand _testRunCommand = null;
public ICommand TestrunStartCommand
{
get
{
return _testRunCommand ?? (_testRunCommand = new RelayCommand(TestrunStartExecute, () => !IsTestrunInProgress));
}
}
This was quite a simple fix and it worked for me.
Addition to Riegardt Steyn's answer above: https://stackoverflow.com/a/33503341/1964969
If you don't want to change Command to CommandWpf usage (as that two RelayCommand versions are not compatible inbetween), another workaround could be to not instantiate a command at the declaration place. Use constructor code instead:
public class SomeVMClass
{
// CanExecute won't work:
// Declaration and instantiation same place
public RelayCommand MyCommand1 => new RelayCommand(MyBusinessLogic, MyCanExecuteValidator);
// CanExecute will work
// Declaration only
public RelayCommand MyCommand2 { get; private set; }
public SomeVMClass()
{
// Let's instantiate our declared command
MyCommand2 = new RelayCommand(MyBusinessLogic, MyCanExecuteValidator);
...
Blockquote
In your Command class change CanExcutedChanged to this
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
This is example of my command class
public class SaveConfigCommand : ICommand
{
public MyViewModel VM { get; set; }
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public SaveConfigCommand(MyViewModel vm)
{
VM = vm;
}
public bool CanExecute(object? parameter)
{
MyObjectModel model = parameter as MyObjectModel;
if (model == null)
return false;
// Validate others properties here
return true;
}
public void Execute(object? parameter)
{
VM.MyMethodInViewModel();
}
}

CanExecute Logic for DelegateCommand

Update: The focus became MVVM instead of the actual question so I'm updating it.
I'm having a problem with CanExecute for DelegateCommand. It doesn't update before I call RaiseCanExecuteChanged, is this the desired behavior?
I uploaded a simple sample project reproducing this problem here : http://dl.dropbox.com/u/39657172/DelegateCommandProblem.zip
The problem is this, I have two Buttons like this. One is Binding Command to a RelayCommand implementation and the other is binding to the Prism implementation of DelegateCommand
<Button Command="{Binding DelegateSaveCommand}"/>
<Button Command="{Binding RelaySaveCommand}"/>
The ViewModel ICommands
DelegateSaveCommand = new DelegateCommand(Save, CanSaveDelegate);
RelaySaveCommand = new RelayCommand(param => Save(), param => CanSaveRelay);
and the CanExecute method/predicate
public bool CanSaveDelegate()
{
return HasChanges;
}
public bool CanSaveRelay
{
get { return HasChanges; }
}
Both are using the property HasChanges. When HasChanges is updated, only the CanSaveRelay updates. Is this the way it's meant to be?
As it already was mentioned, this is intended behavior of DelagateCommand, not a bug.
DelegateCommand doesn't raise CanExecuteChanged event automatically, you have to raise that event manually by calling RaiseCanExecuteChanged when appropriate. Whereas RelayCommand relays on CommandManager.RequerySuggested event for that. This event is raised every time the user clicks somewhere or presses a button.
For situations when it is not very convenient or there is no appropriate place for calling RaiseCanExecuteChanged (like in your scenario you have to subscribe to PropertyChanged event on the model, etc) I have created the following simple wrapper that ensures that the CanExecute method of the wrapped command is executed automatically on CommandManager.RequerySuggested event:
public class AutoCanExecuteCommandWrapper : ICommand
{
public ICommand WrappedCommand { get; private set; }
public AutoCanExecuteCommandWrapper(ICommand wrappedCommand)
{
if (wrappedCommand == null)
{
throw new ArgumentNullException("wrappedCommand");
}
WrappedCommand = wrappedCommand;
}
public void Execute(object parameter)
{
WrappedCommand.Execute(parameter);
}
public bool CanExecute(object parameter)
{
return WrappedCommand.CanExecute(parameter);
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
}
You can use it like this:
DelegateSaveCommand = new AutoCanExecuteCommandWrapper(new DelegateCommand(Save, CanSaveDelegate));
If you want to stick to DelegateCommand you can use ObservesCanExecute:
DelegateSaveCommand = new DelegateCommand(Save, CanSaveDelegate).ObservesCanExecute(CanSaveDelegate);
Note that there is also ObservesProperty available if you are using a property for your CanExecute check. But then your property has to call NotifyPropertyChanged.
There is a bug in the DelegateCommand provided by Prism which doesn't raise the CanExecute event. I beat my head against the wall for a day until I dove into the DelegateCommand class provided by the Prism framework. I don't have the code with me, but I can post my resolution in a bit.
The alternative is to use one of the other RelayCommand frameworks out there.
Edit
Rather than reposting the code, there are other SO questions that provide resolutions:
WPF-Prism CanExecute method not being called
And Kent B. has a good article: MVVM Infrastructure: DelegateCommand

Calling Event after Command Executes

I am looking for a way to be able to have an event run after a command executes. I am working with EditingCommands (ToggleBold, ToggleItalic...ect.) and would like to be able to have a method called directly after the command finishes whatever it is doing.
An example would be that I have some text selected and press Ctrl+B and that executes EditingCommands.ToggleBold. Right after the text is toggled I want to call an method that will update a ToggleButton that is connected with the selection FontWeight.
I tried using the Executed event but that is unfortunately for me called before the text is affected and consequently updates the button with information that will change in just a second. Does anyone know a way around this?
A workaround is to queue another message in your Executed handler:
void copy_Executed(object sender, EventArgs e)
{
Dispatcher.BeginInvoke(new ThreadStart(delegate()
{
//do update of bold button here
}), null);
}
This will ensure your work is added to the back of the queue, and will be executed after other messages of the same or higher priority.
However, I'd like to suggest a better solution. If you think about it, the bold button is responsible for executing two different commands: make bold, and make normal. It switches between these two commands based on the currently selected text/caret position. Therefore, you could write a custom ICommand implementation that encapsulates two sub-commands (completely untested code):
public class TogglingCommand : ICommand
{
private readonly ICommand _command1;
private readonly ICommand _command2;
private ICommand _activeCommand;
public TogglingCommand(ICommand command1, ICommand command2)
{
_command1 = command1;
_command2 = command2;
}
public ICommand ActiveCommand
{
get { return _activeCommand; }
}
public bool CanExecute(object parameter)
{
if (_command1.CanExecute(parameter))
{
_activeCommand = _command1;
}
else if (_command2.CanExecute(parameter))
{
_activeCommand = _command2;
}
else
{
_activeCommand = null;
}
return _activeCommand != null;
}
public void Execute(object parameter)
{
_activeCommand.Execute(parameter);
}
}
You can then construct a TogglingCommand with two commands: one to bolden and one to unbolden text. Then you can bind the Button in your UI to the ActiveCommand property to change it in anyway you like based on what will happen when you click the command. For example, if you're using a ToggleButton you would bind IsChecked to ActiveCommand and convert to true is the active command is unbolden. Of course, the bolden and unbolden commands need CanExecute logic of their own that inspects the selected text.
Can you use the Executed (past tense) Routed Event handler? (Or maybe that's what you're saying you tried)
public partial class CustomerWindow : Window
{
public CustomerWindow()
{
InitializeComponent();
CommandBinding binding = new CommandBinding(ApplicationCommands.Copy);
binding.Executed += new ExecutedRoutedEventHandler(this.copy_Executed);
this.CommandBindings.Add(binding);
}
void copy_Executed(object sender, RoutedEventArgs e)
{
MessageBox.Show("Executed the Copy command");
}
}

Categories

Resources