Synchronized child property in MVVM - c#

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.

Related

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.

binding wpf data templates to a hierarchy of view models

I have a task as part of a larger story to set some properties in a view to be read-only. These properties are part of a hierarchy of view-models and have been matched to views using data templates from an associated resources dictionary. The data templates follow a very simple style, eg.
<DataTemplate DataType="{ x:Type vm:MyTextFieldViewModel}">
<v:TextFieldView />
</DataTemplate>
Using the example here, the view model MyTextFieldViewModel has a public property in a base class it uses for read-only (ie. IsReadOnly). MyTextFieldViewModel is a child of the parent ViewModel that is being used here.
Is there a way in the data template to bind the IsReadOnly property of the MyTextFieldViewModel to its parent ViewModel? Or can I bind the matching property in the view to something outside of the view model specified in the data template (ie. the IsReadOnly property in the view get bound to the matching property in the parent ViewModel)?
Just to clarify a little - there isn't scope for doing this another way as this will involve a lot of extra work that we cannot justify at this point. If we are going to do anything it's in the Data template. We use Data templates elsewhere that bind ViewModels to controls (eg. to textboxes and comboboxes) where we do something similar to what I'm asking about here.
Thanks in advance for any help you can provide.

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?

How to access another control's viewModel

I have a CustomControl B, which uses a DataContext/MVVM (viewModelB). Now I want to bind one Property of my CustomControl to another control A (uses viewModelA as DataContext).
So I have two Ideas:
Whenever PropA in viewModelA changes, I could directly update PropB in the viewModelB. But this creates a dependency between the viewModels, which seems ugly to me. Or is this a common way in the MVVM pattern and can't be avoided?
As an alternative I could think of a dependency property on CustomControlB and wire it to CustomControlA's viewModel by a binding, something like that:
<myControlB PropB={Binding ElementName=myControlA, Path=DataContext.PropA} />.
So far so good, but the dependency property is defined on the view now. How should I visualize it?
a) Should I transfer the value (from the property wrapper) to viewModelB and bind to it from viewB's XAML code?
b) Or should I directly update the view from B's codeBehind? Would this be still a proper MVVM "style"?
Which of the options would you recommend?
regards
Andreas
As long as ViewModelA doesn't actively update ViewModelB, there is no real coupling between the two viewmodels. What I mean is that if your main view model (which knows both viewmodels) is the one that wires up the binding, the view models are still loosely coupled.
So to me any of these are fine:
Bind directly to myControlA.DataContext.PropA from XAML
Have the MainViewModel register for ViewModelA's property changed event and modify ViewModelB's property as necessary. Here MainViewModel knows about the two view models, but they know nothing of each other.

Lazily instantiate a rarely used a view model?

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.

Categories