You can write your own command.
Here is the baseclass I use for my commands.
It has some very basic things that make life easier.
- the
Execute
method accepts an object, so you will be able to pass arrays - a viewmodel can be easily passed in, that is the one you will work with in your commands (this is most of the time the case, swipe it out if you do not need that)
- the changed handler leverages the CommandManager. This is really very helpful
Perhaps you want to change some things. All I have added is in there because it is very helpful. (especially the viewmodel)
public abstract class CommandBase : ICommand
{
public abstract bool CanExecute(object o);
public abstract void Execute(object o);
public PropertyChangedBase ViewModel { get; set; }
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
}
You would have implementations like
public class ExampleCommand : CommandBase
{
public ExampleCommand (PropertyChangedBase viewModel)
{
this.ViewModel = viewModel;
}
public override void Execute(object o)
{
// something like
var settings = UnityContainer.Resolve<ISettings>();
settings.MagicValue = (this.ViewModel as ConcreteViewModel).MagicValue;
}
public override bool CanExecute(object o)
{
return true;
}
}
in your ViewModel, you expose the command to the view by having a property:
public class ExampleViewModel : PropertyChangedBase
{
public ExampleViewModel ()
{
this.DoThisAndThatCommand = new ExampleCommand(this);
}
public CommandBase DoThisAndThatCommand { get; set; }
}
// and in XAML, you can use it like
<Button x:Name="Ok"
Command="{Binding DoThisAndThatCommand }" />
(Given you have connected the ViewModel
and the View
correctly by setting the DataContext
of the View
)
Now, whenever the Button is clicked, the Execute Method of the Command will get called.
You have your ViewModel right in the Command
, so you can easily work with it.
It is very unusual to have a button inside a command or inside a ViewModel
.
The trick about MVVM is to separate the View
from the ViewModel
and to not have
UIElements
in the ViewModel
.
If you do not have the PropertyChangedBase (this one comes with Caliburn.Micro) then I would suggest to use some easy INotifyPropertyChanged implementation.
I found this one here, should be german though
public abstract class NotifyPropertyChangedBase : INotifyPropertyChanged { #region < INotifyPropertyChanged > Members
/// <summary>
/// Is connected to a method which handle changes to a property (located in the WPF Data Binding Engine)
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Raise the [PropertyChanged] event
/// </summary>
/// <param name="propertyName">The name of the property</param>
protected void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
private Dictionary<string, object> propertyValueStorage;
#region Constructor
public NotifyPropertyChangedBase()
{
this.propertyValueStorage = new Dictionary<string, object>();
}
#endregion
/// <summary>
/// Set the value of the property and raise the [PropertyChanged] event
/// (only if the saved value and the new value are not equal)
/// </summary>
/// <typeparam name="T">The property type</typeparam>
/// <param name="property">The property as a lambda expression</param>
/// <param name="value">The new value of the property</param>
protected void SetValue<T>(Expression<Func<T>> property, T value)
{
LambdaExpression lambdaExpression = property as LambdaExpression;
if (lambdaExpression == null)
{
throw new ArgumentException("Invalid lambda expression", "Lambda expression return value can't be null");
}
string propertyName = this.getPropertyName(lambdaExpression);
T storedValue = this.getValue<T>(propertyName);
if (!object.Equals(storedValue, value))
{
this.propertyValueStorage[propertyName] = value;
this.OnPropertyChanged(propertyName);
}
}
/// <summary> Get the value of the property </summary>
/// <typeparam name="T">The property type</typeparam>
/// <param name="property">The property as a lambda expression</param>
/// <returns>The value of the given property (or the default value)</returns>
protected T GetValue<T>(Expression<Func<T>> property)
{
LambdaExpression lambdaExpression = property as LambdaExpression;
if (lambdaExpression == null)
{
throw new ArgumentException("Invalid lambda expression", "Lambda expression return value can't be null");
}
string propertyName = this.getPropertyName(lambdaExpression);
return getValue<T>(propertyName);
}
/// <summary>
/// Try to get the value from the internal dictionary of the given property name
/// </summary>
/// <typeparam name="T">The property type</typeparam>
/// <param name="propertyName">The name of the property</param>
/// <returns>Retrieve the value from the internal dictionary</returns>
private T getValue<T>(string propertyName)
{
object value;
if (propertyValueStorage.TryGetValue(propertyName, out value))
{
return (T)value;
}
else
{
return default(T);
}
}
/// <summary>
/// Extract the property name from a lambda expression
/// </summary>
/// <param name="lambdaExpression">The lambda expression with the property</param>
/// <returns>The extracted property name</returns>
private string getPropertyName(LambdaExpression lambdaExpression)
{
MemberExpression memberExpression;
if (lambdaExpression.Body is UnaryExpression)
{
var unaryExpression = lambdaExpression.Body as UnaryExpression;
memberExpression = unaryExpression.Operand as MemberExpression;
}
else
{
memberExpression = lambdaExpression.Body as MemberExpression;
}
return memberExpression.Member.Name;
}
}
It is very easy to use!
In your ViewModel you have to provide public properties for Binding (this is VERY important) and fire a change notification.
Here is an example of how to use that basic implementation of INPC (INotifyPropertyChanged)
public class LoginViewModel : NotifyPropertyChangedBase
{
public string UserName { get;set; }
}
This INPC implementation makes the NotifyOfPropertyChange call for you, you do not have to care for it! But you will have to inspect what fits your case best.
In your Question you already have a ViewModelBase. Perhaps you want to use this one instead of the above.