WPF/MVVM - binding collection of child controls - wpf

some advice from he WPF/MVVM gurus, pls.
I come from a Windows Forms background and I am porting a personal project from VB.NET to WPF. I started with a straight re-write with the logic going in the code-behind files, albeit with a discrete Data Access Layer.However then I discovered MVVM and that I was 'doing it all wrong' and should be abstracting the logic into a View Model accessing a data model. I think I've absorbed the new paradigm and my code behind files are now (nearly) empty.
(As an aside, I buy into the rationale behind MVVM, but I'm not sure about the 'easier to test and debug' argument, due to all the 'plumbing' of routed events and commands etc, I appreciate the need but it seems to me sometimes to obscure rather than clarify what is going on. But I a very much a novice, maybe it will come with practice.)
Anyhow - here's what has got me scratching my head; the app is a dive planning/recording tool for scuba diving centres. Logically, its like a calendar or diary app, with the need to record the time and site of each dive and who went on each dive. The main screen resembles one page of a diary and I have a user control that encapsulates the dive info. On change of selected date, the diary page is cleared and re-populated with the dives for the new selected date. The View Model retrieves and exposes a list of 'Dive' objects (classes) for the new date. The container is a Stackpanel and I want to clear its children and then create and add the new Dive user controls to the child controls collection.
My question is - where is the appropriate place to do this - View code behind or View Model? The former is relatively easy but seems to me to break the pattern, but I am stumped by how I would achieve it in the view model. The sequence needs to be
User selects a new date (calendar control)
Handle the selected date change event
Clear Stackpanel child controls
Retrieve list of dives for new date from db and generate a user control for each
Add user controls as stackpanel child controls.
I guess what I'm asking is - is there a way of binding the collection of child controls to a collection of objects in the view model such that it responds dynamically - and is this a sensible approach?

The appropriate way to do this would be to wrap your StackPanel in an ItemsControl and bind the ItemsSource to your Dives of the selected day. When your DivesOfThatDay is changed (make sure to implement INotifyChanged or a DependencyProperty, or use an ObservableCollection), every single entry will be generated automatically.
<ItemsControl ItemsSource="{Binding DivesOfThatDay}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type Dive}">
<!-- Your Template -->
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
In MVVM you should always avoid generating Controls in Code-Behind. Your View should take the Data from your ViewModel and do that on its own - otherwise MVVM would be kind of useless. In your case it should work like this:
User selects a new date in the view
Because the date control in the view is bound to some property in your viewmodel, that property gets updated.
The viewmodel reacts to this change by clearing its current collection of dives and fetching a new one.
The model data (from the database) gets encapsulated in a viewmodel (a Dive) and added to the dive collection in the viewmodel
The view (ItemsControl) gets notified of this change (via INotifyPropertyChanged, INotifyCollectionChanged or whatever) and tells its ItemContainerGenerator to update the controls.
The ItemsContainerGenerator generates a view for every Dive in your viewmodel and adds it to the stackpanel.
Or in short V → VM → M → VM → V

Related

How can my design implement MVVM?

I have an issue trying to understand how to implement the MVVM pattern in my application. It's a small application and I will explain what it does.
My application creates a backup of files. The UI lets the user choose which folder they want to back up and where it should be backed up too. After making their selection they click the "start" button.
This then passes the folder source and folder destination to a class library (called backup.cs) which creates the back ups of all the files inside each folder. During this, a log (Log.cs) is created logging each stage and the state of each file it attempted to back up (complete, failed, other, etc). Now, the log is in memory only.
When the back up is complete I want a window to open (a view) which will display all the logs. It's at this point I can't understand how to use the MVVM pattern.
As it stands today, I pass my log (which holds the data in a hierarchical way) to my MainWindow's constructor and bind to the datacontext, using a treeview in my xaml I get the desired results. However, I now want to use MVVM.
My question is very similar to my previous question, as you can see the answer is to pass the log as a paramter to the ViewModel constructor. The issue is, I don't know how to do that and also display a window!
The only way (in my head) I can achieve this is by passing the Log as a parameter to a constructor of my View but this defeats the point of the MVVM. I could pass the parameter to my ViewModel's constructor (which would fit the MVVM pattern) but would that then mean I have to also create an instance of my View from my ViewModel constructor as well? Otherwise all I would do is create my ViewModel but have no way to display the results since the View isn't displayed.
I hope I have explained where I'm struggling clearly, can any one suggest a way forward please?
Most likely you 'll want the viewmodel to accept (and expose through a property) a collection such as a List<Log> -- typically this would be an ObservableCollection<Log>, but if the operation has already completed there is no real point in going that way. This is what you are describing as a possible solution.
To wire the viewmodel to the view, in essence you need to do this:
var viewModel = new LogsViewModel(...);
var view = new LogsView(); // no constructor parameters
view.DataContext = viewModel;
And finally you add view at some place of the application window's logical tree so that it gets displayed. MVVM frameworks automate this procedure, but you can also do it manually as simply as this.
Your view would then bind to the logs collection to display each log, perhaps using a DataTemplate:
<ItemsControl ItemsSource="{Binding Logs}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<!-- XAML to display each Log does here -->
<TextBlock Text="{Binding FileName}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
As an example, if you wanted that LogView being shown based on a button click in you main View.
public override void ShowCommandExecute()
{
var popup = new LogsView
{
WindowStartupLocation = WindowStartupLocation.CenterScreen,
DataContext = new LogsViewViewModel();
};
popup.ShowDialog();
}

Why the view constructor is not called every time I create a new viewmodel?

I am using WPF with model-view-viewmodel pattern. I have a ResultsView and ResultsViewModel which are connected like this:
<DataTemplate DataType="{x:Type VM:ResultsViewModel}">
<VW:ResultsView/>
</DataTemplate>
In my main window I have some kind of paged setup where MainContent property stores the current page (ViewModel instance).
I create the page like this
MainContent = new ResultsViewModel();
ResultsView is based on UserControl, it also has a handler for Loaded event which does some UI initialization stuff.
Everything works fine while the user is navigating between different pages.
But if the user opens the ResultsView two times in a row, the ResultsView constructor is not called the second time, and also Loaded event is not called. It seems that now I have the same old ResultsView instance attached to the new ResultsViewModel()!
Why WPF does not create a new View each time I create a new ViewModel? Is there some way I can force WPF to discard the old view if the old viewmodel is destroyed?
<DataTemplate x:Shared="False" DataType="{x:Type VM:ResultsViewModel}">
<VW:ResultsView/>
</DataTemplate>
See Kent's answer for practical work-around for your issue.
That said, it's a good practice to only instantiate the View once, as there is overhead associated with constructing the Visual Tree and setting up all of the bindings. Generally the View/ViewModel should be designed so that you can swap out the underlying ViewModel without the View caring or even noticing (other than DataContext changing and therefore all binding values being re-evaluated.) If you currently have logic in your Loaded event that prepares for a specific ViewModel, consider registering for notification of when DataContext changes instead (see this example).

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.

WPF events, commands or using both

I am constructing my app infra structure, and finding it hard to achieve a very basic behavior - I want to raise events from different user controls in the system and being able to catch those events on some other user controls that listens to them. For example i have a user control that implements a TreeView. I have another user control that implmements a ListView. Now, i want my ListView to listen to the TreeView, and when the selection is changed on the TreeView, i want to repopulate my ListView accordingly.
I also want this to happen even if the ListView is not located within the TreeView on the WPF logical tree.
PLEASE HELP!!
Thanks,
Oran
Use data binding.
If the content of the list view is stored inside the object shown in the tree view you can just bind into the tree SelectedItem property.
Otherwise bind the tree SelectedItem to a property in your view models (or your window!) and in the setter of this property change the list that is bound to the list view ItemSource property.
You can see the technique in this series on my blog the post I linked to is the last post with the code download link, you'll need to read from the beginning of the series if you want the full explanation.
EDIT: Here's how I did it in one project: (the GridView definition removed since it's not relevant here)
<TreeView
Name="FolderTree"
Width="300"
ItemsSource="{Binding Root.SubFolders}"
ItemTemplate="{StaticResource FolderTemplate}"/>
<ListView
Name="FileView"
ItemsSource="{Binding ElementName=FolderTree, Path=SelectedItem.Files}">
</ListView>
The list bound into the tree view's ItemsSource is of objects that have 3 properties: Name (that is bound to a TextBlock in the FolderTemplate), SubFolders (that is likewise bound to the HierarchicalDataTemplate.ItemsSource property) and Files that is bound to the ListView using {Binding ElementName=FolderTree, Path=SelectedItem.Files}
Note that non of the lists are observable collections (because in this project they never change) but are loaded lazily (on-demand) by the properties getters (because in this project they are expensive to load).
This is the point where the added complexity of MVVM (Model-View-ViewModel pattern) can start to pay off. What you need is a publish/subscribe infrastructure, and MVVM Light has that, along with good MVVM structure that doesn't get overly complex. Prism is another good WPF/Silverlight infrastructure foundation with publish and subscribe support.

Resources