How can a button be bound to a command in a view model like in WPF with MVVM?
How can a button be bound to a command in a view model like in WPF with MVVM?
You could create a generic command binding class that allows a command to be bound to any class that inherits from ButtonBase.
public class CommandBinding<T> where T : ButtonBase
{
private T _invoker;
private ICommand _command;
public CommandBinding(T invoker, ICommand command)
{
_invoker = invoker;
_command = command;
_invoker.Enabled = _command.CanExecute(null);
_invoker.Click += delegate { _command.Execute(null); };
_command.CanExecuteChanged += delegate { _invoker.Enabled = _command.CanExecute(null); };
}
}
The command binding can then be set up using the following code:
CommandBinding<Button> cmdBinding =
new CommandBinding<Button>(btnCut, CutCommand);
This is only the bare bones of my implementation to give you a start so naturally there are a few caveats:
ICommand interface so may have to be altered if you have your own implementation of the command pattern.The generic constraint can also be changed to Control which exposes the Click event and the Enabled property which means commands can be bound to almost any Control.
My approach is based on Gale's answer, but using an extension method and making the binding disposable for lifeycle management:
public static class ButtonExtensions
{
public static IDisposable Bind(this ButtonBase invoker, ICommand command)
{
void Click(object sender, EventArgs args) => command.Execute(null);
void CanExecuteChanged(object sender, EventArgs args) => invoker.Enabled = command.CanExecute(null);
invoker.Enabled = command.CanExecute(null);
invoker.Click += Click;
command.CanExecuteChanged += CanExecuteChanged;
return Disposable.Create(() =>
{
invoker.Enabled = false;
invoker.Click -= Click;
command.CanExecuteChanged -= CanExecuteChanged;
});
}
}
You can use it like this:
private List<IDisposable> Disposables { get; } = new List<IDisposable>();
private ICommand MyCommand { get; }
public MyControl()
{
MyCommand = DelegateCommand.Create(() => ...);
Disposables.Add(myButton.Bind(MyCommand));
}
~MyControl()
{
foreach(var disposable in Disposables)
{
disposable?.Dispose();
}
}
However, one might prefer to use ReactiveUI, which has native support for this: