Lazily instantiate a rarely used a view model? - c#

My main view model (mainViewModel) contains an inner view model (innerViewModel) that is bound to it's view (innerView) using a DataTemplate.
In my mainView I have a ContentPresenter that has it's Content property bound to the innerViewModel and it's Visibility bound to an IsVisible property. The Visibility property is Collapsed and will very rarely be set to Visible.
My mainViewModel is one of ~1000 in a list. Currently each instance of mainViewModel also has an instance of innerViewModel that is almost never used. Is there a standard way of lazily instantiating the innerViewModel such that it is created only when it's view becomes visible? The normal approach of lazily instantiating does not work, since innerViewModel is used in a binding.
Perhaps there is a better approach to housing a rarely seen control within another one?

Try using the Lazy object for lazy initialization.

"Is there a standard way of lazily instantiating the innerViewModel such that it is created only when it's view becomes visible?"
The solution is to do just that. Now I don't actually create an instance of innerViewModel until the IsVisible property is first set to true. This means that the Content of the ContentPresenter on the mainView is bound to null for most of the time, but this does not seem to be a problem.

Related

WPF: When/where are Collection Views created for a bound collection?

I'm familiar with the concept that a collection used for a WPF Binding (for example, ItemsSource) actually uses the default collection view for that collection, but I am looking for some clarification on where that happens. Potentially depending on that answer, what is the recommended way to implement similar binding behavior if I am creating my own UserControl that has its own property like ItemsSource and I want to use ICollectionView functionality on it within my UserControl?
So if my UserControl has a ItemsSource property like:
public static readonly DependencyProperty TestPropProperty =
DependencyProperty.Register("TestProp", typeof(System.Collections.IEnumerable), typeof(MyUserControl),
new FrameworkPropertyMetadata(null, TestPropChanged, null));
And my XAML uses it like this:
<local:MyUserControl TestProp="{Binding MyCollection}"/>
Let's say MyCollection is of type ConfigCollection, my own type, derived from ObservableCollection. From MyUserControl's perspective, the binding sets TestProp to an object of type ConfigCollection (NOT a Collection View object).
The MS docs: https://learn.microsoft.com/en-us/dotnet/desktop/wpf/data/?view=netdesktop-6.0#binding-to-collections
Under "Using a default view", state:
WPF also creates a default collection view for every collection used as a binding source. If you bind directly to a collection, WPF binds to its default view.
That seems to be pretty universal, so in my case I was thinking it might be set as a Collection View whose source was MyCollection, but that is not the case.
So, does the "magic" of translating a collection into its default collection view all happen within each control's code? Is there a recommended way of making sure my ItemsSource can accept either a collection (and use the default view) or accept a Collection View and then use that specific view rather than the default?

Using ViewModels in ObservableCollections in Prism

As far as I know, the default way to use a ObservableCollection that is bound to a listview is with model classes as elements (ObservableCollection<MyModel>). So when a listview element is selected, we use NavigateAsync and pass the model, which then can be used by the ViewModel to "fill itself".
The problem with this approach is, that it's not possible to use ViewModel properties for binding in the listview.
For example:
I have a View, ViewModel and Model "PickList", which contains a collection of "PickLine" objects - each having a View, ViewModel and Model themselves. The PickLine object contains a property "PickedQuantity" and a property "OpenQuantity". Now in my PickList view, I don't want to bind these two to separate items (e.g. two labels), but I want to have one label to display both I a format like for example "PickedQuantity / OpenQuantity". I know this example can be solved by using multi binding or something like this. But that's not the meaning of it all.
My PickLine ViewModel already has a property "QuantityString", that I want to bind to the label of a listview element via DataTemplate. But how can I do this. Is it even possible?
Make a property that combines the two other properties and bind to that. E.g.:
public string FullQuantity {get {return $"{PickedQuantity} / {OpenQuantity}";}}
Then in the setter for PickedQuantity and OpenQuantity, you will want to call whatever PropertyChanged method you have set up to notify the bindings of a property change and pass in the FullQuantity property name so elements that are bound to FullQuantity get updated when either PickedQuantity or OpenQuantity are changed.
This way, you are only binding one label's text to one property and that label would get updated when either of the two quantity properties are changed.
Note: I am unfamiliar with Prism, but this approach should work regardless of the Mvvm framework in use.
Your PickListViewModel should expose a collection property whose items are of type PickLineViewModel (not PickLine).
Whether you need an ObservableCollection<PickLineViewModel> depends on where changes can happen - in service / model that initially created the PickLines or in the GUI or both. In any way, you have to make sure the changes are propagated from one side (the collection of view models) to the other (the collection of models). Google wrapping observable collection as a starter (hint: avoid two-way sync if possible). These blog posts are old but still relevant and make a good reading. A trivial wrapping is described in this answer.

Synchronized child property in MVVM

Question is implementation pattern for the following.
A container UI has child UIs. For example, the container has a property of ObservableCollection<ChildItemViewModel> that is applied to <ChildItem DataContext="{Binding}" /> in XAML template.
I want a property of ChildItemViewModel synchronized among hosted ChildItems. As setting/unsetting PropertyChanged event seems messy, binding is preferable. An idea is that the parent UI has a dependency property bound in two-way to a corresponding dependency property (also bound to a property of the ChildItemViewModel) of every child UI in the template, which still seems redundant to prepare such dependency properties.
May I have your declarative and MVVM-natural way?
If what you are talking about is setting a property on the parent view model from it's child view model the way I usually do it is as follows:
Add a constructor to the child view model that takes the parent view
model as a parameter and assign that to a field in the child view
model.
Add a method to the parent view model to change the desired
property.
Call method from child property (using the 'parent' field).
Hope that is helpful.

WPF MVVM Change Model

I have a UserControl where the data gets passed in via a dependency property.
The UserControl is backed by a view model and the data is assigned to a property of the view model.
The XAML binds to both properties in the view model as well as properties within the passed in data.
The problem is if the user changes the data then databinding with the UI breaks. The UI is still bound to the original data object.
How can I cause the binding to get refreshed? INotifyPropertyChanged is implemented throughout, but it is not the property that is getting stale, but the binding. Basically, how do you go about replacing a backing model?
Since I'm not sure my description is clear I will try to describe it again in pseudo code.
<MyControl Source="{Binding Data}"/>
Where source is a dependency property of MyControl. In the PropertyChangedCallback this data is handed to the view model.
MyViewModel.Data = Source;
Within the MyControl XAML things are bound to this model.
{Binding Path=MyViewModel.Data.Item}
If you are wondering why the dependency property is defined in the UserControl, it is because it is a reusable control and the end user should not know about the view model.
"The UserControl is backed by a view model" and thus breaketh the application.
The UserControl's DataContext should be the Model. Period. If you need to perform UI logic, do it in the codebehind. Need something else? Create DependencyProperties on the surface of your UserControl to supply them.
When you create a ViewModel specifically for your UserControl, you break the natural flow of the DataContext (at this point, probably the Model the UC is designed to work with) and binding within the UserControl. It's pointless in most cases and harmful in some (as you have noticed).
My canonical answer on the subject contains more details.
Are you trying to swap-out the DataContext of the usercontrol during an application's session?
If you are, then I am fairly confident that this will not work.
Have you considered spinning up a new instance of that user control with the other DataConext?

MVVM how best to display ListBox.SlectedItem

I have a ListBox bound to a list of ViewModel objects on the LHS of my screen. The ListBox.SlectedItem is bound to a property on my "MainWindowViewModel", called CurrentItem. On the RHS I have a large area that displays the selected item in detail, containing many nested controls for editing and browsing the information the ViewModel contains.
There are two approaches to building the view that I am aware of.
The large area on the RHS is a ContentPresenter with the Content property bound to CurrentItem. In my resources, define a template to be used with my ViewModel object.
The large area on the RHS is a fixed UserControl containing all the required xaml to display the my ViewModel. I bind the DataContext property of my UserControl to CurrentItem.
Which is the nicer approach and why? Perhaps option 2 is better in general but option 1 could be used when there are view models of different types in the ListBox, each with their own template?
Personally I would not bind directly to the selected Object in the ViewModel because doing that you are breaking the MVVM Pattern.
What I would suggest to do is to create a ViewModel for the whole view Including your list of ViewModels and adding a SelectedViewModel Property that is bound to the selected item. So the selection infromation is also in the ViewModel.
Then you can bind the SelectedObject of the "parent" ViewModel to the RHS.
I would suggest reading this MSDN Article for an introduction how to display SelectedItems using the MVVM Pattern.

Categories