Abstracting commands into the View Model is a valuable practice with XAML/MVVM projects. I get that. And, I see ICommand in in WinRT; but, how do we implement it? I haven't found a sample that actually works. Anyone know?
My all time favorite has to be the DelegateCommand provided by the Microsoft Patterns and Practices team. It allows you to create a typed command:
MyCommand = new DelegateCommand<MyEntity>(OnExecute);
...
private void OnExecute(MyEntity entity)
{...}
It also provides a way to raise the CanExecuteChanged event (to disable/enable the command)
MyCommand.RaiseCanExecuteChanged();
Here's the code:
public class DelegateCommand<T> : ICommand
{
private readonly Func<T, bool> _canExecuteMethod;
private readonly Action<T> _executeMethod;
#region Constructors
public DelegateCommand(Action<T> executeMethod)
: this(executeMethod, null)
{
}
public DelegateCommand(Action<T> executeMethod, Func<T, bool> canExecuteMethod)
{
_executeMethod = executeMethod;
_canExecuteMethod = canExecuteMethod;
}
#endregion Constructors
#region ICommand Members
public event EventHandler CanExecuteChanged;
bool ICommand.CanExecute(object parameter)
{
try
{
return CanExecute((T)parameter);
}
catch { return false; }
}
void ICommand.Execute(object parameter)
{
Execute((T)parameter);
}
#endregion ICommand Members
#region Public Methods
public bool CanExecute(T parameter)
{
return ((_canExecuteMethod == null) || _canExecuteMethod(parameter));
}
public void Execute(T parameter)
{
if (_executeMethod != null)
{
_executeMethod(parameter);
}
}
public void RaiseCanExecuteChanged()
{
OnCanExecuteChanged(EventArgs.Empty);
}
#endregion Public Methods
#region Protected Methods
protected virtual void OnCanExecuteChanged(EventArgs e)
{
var handler = CanExecuteChanged;
if (handler != null)
{
handler(this, e);
}
}
#endregion Protected Methods
}
return CanExecute(parameter as T)
, then in CanExecute
, add a check for parameter == null
? –
Sanmiguel T
is a ref type –
Sanmiguel Check out RelayCommand class (only METRO code). The NotifyPropertyChanged
class can be found here. The NotifyPropertyChanged
class is only used to allow bindings on CanExecute
and update it with RaiseCanExecuteChanged
.
The original relay command class can be found here
Unfortunately there does not seem to be a native class that implements it for you. The interface is not overly complicated if you want to implement it yourself, and the popular MVVM Lite toolkit includes its own version of RelayCommand. You can add MVVM Lite to your project by right-clicking on References and choosing "Manage NuGet Packages". If you don't have this option, enable Nuget under Tools -> Extensions and Updates.
I've been looking for a minimal end-to-end implementation of a XAML-MVVM command, and not found it yet.
So, following #Rico's answer I ended up with the following as a minimal RelayCommand which works. I use a similar minimal version in a large project.
public class RelayCommand : System.Windows.Input.ICommand {
readonly Action<object> execute;
public RelayCommand(Action<object> execute) {
this.execute = execute;
}
public bool CanExecute(object parameter) {
return true;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter) {
this.execute(parameter);
}
}
The larger RelayCommand class seems to provide more control over CanExecute
and CanExecuteChanged
, but you don't need that to get started - and you may not need it at all.
To use it in a view model:
class ViewModel : INotifyPropertyChanged {
<< ... snip VM properties and notifications ...>>
public RelayCommand DoSomethingCommand {
get {
return new RelayCommand(param => {
this.DoSomething(param as AType);
Debug.WriteLine("Command Executed");
});
}
}
}
(We don't need the INotifyPropertyChanged for the Command, but any view model typically implements it.)
Finally, the XAML...
<Grid>
<!-- Set the data context here, for illustration. -->
<Grid.DataContext>
<local:ViewModel/>
</Grid.DataContext>
<!-- A sample control bind to a property -->
<TextBlock
Text="{Binding AProp}"/>
<!-- Bind a command -->
<Button Command="{Binding DoSomethingCommand}" CommandParameter="foo">Change!</Button>
</Grid>
I found this really good example at https://code.msdn.microsoft.com/windowsapps/Working-with-ICommand-690ba1d4
<Page.Resources>
<local:MyCommandsCollection x:Key="MyCommands" />
</Page.Resources>
<Button Width="280"
Height="59"
Margin="513,280,0,0"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Command="{Binding MyFirstCommand}"
CommandParameter="{Binding Text,
ElementName=myTextBox}"
Content="Execute Command" />
public class MyCommandsCollection
{
public MyCommand MyFirstCommand
{
get { return new MyCommand(); }
}
}
public class MyCommand : ICommand
{
public bool CanExecute(object parameter)
{
return true;
}
public event EventHandler CanExecuteChanged;
public async void Execute(object parameter)
{
MessageDialog message = new MessageDialog(
"The command is executing, the value of the TextBox is " + parameter as String);
await message.ShowAsync();
}
}
I tried this out with x:Bind and it works nicely. All I need is to expose a property in my ViewModel that returns a new Instance of the "MyCommand" class and it's all good.
Since I'm setting the DataContext in my XAML, I didn't need to mess with any of the "MyCommandCollection" stuff. Yay compiled binding.
© 2022 - 2024 — McMap. All rights reserved.