How one approaches designing a view with MVVM best practices in mind - wpf

This question is about how to factor the app design with MVVM in mind. I understand the general concepts of MVVM (ViewModel is not aware of the View, Models represent domain objects, etc...). I'm also aware of Commands and how some controls can invoke commands on the ViewModel.
What I can't figure out is how to factor a real-life app to fit this model (as opposed to simple textbox/button/query database examples). Here are some questions:
In a main view I have preview section that displays contextual data depending on what the user is doing (i.e. by interacting with certain controls, show some preview section). Should the View contain all the possible previews in XAML and then show/hide/update? Should ViewModel have properties like "public bool ShowPreviewA" and "public bool ShowPreviewB" set by ViewModel's internal state machine?
Where should the logic live when some controls have intricate interactions between them. For example, 3 checkboxes where at least one must be selected, by preventing unchecking. Seems to me that it would pollute the ViewModel on one hand, and also feel like there is certain "knowledge" about the View on the other hand.
This question is difficult to articulate properly (if I could, I would probably understand MVVM much better).
Any tips are welcomed.
EDIT:
The real question is how to break down the problem when writing the functionality of ViewModels. Is it a combination of top-down - i.e. encode every possible View state in ViewModel properties - and bottom-up - i.e. each logically related set of controls receive their properties from child ViewModels that report "up" some logical state (e.g. valid input)?

In a main view I have preview section that displays contextual data
depending on what the user is doing (i.e. by interacting with certain
controls, show some preview section). Should the View contain all the
possible previews in XAML and then show/hide/update? Should ViewModel
have properties like "public bool ShowPreviewA" and "public bool
ShowPreviewB" set by ViewModel's internal state machine?
No, of course not, if these "previews" are completely different UIs with completely different data, then use DataTemplates.
For example:
Given some classes:
public class Person: BusinessEntity //BusinessEntity is just a fictional base class for Model classes
{
public string LastName {get;set;}
}
public class Product: BusinessEntity
{
public string ProductName {get;set;
}
suppose your ViewModel is defined like this:
public class SomeViewModel: ViewModelBase //Same comment as above
{
public BusinessEntity SelectedEntity {get;set;} //NotifyPropertyChanged() etc
}
Your XAML can be defined like this:
<Window ...>
<Window.Resources>
<!-- DataTemplate for Person class -->
<DataTemplate DataType="Person">
<TextBox Text="{Binding LastName}"/>
</DataTemplate>
<!-- DataTemplate for Product class -->
<DataTemplate DataType="Product">
<TextBox Text="{Binding ProductName}"/>
</DataTemplate>
</Window.Resources>
<ContentPresenter Content="{Binding SelectedEntity}"/>
</Window>
WPF will take care of rendering the appropiate DataTemplate inside the ContentPresenter depending on which object type is put in the SelectedEntity property in the ViewModel.
Where should the logic live when some controls have intricate
interactions between them. For example, 3 checkboxes where at least
one must be selected, by preventing unchecking. Seems to me that it
would pollute the ViewModel on one hand, and also feel like there is
certain "knowledge" about the View on the other hand.
You can easily inherit ObservableCollection<T> to create this logic in a reusable manner. Then in your ViewModel just put some public SelectableCollection<T> MyItems {get;set;} where the selection / mutual exclusion, etc is handled by SelectableCollection and so on.
Bottom line: MVVM is all about reusability and encapsulation of functionality.

Related

Wrap a view and view model to add cross-cutting concerns

In non-ui code I'm used to add decorators/wrappers around instances of interfaces to add cross-cutting concerns like logging, checking etc.
Now I'm looking for a way to do something similar for ui code, which is view-model first in my application.
An example:
I have a sample model like this
public class SampleViewModel
{
// some properties
}
and a data template for it
<DataTemplate DataType={x:Type local:SampleViewModel>
<!-- some content -->
</DataTemplate>
In some cases I need to add a border around a view for a view model which can be animated to highlight the area for instance.
This border could be added to virtually every view, so adding the border to this specific data template is no option.
My initial approach was adding a wrapper view model like this:
public class BlinkingBorderWrapperViewModel
{
public BlinkingBorderWrapperViewModel(IWantBeAbleToBlink innerVM)
{
InnerVM = innerVM;
}
public object InnerVM {get;private set;}
// expose properties relevant for the blinking part
// some may be retrieved via the interface `IWantBeAbleToBlink`
}
and a data template
<DataTemplate DataType={x:Type local:BlinkingBorderWrapperViewModel}>
<!-- draw the wrapping stuff -->
<Border ...>
<!-- include the wrapped vm -->
<ContentControl Content={Binding Path=InnerVM} />
<Border ...>
</DataTemplate>
The interface IWantBeAbleToBlink is used to exchange data with the wrapper (e.g. if it should blink right now) and as a tag to check if it should be wrapped.
This works so far for a single wrapper with these drawbacks:
the view model needs to store the wrapped sub-viewmodels and thus looses type information (the wrapper can be no template due to data template restrictions).
I have no clue how to add a second, third, ... wrapper in a way that is maintainable.
I might add several wrappers, but this would have to be done from the view's side (i.e. in XAML and not from the view model)
Is there a way to achieve this adding of cross-cutting concerns to view (models) that allows several aspects to be added and is usable with an view model first approach?
Other stuff I might want to add:
An anchor tag so the view (model) can be navigated to
A wrapper logging mouse moves
A wrapper for enabling/disabling
...
from the comments:
Leaking UI logic?
In my opinion the view model should be able to express the need to "be highlighted". This could be done by blinking, a change in the background, change in font-size or whatever the view implements.
Since the view model should not care how this is done (and the way the highlighting is performed should be changeable without having a need to adjust the view model), I'd like just to provide an interface better named IWantToBeAbleToBeHighlighted (instead of IWantBeAbleToBlink) with maybe just a single switch.
The concrete properties exposed to the view (blink frequence, or new font size or new background color) should be included in the wrapper view model.

Reusing User Controls

I'm trying to alter an existing WPF application and my lack of WPF experience (I'm WINFORMS) is making this difficult.
I've come across a situation where I need to reuse a UserControl and I'm not sure how to do this in terms of modifying the xaml. I'll explain.
UserControlA has the following code:
<Grid Name="gdMain" Style="{StaticResource ContentRoot}">
<content:MonitorAlarmsPage />
</Grid>
Now, "MonitorAlarmsPage" is an AXML document that defines another UserControl - UserControlB. This UserControl, once created has to persist for the lifetime of the application.
So, I could have many UserControlAs, but only ONE UserControlB.
I've created a static class that has an appropriate UserControlB field which is updated when UserControlB is created, but how do I modify the content:MonitorAlarmsPage so that the content of the grid is replaced by the existing UserControlB as referenced in this static class and not by the XAML file that defines UserControlB? Can this actually be done? i.e. essentially, insert pre created user controls inside an XAML page.
To make things a bit clearer, UserControlB is essentially a page that can sit inside another page. The page is complex and there is a massive overhead incurred when it is created and so must only be created once.
WPF is very different to WinForms, so you certainly have your work cut out for you. The basic idea for the solution to your problem is this. You'll need to add a DependencyProperty to the MonitorAlarmsPage control. This will enable you to data bind to this property from outside the control.. This property should be of a type of custom class that you define, that contains all of the properties required in the inner control.
The next stage is to develop a DataTemplate that defines how WPF should display your custom class when it comes across an instance of it. In this DataTemplate, you declare your inner control, so that when WPF sees the custom class, it displays the inner control and sets the custom class as the DataContext for the inner control:
<DataTemplate DataType="{x:Type YourXmlNamespacePrefix:YourCustomClass}">
<YourControlsXmlNamespacePrefix:YourInnerControl DataContext="{Binding}" />
</DataTemplate>
Finally, you'll need to data bind your custom class (the static class) to the outer UserControl:
<content:MonitorAlarmsPage YourCustomClassProperty="{Binding YourStaticClass}" />
So just to be clear... this static class should be a data class, not a UI class. When using WPF, we manipulate data, not UI elements. We let the wonderful templating system generate the UI for us.

Dynamically bind Views into a ContainerControl with MVVM

I've been learning the MVVM pattern with Josh Smith's article and I want to create a classic layout with some links to the right (managed with commands) so when I click one I can show my view to the right into a tab control (inside it there is a ContentControl).
This is simple when I use a DataTemplate with the specific View and ViewModel I want to show on screen like this.
<!-- this section into my MainWindow's resources file -->
<DataTemplate xmlns:vm='clr-namespace:WpfFramework.ViewModels'
xmlns:vw='clr-namespace:WpfFramework.Views'
DataType="{x:Type vm:MySpecificViewModel }" >
<vw:MySpecificView />
</DataTemplate>
But, I want something more generic. I mean that my mainWindow should not know a specific View nor a specific ViewModel. It should only know that it binds to some commands and has a tab control which shows "some view". Every sample including Josh Smith's article seems to have limited universe of views and viewmodels, that's great with a sample.
So, how can I tell my ContentControl that some view (with its corresponding viewModel) is gonna be there without being so specific (without "burning" into the mainView the concrete types)?
best regards
Rodrigo
PD. I have tryed with base a ViewModel and Base View but it doesn't seem to work.
In your main View, bind a ContentControl to a generic ViewModelBase property
<ContentControl Content="{Binding CurrentPage}" />
CurrentPage would be defined in the main ViewModel as a ViewModelBase object, and to switch pages you simply set CurrentPage to whatever you want.
So when you click on something like the HomePageCommand, the main ViewModel would execute CurrentPage = new HomePageViewModel(); providing that HomePageViewModel inherits from ViewModelBase.
I wrote something a little while ago that shows some samples here if you're interested

Using Microsoft (or other) ribbon with large project and MVVM

Our application is a large project with many modules and view. The main window has a ribbon in it, and we are looking for the best way to integrate the ribbon in the application.
I've created a service which modules a views can register to add ribbon items relevant for them, and, in addition, any main view instance can provide its own ribbon items relevant to the that instance. a RibbonItem is a small class which abstract the options of a ribbon item, and mainly have Title, Description, Command, UIType and ChildItems. The service is in charge to rebuild the ribbon when the main view changes.
A colleague of mine thinks this is bad MVVM, as users need to design their ribbon view in C# code and not in XAML, and he also say it would be hard in this way to make a group of items disabled or enabled at once, as each command of these items will need to update its CanExecute separately. Instead, he suggested to have a main Ribbon View and ViewModel files, where each developer that want to add a ribbon button for her module or view would need to add them in the View XAML and add a relevant command in the ViewModel. In addition, VisualStates will be used to determine what items will be displayed or enabled based on changes in the ViewModel (such as view change, or selection change). I really don't like this solution, mainly because all developers will have to put their modules knowledge in once big file.
Note that some items in the ribbon (e.g. Options, Exit) are common to the entire application, while some are relevant to a specific application domain and some are only relevant for a specific view.
Edit: I guess my main question is what is the recommended way to allow multiple development teams integrate on a single ribbon? Should we have a single RibbonView and single RibbonViewModel which will contain all of the possible items in a ribbon, and each team will add its items to these V/VM and also define the logic on when to show them (probably by using visual state)? Or do we allow every view, view-model or module register ribbon items (within their own C# code) against a service, and have the service then render the ribbon as needed when the active view changes with all items registered to that type? Or is there any better way to achieve this integration?
What do you think?
Do you have a better idea or an opinion about how to manage the single ribbon resource which is common to multiple developers?
Thanks,
splintor
I agree with Will's comment, your viewmodel should not care or know how it is being rendered or if the designers ever decide to change how it's rendered.
A ViewModel should only contain all required information for the presentation layer to render it.
So the ViewModel should have all the properties that the Ribbon bar needs bind to in order to function. Then you can use a Resources.xaml or some other strategy to present it.
Taking a shot in the dark I would try something like this for the ViewModels:
public interface IMenuViewModel : INotifyPropertyChanged
{
ICommand Command {get;}
string Title {get;}
string Description {get;}
UIType Type {get;}
IList<IMenuViewModel> ChildItems {get;}
}
I would then probably create an abstract class that provides implements INotifyPropertyChanged with a collection class the implements INotifyCollectionChanged to take care of the plumbing code.
I would then probably do something like this in the Resources.xaml
<DataTemplate DataType="{x:Type vm:IMenuViewModel}">
<StackPanel>
<Button Command="{Binding Command}" Content="{Binding Type}"/>
<ItemsControl ItemsSource="{Binding ChildItems}"/>
</StackPanel>
</DataTemplate>
to provide a default view for your viewmodels
and then all someone has to do to create an entry into your ribbon bar is
1) Implement IMenuViewModel
2) Optionally add another DataTemplate entry into your resources.xaml if they want their widget rendered differently like so:
<DataTemplate DataType="{x:Type vm:FooViewModel}">
<v:FooView />
</DataTemplate>
I hope I didn't dig to deep on how I would implement.
The main point is that a ViewModel should only expose properties required for the view to do it's job(which is rendering the ViewModel), not for the ViewModel to do the job or care how it's done.

Multiple ViewModels associated with a single view

I have a View that displays a DataGrid which is bound to an ObservableCollection in the ViewModel. For the sake of discussion, let's say we have a Team View containing a Team DataGrid, in which each row represents a Player.
My question is about what data type I should use to represent the players in my Team collection. Is it a good idea for the items in the collection to be ViewModels themselves? In this case, my Team View would be associated with a single Team ViewModel as well as any number of Player ViewModels (in the Team collection).
Does having multiple ViewModels associated with a single View violate any design guidelines for MVVM , and is there a preferred way of implementing this scenario?
Thanks!
No that is fine; each object should be a ViewModel in its own right. It makes for cleaner code, nicer interactions, and remember, if it works well then it's correct (even if it violates guidelines).
I would do it exactly the way you are prescribing. I'd bind my grid to a Team, which would have an ObservableCollection<Player>, where Player is another ViewModel-type class. Each row item would get the Player as its DataContext and so you're still binding to ViewModel properties as you'd expect: and Player can still have public properties for ICommands (likely RelayCommands) for manipulation!
Hope that helps!
Far from violating guidelines, I think this is recommendated design. At least in my projects you will see this pattern repeatedly.
This pattern comes in particularly useful in conjunction with DataTemplates. For example you could define a DataTemplate in your Application.Resources for your PlayerViewModel like so:
<DataTemplate DataType="viewModels:PlayerViewModel">
<StackPanel Orientation="Vertical">
<Image Source="/Images/Player.png"/>
<TextBlock Text="{Binding Name}"/>
</StackPanel>
</DataTemplate>
And then if you wanted to display a list of players you simply bind a ListBox etc to your TeamViewModel.Players ObservableCollection and you automatically get the above DataTemplate displayed for each player:
<ListBox ItemsSource="{Binding Players}"/>
I agree with both of the other answers (the ones by Kieren and Groky) but feel they fail to mention a very important consideration in this decision.
You should only create a view model if there is something view-specific about what you are doing. If all you are doing is binding to data and invoking commands which naturally belong on your model, there is no reason to create a view model.
For example, suppose:
Your Player object has a Name property, a Rank property, a Promote() method, and a Delete() method.
Your view is a simple one that allows you to edit the Name and Rank of any player, and also has buttons to promote and delete players.
In this case adding a view model between your view and your model is pointless. Such a view can bind directly to the model:
Bind TextBox.Text to the Name property
Bind Slider.Value to the Rank property
Bind the Promote button to the Promote() method
Bind the Delete button to the Delete() method
Note that instead of binding the Delete button to the Delete() method you may want to set its Command to ApplicationCommands.Delete and use a CommandBinding to invoke the Delete() method.
My point here is that in most cases if your models are well-designed there will be no need to insert a view model object. A view model is only really necessary when view-specific state needs to be tracked (such as "current Player"), conversions are too complex to be handled by simple binding, or you need commands that affect several different model objects and/or view model properties at the same time.
In my experience, if the model is correctly designed only about 50% or so of all views actually need a view model, and in the case of items in a list this is more like 20%.
An example of a time when you might use a view model for an item in a list is when you need to keep a separate "selected" flag that is part of your view but not of your model, and the basic functionality in ListBox is not enough.
By CLEAN COD SOLID principles, it is nice to associate one view model to one view.
Separation of concerns should be separated for each view and it's much easier to maintain the codebase in the future.
You can do it but it's not recommended.

Resources