Commands across View-Models - wpf

I am working on my first WPF/MVVM application, and I have come across a limitation in my knowledge of commands!
Here is my scenario.
I have a window - Customer.xaml.
It houses 2 usercontrols
viewCustomerSearch.xaml
viewCustomerDetails.xaml
Each of THOSE has it's own view model.
So, the hierarchy looks like this:
... Customer.xaml
... ... viewCustomerSearch.xaml
... ... ... viewmodelCustomerSearch.xaml
... ... viewCustomerDetails.xaml
... ... ... viewmodelCustomerDetails.xaml
I understand this to be a 'not uncommon' scenario.
For what it is worth, the user selects a customer by double clicking on a listview line in the viewCustomerSearch.xaml control.
The problem is: I now need to tell the viewmodelCustomerDetails.xaml class which customer the user has just selected. I cannot work this out at all.
Does anyone have any help on where I declare the command I need, how it gets hooked up, where the implementation code fires, etc?
Any help gratefully appreciated,
DS

Typically, to do inter-viewmodel communication, you can either:
Use standard .NET events, and use the parent view model as the mediator - in your case the Customer view model would have references to the 2 child view models, and can subscribe to events, and call appropriate methods on the child view models when the events are published
Use an event aggregator pattern
Frameworks such as Caliburn.Micro and Prism provide an implementation of the event aggregator pattern.

Alternatively, if you don't need completely decoupled view-models, then your Customers.xaml could set its DataContext to an instance of CustomersViewModel. The search view would inherit this data context, would bind it's list view to the list of customers, and would set the SelectedItem property in response to a double click. The detail view DataContext would be bound to the SelectedItem property.
public class CustomersViewModel : ViewModelBase
{
public Customer SelectedItem
{
get { return _selectedItem; }
set { Set(() => SelectedItem, ref _selectedItem, value); }
}
private Customer _selectedItem;
public IEnumerable<Customer> Customers { get; private set; }
}
public class Customer : ViewModelBase
{
private string _name;
public string Name
{
get { return _name; }
set { Set(() => Name, ref _name, value); }
}
...
}

Related

How to implement MVVM Custom Control?

I have the Expense screen that contains a textbox where I can put the price of an expense. As I have built lot of logic (validation etc.) for this textbox I would like to extract it to a separate control and reuse it on other screens. I'm trying to build it in the mvvm style. Here's what I have for now:
ExpenseView
<page>
<Label Text={Binding Date} />
<wpfControls:CurrencyTextBoxView ViewModel="{Binding PriceViewModel}" />
</page>
ExpenseViewModel
public class ExpenseViewModel : INotifyPropertyChanged
{
private ExpenseModel Model { get; }
public string Date
{
get { return Model.Date.ToString(); }
set
{
Model.Date = DateTime.Parse(value);
RaisePropertyChanged();
}
}
private CurrencyTextBoxViewModel _priceViewModel;
public CurrencyTextBoxViewModel PriceViewModel
{
get { return _priceViewModel; }
set { _priceViewModel = value; }
}
}
ExpenseModel
public class ExpenseModel
{
public DateTime Date { get; set; }
public decimal Price { get; set; } // This is the bit I don't know how to implement correctly
}
CurrencyTextBoxView
<control>
<TextBox Content={Binding Price} />
</control>
CurrencyTextBoxViewModel
public class CurrencyTextBoxViewModel : INotifyPropertyChanged
{
private CurrencyModel Model { get; }
public string Price
{
get { return Model.Price.ToString(); }
set
{
Model.Price = decimal.Parse(value);
RaisePropertyChanged();
}
}
}
CurrencyModel
public class CurrencyModel
{
public decimal Price { get; set; }
}
And now the problem is: I need to have the Price property in my ExpenseModel as well (as it's being saved in the db). I don't want to have the Date property in my CurrencyModel (as not always it makes sense).
Should I keep the CurrencyModel inside of my ExpenseModel? How would I update it efficiently when the text in the currency textbox would change?
Also the ExpenseModel is located in different project than the rest of my classes and I wouldn't like to make this project dependend on the project with the CurrencyModel.
Should I listen to PropertyChanged events from CurrencyTextBoxViewModel and update the ExpenseModel.Price whenever the CurrencyTextBoxViewModel.Price string changes? I like the way my view models act as parsers of models for views (the Date property example). Is there any way to implement the PriceViewModel so that its getter returns data straight from the ExpenseModel (so it acts as a parser)?
I know there are lot of ways to implement it but I'm looking for the most mvvm-consistent one. Also, I'm not sure if I have implemented the whole pattern correctly?
As for Date property, place it only on the view model if it needs no serialization.
How I would do:
If you want to reuse your CurrencyTextBox, I would not tie the control with the model. No CurrencyModel is needed. Prepare a DependencyProperty (say CurrencyText) on CurrencyTextBox. The view goes as:
<xxx:CurrencyTextBox CurrencyText="{Binding Currency}" />
You can omit CurrencyTextBoxViewModel because the CurrencyText can be bound to the internal TextBox.Text (or, just update relevant property on the view model). Anyway, with two-way binding, when CurrencyText is updated, ExpenseViewModel.Currency is also update, and here you can update your model.
I think it is ok to listen event of the model because the view model live shorter than the model. However you should block propagate event firing if the new value is the same as the old one.
The model serves as the origin of data and does business logic. The view model is just a shadow of the model, thus it is usually thin. Consider using some MVVM frameworks to get a sense of that.

Binding guard properties in Caliburn.Micro to properties on a object in the view model

I have a ViewModel class that looks like this.
class MyViewModel : Screen
{
public BindableCollection<MyObject> MyObjects { get; set; }
private MyObject selectedObject;
public MyObject SelectedMyObject
{
get { return selectedObject; }
set
{
selectedObject = value:
//some additional unrelated logic
}
}
public void SaveObject()
{
//some logic
}
public bool CanSaveObject{
get{
//logic to determine if the selectedObject is valid
}
}
That is the relevant code. Now the problem.
MyObject is a class with three properties. In the View I have a ListView that is bound to the MyObjects collection, and three TextBoxes that are bound to the SelectedItem in the ListView.
When I fill in the textboxes, the related object gets changed in the Model, but I want to make sure that the object is in a valid state before you can save it. CanSaveObject has the necessary logic, but the problem is that is never gets called since I don't have any oppurtunity to call NotifyOfPropertyChanged when the textboxes are filled since only the properties of selectedObject are called, and no properties on MyViewModel.
So the question is: Are there any good way to do this without making properties on the ViewModel that encapsulate the properties inside MyObject.
I have got it working if I make properties like these, and then bind to these instead of the SelectedItem directly in the view, but the viewmodel gets cluttered up in the hurry if hacks like this is the only way to do it. I hope it's not :)
public string SelectedObjectPropertyOne{
get{ return selectedObject.PropertyOne; }
set{
selectedObject.PropertyOne = value;
NotifyOfPropertyChange(() => SelectedObjectPropertyOne);
NotifyOfPropertyChange(() => CanSaveObject);
}
}
ActionMessage.EnforceGuardsDuringInvocation is a static boolean field that can be set to enforce a guard check when an action is about to be invoked. This will guard the actual Save action from being invoked, however it will not help with the issue of the UI appearance based on the guard state immediately after an update to the selected model.
Without doing that, the only other modification I could suggest would be to create a VM type for MyObject model and move the validation and save logic there. This would also allow you to simplify your Views...

MVVM for collections

I have recently started learning wpf and am trying to use mvvm.
My understanding is that in the mvvm neither the view or the model should know the other exists.
What I am trying to do is show a list of customers on the screen. But if I code the viewModel as shown below. which is similar to many examples I see on the net, then I end up with some code looking like this
class Customer
{
public String Name {get;set;}
public String Address {get;set;} }
}
class MainWindowViewModel
{
ObservableCollection<Customer> customers = new ObservableCollection<Customer>();
public ObservableCollection<Customer> Customer
{
get {return customers;}
}
public MainWindowViewModel()
{
//cust1 and cust2 are Customer objets
customers.Add(cust1);
customers.Add(cust2);
}
}
Now if I create an instance of my MainWindowViewModel and set it as the datacontext of my MainWindowView (my view) and i further bind the viewmodels Customers property to a listBox, then the view will need a reference to the assembly that contains my Models.
So my questions are.
1) Is adding a reference to Models assembly allowable in MVVM, since this would mean the view knows about the model.
2) would a better solution be to wrap each Customer object in a CustomerViewModel and have the MainWindowViewModel contain ObservableCollection of CustomerViewModel
instead of ObservableCollection of Customer. This would separate the models completely from the view.
I'm not sure why you think the project containing your views requires a reference to your model project? There is nothing in your view which references your models directly - your binding expressions in XAML are linked by name only, and to properties on your view model anyway, not your model.
Wrapping your model in a view model is a good option if your view requires additional data than your model provides, and it is undesirable to change your model. For example, you view may need to display the Age of a User type, but User only has a DateOfBirth property. Creating a UserViewModel with an Age property would be a good option if you didn't want to alter your model.
Answers to your questions:
What is bad about the View referring the Model? This is absolutely ok when it simplifies the code. Just the other way around (Model -> View) is bad practice.
You don't need to wrap each Customer object in a CustomerViewModel when you don't have special needs. I would suggest to follow a pragmatic way and keep the code simple.
You might be interested in the BookLibrary sample application of the WPF Application Framework (WAF) which shows the scenario you describe here.
We usually create a CustomerViewModel. That is enforced by our generic CollectionViewModelBase class. This unsures that every part the user interface uses is exspecially created to be displayed and we don't have any UI related code in the models which are often serializable POCOs.
The MVVM pattern is similar to any other MVx pattern (MVC, MVP, ...) in that it encourages separation of concerns (SoC), which in turn improve maintainability / testability of your code. Beyond the usual SoC, MVVM gives the following:
Unit testing of your view logic; this is because you move logic from your view into your view-model, making your view as dumb as possible.
Developer-designer workflow; because the view is 'dumb', it is easier to work with the XAML without the logic behind it.
Regarding visibility, i.e. what is visible to what, it is strictly as follows:
Model <= ViewModel <= View
In other words, the ViewModel can see the Model, but the Model cannot see the ViewModel. Likewise, the View can see the ViewModel, but not vice-versa.
Because the ViewModel has no reference to the View, it enables your code to be executed without any view components present, this enables (1) above.
The purpose of your ViewModel is to 'shape' your Model to make binding to the View easier. If your View is simple, then it is quite acceptable to do the following:
Model <= View
This still allows (1) unit testing, (2) developer-designer workflow.
It is also fine to use a hybrid approach, sometimes exposing your Model to your view, other times wrapping it in a ViewModel. For example:
http://www.scottlogic.co.uk/blog/colin/2009/08/the-mini-viewmodel-pattern/
Please don't create a bunch of boiler-plate ViewModel code, just because you think you have to!
You will definitively want to wrap your models in view only objects like below :
/// <summary>
/// Business model object : Should be in your separate business model only library
/// </summary>
public class BusinessModelObject
{
public string Prop1 { get; set; }
public int Prop2 { get; set; }
}
/// <summary>
/// Base notifying object : Should be in your GUI library
/// </summary>
public abstract class NotifyingObject<T> : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(PropertyChangedEventArgs e)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, e);
}
private static readonly PropertyChangedEventArgs ModelPropertyChanged = new PropertyChangedEventArgs("Model");
private T _model;
public T Model
{
get { return _model; }
set
{
_model = value;
NotifyPropertyChanged(ModelPropertyChanged);
}
}
}
/// <summary>
/// Model decorator : Should be in your GUI library
/// </summary>
public class BusinessModelObjectAdapter : NotifyingObject<BusinessModelObject>
{
public BusinessModelObjectAdapter(BusinessModelObject model)
{
this.Model = Model;
}
private static readonly PropertyChangedEventArgs Prop1PropertyChanged = new PropertyChangedEventArgs("Prop1");
private string _prop1;
public string Prop1
{
get { return Model.Prop1; }
set
{
Model.Prop1 = value;
NotifyPropertyChanged(Prop1PropertyChanged);
}
}
private static readonly PropertyChangedEventArgs Prop2PropertyChanged = new PropertyChangedEventArgs("Prop2");
private int _prop2;
public int Prop2
{
get { return Model.Prop2; }
set
{
Model.Prop2 = value;
NotifyPropertyChanged(Prop1PropertyChanged);
}
}
//and here you can add whatever property aimed a presenting your model without altering it at any time
}

Silverlight UserControl and his own Viewmodel hosted in a View

I want to make a reusable UserControl as:
A VIEW with its own VIEWMODEL where is the logic data recovery
This UserControl (or View ?) has a button 'OK' to target a RELAYCOMMAND in his viewmodel
I'm hosting this 'UserControl' in another VIEW ('MainPage'), who, herself, has its viewmodel
My question is:
How can I target the properties of VIEWMODEL of my 'MainPage' with the values outlined by my UC ?
As long as your user control is contained in your main page it will inherit the main pages view model. This is the default and it applies unless you explicitly change the data context through data binding or code.
If your user control binds to it own view model then you could let the main view model contain an instance of the child view model and expose it through a public property. Now you could set the data context of your user control by binding the DataContext property to the property on the main view model.
Finally if your child view model has a reference to the main view model then they will be able to communicate as needed.
Edit:
I'll try to setup a simple example:
First the view models:
public class MainPageViewModel
{
public MainPageViewModel()
{
ChildViewModel = new ChildViewModel(this);
}
public ChildViewModel {get; private set; }
public ICommand OkCommand { get { // return the command here }}
}
public class ChildViewModel
{
private MainPageViewModel _parentViewModel;
public ChildViewModel(MainPageViewModel parentViewModel)
{
_parentViewModel = parentViewModel;
}
// Returns the command from the main page view model
public ICommand OkCommand { get { return _parentViewModel.OkCommand; } }
// Other properties as well
}
Here we have the main view model that has the child view model as a property. The child view model exposes the OkCommand that returns the value from the main view model.
Now in your main page xaml you can do the following:
<uc:MyUserControl DataContext="{Binding ChildViewModel}" />
Here you insert your user control and set it's data context to the child user control view model.

Binding ContextMenu on flattened ViewModel

suppose the following classes:
public class Model
{
public ObservableCollection<A> Items { get; set; }
}
public class A
{
ObservableCollection<B> Data { get; set; }
//... some more properties
}
public class B
{
//..properties
}
The model is bound to a RibbonMenu and should also be used in a context menu. The context menu must be bound to all items of class B in the model. Changes in the model (new items added, items removed, items changed ...) should change both the context menu and the RibbonMenu.
The RibbonMenu works nicely but how is it possible to bind the contextmenu without creating a separate model?
You could create wrapper properties that flatten your A and B entities as needed for the view controls and expose them publicly from Model.
So, for instance, in Model, you have a private backer of ObservableCollection<A>. Then you have a public ObservableCollection<A> that simply returns the private backer for the ribbon to bind to.
Then also have a public ObservableCollection<B> that does whatever it needs to do in its getter to return what you want for the context menu. For example, if you want the distinct Bs across all As, have the getter do a query on all of A's Bs to return the correct list.
Finally, to tell the view that changes were made in Model, implement INotifyPropertyChanged and raise the PropertyChanged event in the setters of your public members.

Resources