WPF Commands - Relation of View to the Model - c#

I'm trying to understand a little better the MVVM design pattern for WPF developing and this is a basic question in this subject.
Let's say I got the model implemented and it got a method which does the main action. In the View i want to create a button which will activate this action when pressing it. In order to do so I need to associate the Click event with an event handler, which actually is only suppose to call the model method.
The problem is that as I understand, the view doesn't even suppose to know the model. So how can I make the button in the View do the action I want?

That's where the view model comes in. First you should consider using Commands instead of event handlers. With Commands you can bind the "action" to the button instead of hard coding an event to the Click event. Like this:
<Button Command="{Binding Path=ActionCommand}"/>
Now your view model have to have a property that implements ICommand. There are a lot of implementations for this, e g RelayCommand in MVVM Light Toolkit. Through this property you call your models action. This is done by a reference to the model that your view model has. The reference to the model could be set through dependency injection or just supplying it at the creation of the view model.
Simple View model class:
public class ViewModel : INotifyPropertyChanged
{
private Model _model;
private ICommand _actionCommand;
public ViewModel(Model model)
{
_model = model;
_actionCommand = new RelayCommand(ExecuteAction);
}
public ICommand ActionCommand
{
get { return _actionCommand; }
}
private void ExecuteAction()
{
_model.Action();
}
}
This means that your view doesn't really know the type of the ViewModel, just that it has a Command-property called ActionCommand. To set the Views view model you use the View.Datacontext. This can be done in several different ways. Dependency injection could be used here too. Another solution is to use a ViewModelLocator, that uses the Locator pattern to connect the view to its ViewModel.

In your Model you have your function:
class MainWindowModel
{
public void MyAction()
{...}
}
In the constructor of your ViewModel you create an instance of your model like:
class MainWindowViewModel
{
private readonly MainWindowModel mainWindowModel;
public MainWindowViewModel()
{
this.mainWindowModel = new MainWindowModel();
}
Then you have an implementation of ICommand like RelayCommand:
public class RelayCommand : ICommand
{
private readonly Action<object> execute;
private readonly Predicate<object> canExecute;
public RelayCommand(Action<object> exectue, Predicate<object> canExecute = null)
{
if (exectue == null)
throw new ArgumentNullException("exectue");
this.execute = exectue;
this.canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
return this.canExecute == null || this.canExecute(parameter);
}
public void Execute(object parameter)
{
this.execute(parameter);
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
}
so. now you have a property in your ViewModel:
private ICommand myCommand;
public ICommand MyCommand
{
get { return this.myCommand; }
set
{
this.myCommand = value;
OnPropertyChanged();
}
}
The OnPropertyChanged-Event you get, when you implement the INotifyPropertyChanged-Interface
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
In your constructor of your ViewModel you instanziate the MyCommand like
this.MyCommand = new RelayCommand(MyCommandExecute);
Then you have to create a method in your viewmodel where you call the MyAction-Method of your model:
public void MyCommandExecute(object parameter)
{
this.mainWindowModel.MyAction();
}
In your xaml you have to set the DataContext like:
<Window.DataContext>
<viewModel:MainWindowViewModel/>
</Window.DataContext>
The small viewModel is the Namespace of your ViewModel. This you have to add in the Window-Definition like:
xmlns:viewModel="clr-namespace:TestApplication.ViewModel"
Now you can bind your button-Command to the ICommand-Property of your ViewModel like:

You need to use the ICommand interface, and supply an implementation, for which I suggest that you read this article on MSDN.
In your ViewModel, you would create an instance of ICommand, e.g. ButtonClick, which would look something like (based on RelayCommand):
public class ViewModel
{
public ViewModel()
{
this.ButtonClick = new RelayCommand(_ => this.DoSomething());
}
public ICommand ButtonClick { get; set; }
public void DoSomething()
{
// Something...
}
}
Then in your xaml you would bind to ButtonClick:
<Button Text="Click" Command="{Binding ButtonClick}" />

Related

Call a method in the ViewModel without a command

I have the following scenario:
public class MyCommand : ICommand
{
MyViewModel _viewModel;
public MyCommand(MyViewModel viewModel)
{
_viewModel = viewModel;
}
public bool CanExecute(object parameter)
{
return true;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
_viewModel.SomeMethod();
}
}
Essentially, this command will simply call a method in my ViewModel when a button is clicked. CanExecute always returns true.
The issue is that even though this is generally considered to be the best way of doing things, it isn't very elegant. It begs the question of why I need a command to do this very simple process.
Why can't I just skip the command, and call the method directly?
I've had a think about this, and the only way that I can see to achieve this would be to create a command where I can specify the method name that I want to call in the CommandParameter property on my button.
I hope someone else can offer an elegant solution to this problem.
You can use the "CallMethodAction" action from Blend SDK to call a method on the view model from your view. It will look something like this:
<Button ...>
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<ei:CallMethodAction MethodName="MyMethod" TargetObject="{Binding}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
However it is still considered the best practice to use commands for that. As Ayyappan mentioned, you could use a library like MVVM Light Toolkit so you don't have to write your own implementation of ICommand.
PS: See also WPF: MVVM: Command vs CallMethodAction?
You can use RelayCommand from MVVMLight or DelegateCommand from PRISM. They both will give a generic way of implemeting Commands.
Or even you can create your own Common command class and use it. Refer the sample below.
public class ViewModel : INotifyPropertyChanged
{
public BaseCommand GetDataCommand { get; set; }
public ViewModel()
{
GetDataCommand = new BaseCommand(GetData);
}
private void GetData(object param)
{
}
}
public class BaseCommand : ICommand
{
private Predicate<object> _canExecute;
private Action<object> _method;
public event EventHandler CanExecuteChanged
{
add
{
CommandManager.RequerySuggested += value;
}
remove
{
CommandManager.RequerySuggested -= value;
}
}
public BaseCommand(Action<object> method, Predicate<object> canExecute=null)
{
_method = method;
_canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
if (_canExecute == null)
{
return true;
}
return _canExecute(parameter);
}
public void Execute(object parameter)
{
_method.Invoke(parameter);
}
}

Rig up a Command

I have a command parameter set on a button as follows:
<hw:ActionButton Content="MC" Command="{Binding ActionCommand}" CommandParameter="{x:Static hw:Action.MemoryClear}" Grid.Row="2" Grid.Column="0" />
The command is in the class:
public class ActionCommand : ICommand
{
private readonly CalculatorViewModel _viewModel;
public ActionCommand(CalculatorViewModel viewModel)
{
_viewModel = viewModel;
}
public bool CanExecute(object parameter)
{
return _viewModel != null && parameter is Action;
}
public void Execute(object parameter)
{
_viewModel.ProcessAction((Action)parameter);
}
public event EventHandler CanExecuteChanged;
}
Now I know commands must be in the ViewModel, so how do I couple this command to the ViewModel? Do I merely have an instance of it in the ViewModel called ActionCommand, which doesn't seem to work, or what must I do?
here is a sample for the same
class CalculatorViewModel
{
public CalculatorViewModel()
{
ActionCommand = new ActionCommand(this);
...
}
public ActionCommand ActionCommand { get; private set; }
...
}
above is a simple sample demonstrating a property for command which can be bound to button in the UI
point to note here is that the binding works only with public properties, public variables does not work in the same way.
as a suggestion you may use some DelegateCommand if you are going to create more similar commands.
What is CanExecuteChanged?
from Allowing CommandManager to query your ICommand objects
Because by default WPF has no idea that your custom ICommand objects exist. How would it?
Fortunately there is an easy solution to this problem. In your ICommand implementation, you make the CanExecuteChanged event hook the CommandManager’s RequerySuggested event.
sample
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}

Read value of a control in one window from another window in WPF MVVM application

I'm creating a small WPF application. I have to follow MVVM pattern to which I'm new.
I have two views(A,B) and two viewmodels.
This is my scenario, I have a radiobutton in window A. After checking the radiobutton, I click next in window A. The event will close Window A and open window B.
In the constructor of viewmodel of window B, I need to know if Radiobutton in window A is checked or not. How do I do this?
If you "have to follow MVVM pattern", you "have to" do the following.
First, you need create two ViewModels, for view A and for view B (but if you want - you can left only one ViewModel). For ViewModelA you should create ICommand property, that you bind with next button, and bool property IsChecked. ViewModelB should either contain a constructor, which will accept the bool parameter, or a property, which should be updated before view B will show.
Here the example of how it can be accomplished:
public class ViewModelA : ViewModelBase
{
public ViewModelA()
{
_nextCommand = new Cmd(this);
}
public ICommand NextCommand { get { return _nextCommand; } }
public bool IsChecked
{
get { return _isChecked; }
set
{
_isChecked = value;
RaisePropertyChanged("IsChecked");
}
}
private ICommand _nextCommand;
private bool _isChecked;
}
In the *.xaml view for A you should have the following:
<RadioButton IsChecked="{Binding Path=IsChecked}" />
<Button Content="Next" Command="{Binding Path=NextCommand}" />
Please note: DataContext for that view should be of type ViewModelA.
For ViewModelB:
public class ViewModelB : ViewModelBase
{
private bool _isAChecked;
public bool IsAChecked
{
get
{
return _isAChecked;
}
set
{
_isAChecked = value;
RaisePropertyChanged("IsAChecked");
}
}
}
public class Cmd : ICommand
{
ViewModelA _vmA;
public Cmd(ViewModelA vmA)
{
_vmA = vmA;
}
public bool CanExecute(object parameter)
{
return true;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
var vmB = new ViewModelB();
vmB.IsAChecked = _vmA.IsChecked;
// after that create ViewB, and set its DataContext to vmB
}
}
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
Instead of creating for each command its class, you can see the examples of the RelayCommand, or DelegateCommand, which are approximately do the same things.

How to bind to property with only get accessor

I have some custom editable listbox on my wpf window.
I also have a viewmodel class with Property Changed which looks like that:
public bool HasChanges
{
get
{
return customers.Any(customer => customer.Changed);
}
}
So, I would like to bind my Save button to this property:
<Button IsEnabled="{Binding HasChanges, Mode=OneWay}"...
My question is how to update Save button if one of the listbox rows is changed?
The proper way to deal with buttons is to implement ICommand interface. Here is an example from my solution:
public 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;
}
#region ICommand Members
public bool CanExecute(object parameter)
{
return _canExecute == null ? true : _canExecute(parameter);
}
public void Execute(object parameter)
{
_execute(parameter);
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
#endregion
}
You can then databind to button like this:
<Button Command="{Binding MyCommand}" .../>
Whats left is to declare an ICommand property on your viewmodel:
public ICommand MyCommand { get; private set; }
//in constructor:
MyCommand = new RelayCommand(_ => SomeActionOnButtonClick(), _ => HasChanges);
The state of the button will then automatically update on most changes. If it doesnt for some reason - you can force the update by calling CommandManager.InvalidateRequerySuggested
In order for WPF to react to changes in properties, the class must implement INotifyPropertyChanged interface. You need to send notifications every time a customer is changed, like this:
class CustomerList : INotifyPropertyChanged {
public event PropertyChangedEventHandler PropertyChanged;
private List<Customer> customers = ...
public bool HasChanges {
get {
return customers.Any(customer => customer.Changed);
}
}
// Callers who change customers inside your list must call this method
public void ChangeCustomer(Customer c) {
// Do whatever you need to do, ...
...
// then send out the notification to WPF
OnPropertyChanged("HasChanges");
}
protected void OnPropertyChanged(string name) {
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) {
handler(this, new PropertyChangedEventArgs(name));
}
}
}
Your ViewModel should implement INotifyPropertyChanged and should raise the PropertyChanged Event for HasChanges when ever you change a Customer in customers
Update:
If Customers implement INotifyPropertyChanged and customers it self is an observable collection. You could subscribe, and depending on the action desubscribe, to all the customers in the CollectionChangedEvent of your customers collection.
If your ViewModel implements INotifyPropertyChanged, you just need to call the OnPropertyChanged() method on HasChanges. With Prism, the equivalent method is RaisePropertyChanged.
However, with MVVM, you might want to put that test in the CanExecute method of your command which is bound to your button Command property. This will handle the IsEnabled automatically.
The button somehow has to receive notifications. In your case you probably implement the INotifyPropertyChanged interface in your viewmodel. When your "listbox row is changed" you should raise the PropertyChanged event for the "HasChanges" property. Changes should be noticed however in you viewmodel and there the event should be raised.
As a different solution since you have a viewmodel you could use a command on your button and the CanExecute would have the logic returning true or false, which also has to be marked by you when changes have happened.

How to navigate in WPF using PRISM and bind DataContext

There must be a lot of questions surrounding this area but I couldn't find anything to help in my instance.
The problem I'm experiencing is getting my ViewModel, and specifically a property within ViewModel, to be updated to my View. Below is my implementation. I think I understand where I'm going wrong but not sure how to resolve it.
I have a Module that has a list and edit view. Quite simply lists domain objects and then ability to edit a domain object.
My xaml binds the DataContent to a ViewModel property in my View.
I then use the INavigationAware.NavigateTo method to navigate to my ViewModel and this is where I load the domain object.
The problem is that obviously this is not reflected back to the View. The view already has an instance of the ViewModel. This method worked fine when the ViewModel was using a list of objects using ObservableCollection. However, this did not work when using a simple object or even an ObservableObject.
Could someone please help my understanding or point me to some links with a better implementation of what I am trying to achieve?
MyModule
public class MyModule : IModule
{
private readonly IRegionManager _regionManager;
public MyModule(IRegionManager regionManager)
{
_regionManager = regionManager;
}
public void Initialize()
{
_regionManager.RegisterViewWithRegion(Constants.MainRegionName, typeof(MyListView));
_regionManager.RegisterViewWithRegion(Constants.MainRegionName, typeof(MyEditView));
}
}
XAML
<UserControl
DataContext="ViewModel">
...
<TextBlock Text="{Binding Path=MyDomainObject.AProperty}" />
...
View
public partial class MyEditView
{
public readonly static string ViewName = "MyEditView";
public MyEditView(MyEditViewModel viewModel)
{
InitializeComponent();
ViewModel = viewModel;
}
public MyEditViewModel ViewModel
{
get { return DataContext as MyEditViewModel; }
private set { DataContext = value; }
}
}
ViewModel
public class MyViewModel : INavigationAware
{
private readonly IRegionManager _regionManager;
public MyDomainObject MyDomainObject { get; set; }
public void Load(ViewModelKey key)
{
// get domain object
// this method worked when MyDomainObject was
// ObservableCollection<T> as just adding elements to list
// where this is creating a new instance of MyDomainObject
var id = parameter from navigationContext;
MyDomainObejct = server.GetDomainObject(id);
}
public void OnNavigatedTo(NavigationContext navigationContext)
{
var key = key from navigationContext;
Load(key);
}
}
SOLUTION
public class MyEditViewModel : INavigationAware
{
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName]string propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
private MyDomainObject _myDomainObject;
public MyDomainObject MyDomainObject
{
get
{
return _myDomainObject;
}
set
{
if (value != _myDomainObject)
{
_myDomainObject = value;
NotifyPropertyChanged();
}
}
}
View
public partial class MyEditView
{
public MyEditView(MyEditViewModel viewModel)
{
InitializeComponent();
ViewModel = viewModel;
ViewModel.PropertyChanged += ViewModel_PropertyChanged;
}
public MyEditViewModel ViewModel
{
get { return DataContext as MyEditViewModel; }
private set { DataContext = value; }
}
private void ViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (!(sender is MyEditViewModel))
return;
ViewModel = (MyEditViewModel)sender;
}
}
For your binding to update you need to implement INotifyPropertyChanged and raise PropertyChanged Event on the set accessor of your domain object.
public event PropertyChangedEventHandler PropertyChanged = delegate {};
public MyDomainObject MyDomainObject
{
get
{
return myDomainObject;
}
set
{
if(value != myDomainObject)
{
myDomainObject = value;
RaisePropertyChanged("MyDomainObject");
}
}
}
private void RaisePropertyChanged(String p)
{
PropertyChanged(this, new PropertyChangedEventArgs(p));
}
Or as in the Prism book, inherit NotificationObject and call RaisePropertyChanged(()=> PropertyName) which is refactoring-safe

Categories

Resources