Simple RelayCommand defined as follows:
public class RelayCommand : IDelegateCommand
{
readonly Predicate<object> _canExecute;
readonly Action<object> _execute;
public RelayCommand(Predicate<object> canExecute, Action<object> execute)
: this(canExecute, execute, true)
{
}
public RelayCommand(Predicate<object> canExecute, Action<object> execute, bool isCommandAllowed)
{
_canExecute = canExecute;
_execute = execute;
IsAllowed = isCommandAllowed;
}
public void RaiseCanExecuteChanged()
{
CanExecuteChanged(this, EventArgs.Empty);
}
#region ICommand Members
public virtual bool CanExecute(object parameter)
{
return _canExecute(parameter);
}
public event EventHandler CanExecuteChanged = delegate { };
public virtual void Execute(object parameter)
{
_execute(parameter);
}
IDelegateCommand is defined as follows:
public interface IDelegateCommand : ICommand
{
void RaiseCanExecuteChanged();
}
Is bound to a WPF Button as follows:
<Button Command="{Binding StartSimulationCommand}" Content="Start Simulation"/>
And corresponding ViewModel usage of the Command is given as follows:
public class MainViewModel
{
// missing irrelevant bits
public ICommand StartSimulationCommand
{
get { return new RelayCommand(arg => true, arg =>
{
var inputValidationResponse = ValidateInputs();
if (!string.IsNullOrEmpty(inputValidationResponse))
{
_logger.Error(inputValidationResponse);
return;
}
// this method opens a websocket and if that operation is
// successful, property called IsWebSocketOpen is updated.
OpenWebSocketChannel();
// update command states
StopSimulationCommand.RaiseCanExecuteChanged();
});
}
}
public RelayCommand StopSimulationCommand
{
get { return new RelayCommand(arg => IsWebSocketOpened, arg => { CloseWebSocketChannel(); }); }
}
private bool _isWebSocketOpened;
public bool IsWebSocketOpened {
get { return _isWebSocketOpened; }
set { SetField(ref _isWebSocketOpened, value, "IsWebSocketOpened"); }
}
}
When I invoke StopSimulationCommand.RaiseCanExecuteChanged, the state of the button bound to StopSimulationCommand does not changed to enabled, even through the predicate for that command now returns true.
What did I miss?
You are creating a new instance of the RelayCommand in the setter of the StopSimulationCommand. You should create the command once in the constructor of the view model class:
public class MainViewModel : INotifyPropertyChanged
{
public MainViewModel ()
{
StopSimulationCommand = new RelayCommand(arg => IsWebSocketOpened, arg => { CloseWebSocketChannel(); });
}
public RelayCommand StopSimulationCommand { get; private set; }
}
Related
I want to enable RibbonButton when textbox property text isn't null. Disable RibbonButton when textbox property text is null. I want to use CanExecute method in ICommand for it. How can I do it?
View:
<Custom:RibbonButton
LargeImageSource="..\Shared\img\save_diskete.png"
Label="Save"
Command="{Binding ButtonCommand}">
</Custom:RibbonButton>
ViewModel
class KomentarViewModel:BaseViewModel
{
#region Data
private ICommand m_ButtonCommand;
public ICommand ButtonCommand
{
get
{
return m_ButtonCommand;
}
set
{
m_ButtonCommand = value;
}
}
private string textKomentar;
public string TextKomentar
{
get
{
return this.textKomentar;
}
set
{
// Implement with property changed handling for INotifyPropertyChanged
if (!string.Equals(this.textKomentar, value))
{
textKomentar = value;
OnPropertyChanged("TextKomentar");
}
}
}
private ObservableCollection<Komentar> allCommentsInc;
public ObservableCollection<Komentar> AllCommentsInc
{
get
{
return allCommentsInc;
}
set
{
allCommentsInc = value;
OnPropertyChanged("AllCommentsInc");
}
}
public int idIncident { get; private set; }
public Incident incident { get; private set; }
#endregion
#region Constructor
public KomentarViewModel(int id)
{
CC_RK2Entities context = new CC_RK2Entities();
this.idIncident = id;
AllCommentsInc = new ObservableCollection<Komentar>(context.Komentar.Where(a => a.Incident_id == idIncident));
incident = context.Incident.Where(a => a.id == idIncident).First();
//ButtonCommand = new RelayCommand(new Action<object>(ShowMessage));
}
#endregion
#region Methods
//ukaz napsany text
public void ShowMessage(object obj)
{
//MessageBox.Show(obj.ToString());
MessageBox.Show(this.TextKomentar);
}
}
RelayCommand
namespace Admin.Shared.Commands
{
class RelayCommand : ICommand
{
private Action<object> _action;
public RelayCommand(Action<object> action)
{
_action = action;
}
#region ICommand Members
public bool CanExecute(object parameter)
{
return true;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
_action(parameter);
}
#endregion
}
}
You need to modify your RelayCommand class like this
class RelayCommand : ICommand
{
private Action<object> _action;
private Func<bool> _func;
public RelayCommand(Action<object> action,Func<bool> func)
{
_action = action;
_func = func;
}
public void RaiseCanExecuteChanged()
{
if(CanExecuteChanged!=null)
CanExecuteChanged(this,new EventArgs());
}
#region ICommand Members
public bool CanExecute(object parameter)
{
if (_func != null)
return _func();
return true;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
_action(parameter);
}
#endregion
}
Initialize ButtonCommand as
ButtonCommand = new RelayCommand((s) => ShowMessage(s),()=>!string.IsNullOrEmpty(TextKomentar));
RaiseCanExcuteChanged from the setter of Text property
public string TextKomentar
{
get
{
return this.textKomentar;
}
set
{
// Implement with property changed handling for INotifyPropertyChanged
if (!string.Equals(this.textKomentar, value))
{
textKomentar = value;
OnPropertyChanged("TextKomentar");
}
ButtonCommand.RaiseCanExecuteChanged();
}
}
implement this for canexecute:
public bool CanExecute(object parameter)
{if(thistext available)
return true;
else
return false;
}
Since, CanExecuteChanged is raised when the CanExecute method of an ICommand gets changed. it gets invoked when some command that could change canexecute.
and can execute changed should be changed to this:
public event EventHandler CanExecuteChanged {
add {
CommandManager.RequerySuggested += value;
}
remove {
CommandManager.RequerySuggested -= value;
}
}
EDIT
in your view model constructor:
m_ButtonCommand= new RelayCommand(Submit, CanSubmit);
now method for this submit:
private bool CanSubmit(object obj)
{
if(thistext available)
return true;
else
return false;
}
public void Submit(object _)
{//... code}
public event EventHandler CanExecuteChanged {
add {
CommandManager.RequerySuggested += value;
}
remove {
CommandManager.RequerySuggested -= value;
}
}
do it like this.
In straightforward words, you need the following:
Let's first create our own delegate command:
public class DelegateCommand : DelegateCommandBase
{
private Action _executeMethod;
private Func<bool> _canExecute;
public DelegateCommand(Action executeMethod)
: this(executeMethod, () => true) {}
public DelegateCommand(Action executeMethod, Func<bool> _canExecute): base()
{
if (executeMethod == null || _canExecute == null) {
throw new ArgumentNullException(nameof(executeMethod),
Resources.DelegateCommandDelegatesCannotBeNull);
}
_executeMethod = executeMethod;
_canExecute = _canExecute;
}
public void Execute() => _executeMethod();
public bool CanExecute() => _canExecute();
protected override void Execute(object parameter) => Execute();
protected override bool CanExecute(object parameter) => CanExecute();
public DelegateCommand ObservesProperty<T>(Expression<Func<T>> propertyExpression)
{
ObservesPropertyInternal(propertyExpression);
return this;
}
public DelegateCommand ObservesCanExecute(Expression<Func<bool>> canExecuteExpression)
{
_canExecute = canExecuteExpression.Compile();
ObservesPropertyInternal(canExecuteExpression);
return this;
}
}
Here, DelegateCommandBase is actually from Prism.Commands namespace.
If you don't use Prism as an MVVM framework for WPF, you can create your own copy of DelegateCommandBase (look for the solution here).
In your View Model, create a member with type DelegateCommand and initialize it in the constructor:
public class MyViewModel
{
private DelegateCommand _okCommand;
public DelegateCommand OkCommand
{
get => _okCommand;
set => SetProperty(ref _okCommand, value);
}
public MyViewModel()
{
OkCommand = new PrismCommands.DelegateCommand(OkCommandHandler,
OkCanExecuteCommandHandler);
}
private void OkCommandHandler()
{
// ...
}
// This is important part: need to return true/false based
// on the need to enable or disable item
private bool OkCanExecuteCommandHandler() =>
return some_condition_to_enable_disable_item;
}
Note: make sure to raise execution changed event, every time something changes that can affect some_condition_to_enable_disable_item condition behavior.
For example, in the case of Prism, you can call RaiseCanExecuteChanged method once a change happens related to the condition (in our case OkCommand.RaiseCanExecuteChanged();).
Small hint: for Telerik WPF Controls, you need to call InvalidateCanExecute() instead of RaiseCanExecuteChanged().
Finally, our XAML will look like this:
<Button x:Name="btnOk"
Content="Ok"
Command="{Binding OkCommand}"/>
Last time I used Microsoft.Practices.Prism.Commands namesapce from Microsoft.Practices.Prism.dll. Class DelegateCommand has own RaiseCanExecuteChanged() method. So the benifit is you don't have to write yout own implementation of ICommand.
XAML:
<StackPanel>
<CheckBox IsChecked="{Binding IsCanDoExportChecked}" />
<Button Command="{Binding ExportCommand}" Content="Export" />
</StackPanel>
ViewModel:
public class ViewModel
{
public DelegateCommand ExportCommand { get; }
public ViewModel()
{
ExportCommand = new DelegateCommand(Export, CanDoExptor);
}
private void Export()
{
//logic
}
private bool _isCanDoExportChecked;
public bool IsCanDoExportChecked
{
get { return _isCanDoExportChecked; }
set
{
if (_isCanDoExportChecked == value) return;
_isCanDoExportChecked = value;
ExportCommand.RaiseCanExecuteChanged();
}
}
private bool CanDoExptor()
{
return IsCanDoExportChecked;
}
}
As title clearly defines, I want to run an Action in my View just before a command is executed in the ViewModel.
Scenario
Assume that we have a NavigationDrawer with two or more item types (e.g. NavigationItem and ExpandableItem). Each item was bound to a command on its corresponding ViewModel.
Now I want to:
Close the drawer before executing NavigationItem command.
Show an animation for the ExpandableItem before executing the command and do not close drawer.
I have tried the following approach:
Core Project
public class MvxInteractiveCommand : MvxCommandBase, IMvxCommand
{
private readonly Action _execute;
private readonly Func<bool> _canExecute;
public MvxInteractiveCommand(Action execute)
: this(execute, null)
{
}
public MvxInteractiveCommand(Action execute, Func<bool> canExecute)
{
_canExecute = canExecute;
_execute = execute;
Delay = 0;
}
/// <summary>
/// Delay in milliseconds to execute the action. Default is zero means immediate execution
/// </summary>
public int Delay { get; set; }
public object InteractData { get; set; }
public bool CanExecute(object parameter)
{
return _canExecute == null || _canExecute();
}
public bool CanExecute()
{
return CanExecute(null);
}
public void Execute(object parameter)
{
if (CanExecute(parameter))
{
Mvx.Resolve<IMvxMessenger>().Publish(new CommandExecutingMessage(this) { Data = InteractData });
if (Delay == 0)
_execute();
else
Task.Delay(200).ContinueWith(obj => _execute());
}
}
public void Execute()
{
Execute(null);
}
}
public class CommandExecutingMessage : MvxMessage
{
public CommandExecutingMessage(object sender)
: base(sender)
{
}
public object Data { get; set; }
}
NavigationSimpleItem.cs:
public class NavigationSimpleItem : MvxNavigatingObject
{
private readonly IMvxCommand _clickCommand;
public NavigationSimpleItem(string caption, int id, Action doCommand, Func<bool> canDoCommand = null)
{
...
if (doCommand == null)
throw new ArgumentNullException("doCommand");
_clickCommand = new MvxInteractiveCommand(doCommand, canDoCommand);
}
public override IMvxCommand ClickCommand
{
get { return _clickCommand; }
}
...
}
Droid Project
MainView.cs:
public class MainView : MvxActivity
{
private MvxSubscriptionToken _token;
public MainView()
{
}
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
SetContentView(Resource.Layout.MainView);
}
protected override void OnViewModelSet()
{
base.OnViewModelSet();
_token = Mvx.Resolve<IMvxMessenger>().SubscribeOnMainThread<CommandExecutingMessage>(e =>
{
DrawerWrapper.CloseDrawer();
});
}
}
Notes: As you can see, I use Messenger Plugin to interact between the View and the ViewModel. It works fine with my first need, which is to run a simple Action on every item click.
However, my view needs to know which item is clicked and what its state is.
I also want more complex scenarios later such as waiting for an UI action to complete before command execution (something like this answer).
I have the following question.
I have the following simple xaml:
<TextBox Name="NameBox" Text ="{Binding Name}" />
<Button Content="Save" Command="{Binding SaveCommand}" CommandParameter="{Binding Entity}" />
And i bind DataContext of this Window to following View Model
public class MyViewModel
{
public SimpleModel Entity { get; set; }
private ICommand _saveCommand;
public ICommand SaveCommand { get { return _saveCommand ?? (_saveCommand = new MyCommand(OnSaveItem, parameter => CanSaveItem())); } }
public void OnSaveItem(object parameter)
{
// some code
}
public virtual bool CanSaveItem()
{
return !String.IsNullOrWhiteSpace(Entity.Name);
}
}
SimpleModel is
public class SimpleModel
{
public int Id { get; set; }
public string Name { get; set; }
}
This code works mostly correct but i can not
make method CanSaveItem to work properly. I don't know how to tell to SaveCommand that properties of ViewModel was changed. I know that i have to use CanExecuteChanged or CommandManager.InvalidateRequerySuggested and i tried to use their some times but i don't know how to do it properly and it didn't take an effect. Could you help me with this problem?
UPD.
public class MyCommand : ICommand
{
public MyCommand(Action<object> execute, Predicate<object> canExecute)
{
_canExecute = canExecute;
_execute = execute;
}
public bool CanExecute(object parameter)
{
return _canExecute(parameter);
}
public void Execute(object parameter)
{
_execute(parameter);
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
private readonly Predicate<object> _canExecute;
private readonly Action<object> _execute;
}
It appears you are on an early learning curve, and this can be confusing... and sometimes still is to me too.
Anyhow, I've made some slight changes to what you had and explain what I did to them.
public class MyViewModel
{
public SimpleModel Entity { get; set; }
private MyCommand _saveCommand;
public MyCommand SaveCommand { get { return _saveCommand ?? (_saveCommand = new MyCommand(OnSaveItem, parameter => CanSaveItem())); } }
public MyViewModel()
{
//------ You need to create an instance of your entity to bind to
Entity = new SimpleModel();
//-- I added an event handler as your "Entity" object doesn't know
//-- about the button on the view model. So when it has something
//-- change, have it call anybody listening to its exposed event.
Entity.SomethingChanged += MyMVVM_SomethingChanged;
}
void MyMVVM_SomethingChanged(object sender, EventArgs e)
{
// Tell our mvvm command object to re-check its CanExecute
SaveCommand.RaiseCanExecuteChanged();
}
public void OnSaveItem(object parameter)
{
// some code
}
public virtual bool CanSaveItem()
{
//-- Checking directly to your Entity object
return !String.IsNullOrWhiteSpace(Entity.Name);
}
}
public class SimpleModel
{
//-- Simple constructor to default some values so when you run
//-- your form, you SHOULD see the values immediately to KNOW
//-- the bindings are correctly talking to this entity.
public SimpleModel()
{
_name = "test1";
_Id = 123;
}
//-- changed to public and private... and notice in the setter
//-- to call this class's "somethingChanged" method
private int _Id;
public int Id
{
get { return _Id; }
set
{
_Id = value;
somethingChanged("Id");
}
}
private string _name;
public string Name
{ get { return _name; }
set { _name = value;
somethingChanged( "Name" );
}
}
//-- Expose publicly for anything else to listen to (i.e. your view model)
public event EventHandler SomethingChanged;
//-- So, when any property above changes, it calls this method with whatever
//-- its property is just as a reference. Then checks. Is there anything
//-- listening to our exposed event handler? If so, pass the information on
private void somethingChanged( string whatProperty)
{
// if something is listening
if (SomethingChanged != null)
SomethingChanged(whatProperty, null);
}
}
public class MyCommand : ICommand
{
public MyCommand(Action<object> execute, Predicate<object> canExecute)
{
_canExecute = canExecute;
_execute = execute;
}
public bool CanExecute(object parameter)
{
return _canExecute(parameter);
}
public void Execute(object parameter)
{
_execute(parameter);
}
private readonly Predicate<object> _canExecute;
private readonly Action<object> _execute;
//-- Change to the event handler definition, just expose it
public event EventHandler CanExecuteChanged;
//-- Now expose this method so your mvvm can call it and it rechecks
//-- it's own CanExecute reference
public void RaiseCanExecuteChanged()
{
if (CanExecuteChanged != null)
CanExecuteChanged(this, new EventArgs());
}
}
Finally, the bindings in the form. I don't know how you have set the "DataContext" of your view to your view model, but assuming that is all correct and no issue, adjust the textbox and command button to something like
<TextBox Name="NameBox" Text ="{Binding Entity.Name,
NotifyOnTargetUpdated=True, UpdateSourceTrigger=PropertyChanged}" />
<Button Content="Save" Command="{Binding SaveCommand}" CommandParameter="{Binding Entity}" />
Notice the text binding is to the "Entity" object on your MVVM and then the ".Name" property of your Entity object. The important thing here is the UpdateSourceTrigger. This forces an update back to your data binding for every character change, so as soon as you remove the last character, or start typing the first character, the "Save" button will then be refreshed respectively.
I would try invoking CommandManager.InvalidateRequerySuggested.
http://msdn.microsoft.com/en-us/library/system.windows.input.commandmanager.invalidaterequerysuggested(v=vs.110).aspx
I need to disable button for a while it running. I have this code:
RelayCommand.cs This is my command class.
class RelayCommand : ICommand
{
readonly Action<object> _execute;
readonly Predicate<object> _canExecute;
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 bool CanExecute(object parameter)
{
return _canExecute == null ? true : _canExecute(parameter);
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public void Execute(object parameter)
{
_execute(parameter);
}
}
MainWindowViewModel.cs This is my command for binding.
private RelayCommand _getTextCommand;
public ICommand GetTextCommand
{
get
{
if (_getTextCommand == null)
{
_getTextCommand = new RelayCommand(
param => this.GetText(param),
param => true
);
}
return _getTextCommand;
}
}
MainWindow.xaml This is XAML code where i bind my command.
<Button x:Name="getTextButton" Command="{Binding GetTextCommand}" CommandParameter="{Binding ElementName=textTypeSelector, Path=SelectedIndex}"/>
This is the code i am launching in command:
public async void GetText(object o)
{
await Task.Factory.StartNew(() =>
{
// code
});
}
Try this: add a boolean property in view model and implement INotifyPropertyChanged in view model
private bool isEnable = true;
public bool IsEnable
{
get { return isEnable; }
set
{
isEnable = value;
NotifyPropertyChanged();
}
}
public async void GetText(object o)
{
await Task.Factory.StartNew(() =>
{
IsEnable = false;
});
IsEnable = true;
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
Bind it to button IsEnable property
<Button x:Name="abc"
Command="{Binding GetTextCommand}"
IsEnabled="{Binding IsEnable}" />
Set IsEnable whereever you want.
Add a boolean to your ViewModel to indicate that the command is executing and set the boolean in your GetText() method.
private bool _isRunning = false;
public async void GetText(object o)
{
await Task.Factory.StartNew(() =>
{
_isRunning = true;
CommandManager.InvalidateRequerySuggested();
// code
_isRunning = false;
CommandManager.InvalidateRequerySuggested();
});
}
public bool CanGetText(object o){
return ! _isRunning;
}
Then change your RelayCommand to the following
_getTextCommand = new RelayCommand(this.GetText,CanGetText);
The problem is that you are passing true for the CanExecute delegate. Pass it a method that will execute every time it needs evaluation, and within that method you can test to see whether the Command should be executable or otherwise.
Here is an implementation that I'm using. It doesn't need an additional property in the ViewModel.
public sealed class AsyncDelegateCommand : ICommand
{
private readonly Func<bool> _canExecute;
private readonly Func<Task> _executeAsync;
private Task _currentExecution;
public AsyncDelegateCommand(Func<Task> executeAsync)
: this(executeAsync, null)
{
}
public AsyncDelegateCommand(Func<Task> executeAsync, Func<bool> canExecute)
{
if (executeAsync == null) throw new ArgumentNullException("executeAsync");
_executeAsync = executeAsync;
_canExecute = canExecute;
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public bool CanExecute(object parameter)
{
if (_currentExecution != null && !_currentExecution.IsCompleted)
{
return false;
}
return _canExecute == null || _canExecute();
}
public async void Execute(object parameter)
{
try
{
_currentExecution = _executeAsync();
await _currentExecution;
}
finally
{
_currentExecution = null;
CommandManager.InvalidateRequerySuggested();
}
}
}
So in this particular MVVM implementation I'm doing, I need several commands. I really got tired of implementing the ICommand classes one by one, so I came up with a solution, but I don't know how good it is, so the input of any WPF expert here will be greatly appreciated. And if you could provide a better solution, even better.
What I did is a single ICommand class and two delegates which take an object as a parameter, one delegate is void (for OnExecute), the other bool (for OnCanExecute). So in the constructor of my ICommand (which is called by the ViewModel class) I send the two methods, and on each ICommand method I invoke the delegates' methods.
It works really good, but I'm not sure if this is a bad way to do it, or if there's a better way. Below is the complete code, any input will be greatly appreciated, even negative, but please be constructive.
ViewModel:
public class TestViewModel : DependencyObject
{
public ICommand Command1 { get; set; }
public ICommand Command2 { get; set; }
public ICommand Command3 { get; set; }
public TestViewModel()
{
this.Command1 = new TestCommand(ExecuteCommand1, CanExecuteCommand1);
this.Command2 = new TestCommand(ExecuteCommand2, CanExecuteCommand2);
this.Command3 = new TestCommand(ExecuteCommand3, CanExecuteCommand3);
}
public bool CanExecuteCommand1(object parameter)
{
return true;
}
public void ExecuteCommand1(object parameter)
{
MessageBox.Show("Executing command 1");
}
public bool CanExecuteCommand2(object parameter)
{
return true;
}
public void ExecuteCommand2(object parameter)
{
MessageBox.Show("Executing command 2");
}
public bool CanExecuteCommand3(object parameter)
{
return true;
}
public void ExecuteCommand3(object parameter)
{
MessageBox.Show("Executing command 3");
}
}
ICommand:
public class TestCommand : ICommand
{
public delegate void ICommandOnExecute(object parameter);
public delegate bool ICommandOnCanExecute(object parameter);
private ICommandOnExecute _execute;
private ICommandOnCanExecute _canExecute;
public TestCommand(ICommandOnExecute onExecuteMethod, ICommandOnCanExecute onCanExecuteMethod)
{
_execute = onExecuteMethod;
_canExecute = onCanExecuteMethod;
}
#region ICommand Members
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public bool CanExecute(object parameter)
{
return _canExecute.Invoke(parameter);
}
public void Execute(object parameter)
{
_execute.Invoke(parameter);
}
#endregion
}
This is almost identical to how Karl Shifflet demonstrated a RelayCommand, where Execute fires a predetermined Action<T>. A top-notch solution, if you ask me.
public class RelayCommand : ICommand
{
private readonly Predicate<object> _canExecute;
private readonly Action<object> _execute;
public RelayCommand(Predicate<object> canExecute, Action<object> execute)
{
_canExecute = canExecute;
_execute = execute;
}
public event EventHandler CanExecuteChanged
{
add => CommandManager.RequerySuggested += value;
remove => CommandManager.RequerySuggested -= value;
}
public bool CanExecute(object parameter)
{
return _canExecute(parameter);
}
public void Execute(object parameter)
{
_execute(parameter);
}
}
This could then be used as...
public class MyViewModel
{
private ICommand _doSomething;
public ICommand DoSomethingCommand
{
get
{
if (_doSomething == null)
{
_doSomething = new RelayCommand(
p => this.CanDoSomething,
p => this.DoSomeImportantMethod());
}
return _doSomething;
}
}
}
Read more:
Josh Smith (introducer of RelayCommand): Patterns - WPF Apps With The MVVM Design Pattern
I have written this article about the ICommand interface.
The idea - creating a universal command that takes two delegates: one is called when ICommand.Execute (object param) is invoked, the second checks the status of whether you can execute the command (ICommand.CanExecute (object param)).
Requires the method to switching event CanExecuteChanged. It is called from the user interface elements for switching the state CanExecute() command.
public class ModelCommand : ICommand
{
#region Constructors
public ModelCommand(Action<object> execute)
: this(execute, null) { }
public ModelCommand(Action<object> execute, Predicate<object> canExecute)
{
_execute = execute;
_canExecute = canExecute;
}
#endregion
#region ICommand Members
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter)
{
return _canExecute != null ? _canExecute(parameter) : true;
}
public void Execute(object parameter)
{
if (_execute != null)
_execute(parameter);
}
public void OnCanExecuteChanged()
{
CanExecuteChanged(this, EventArgs.Empty);
}
#endregion
private readonly Action<object> _execute = null;
private readonly Predicate<object> _canExecute = null;
}
I've just created a little example showing how to implement commands in convention over configuration style. However it requires Reflection.Emit() to be available. The supporting code may seem a little weird but once written it can be used many times.
Teaser:
public class SampleViewModel: BaseViewModelStub
{
public string Name { get; set; }
[UiCommand]
public void HelloWorld()
{
MessageBox.Show("Hello World!");
}
[UiCommand]
public void Print()
{
MessageBox.Show(String.Concat("Hello, ", Name, "!"), "SampleViewModel");
}
public bool CanPrint()
{
return !String.IsNullOrEmpty(Name);
}
}
}
UPDATE: now there seem to exist some libraries like http://www.codeproject.com/Articles/101881/Executing-Command-Logic-in-a-View-Model that solve the problem of ICommand boilerplate code.
#Carlo I really like your implementation of this, but I wanted to share my version and how to use it in my ViewModel
First implement ICommand
public class Command : ICommand
{
public delegate void ICommandOnExecute();
public delegate bool ICommandOnCanExecute();
private ICommandOnExecute _execute;
private ICommandOnCanExecute _canExecute;
public Command(ICommandOnExecute onExecuteMethod, ICommandOnCanExecute onCanExecuteMethod = null)
{
_execute = onExecuteMethod;
_canExecute = onCanExecuteMethod;
}
#region ICommand Members
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public bool CanExecute(object parameter)
{
return _canExecute?.Invoke() ?? true;
}
public void Execute(object parameter)
{
_execute?.Invoke();
}
#endregion
}
Notice I have removed the parameter from ICommandOnExecute and ICommandOnCanExecute and added a null to the constructor
Then to use in the ViewModel
public Command CommandToRun_WithCheck
{
get
{
return new Command(() =>
{
// Code to run
}, () =>
{
// Code to check to see if we can run
// Return true or false
});
}
}
public Command CommandToRun_NoCheck
{
get
{
return new Command(() =>
{
// Code to run
});
}
}
I just find this way cleaner as I don't need to assign variables and then instantiate, it all done in one go.