working with WPF usercontrol and MVVM - wpf

I have following questions
Should the consumer of my usercontrol assign the usercontrol's DataContext or set some dependency property. (related to #3 : if DataContext then my individual items need to bind directly to the object given in DC, if DP then I have the luxury to have bind to any VM)
If they set property, and if I am using 3 primitive items, should I accept them as individual properties or combine them together to a Model for my usercontrol
Should I ask the consumer of my usercontrol to send me model or viewmodel ( I say viewmodel but for all the controls I have used so far, I have never seen anybody asking me to send them VM - I am sure some could be implementing MVVM internally

Your consumer wants a user control. So I presume the user control should be able to work in any context/application(WPF). So, to answer your questions
1) The consumer should set dependency properties which is defined in the user control. By using the datacontext you will be coupling the usercontrol to the consumer.
2)Take them as individual primitive properties, otherwise the consumer needs to create an object unnecessarily to cater with your model(coupling again-why should the consumer need to know about your model?).
3)No, you should not ask the cosumer to send you the view model.Why do you need to know which consumer is using your "generic" user control.
If you cannot do any of the above because of practical considerations - then dont worry about breaking any/all the rules because your user conrol is coupled with a specific context-it is not generic any more. If you write a generic user control, any WPF application can use your user control. It is your call.

1.
I would say this depends on the kind of UserControl, if it is "generic" you should be able to change the DataContext as the control internally should not have anything to do with the DataContext. For example if i create an ImageButton UserControl which exposes the properties Caption and ImageSource then those should be bound internally independent of the DataContext, the on the instance those can be bound and the DataContext may be changed as well, e.g.
<uc:ImageButton Caption="{Binding ButtonInfo.Caption}"
ImageSource="{Binding ButtonInfo.Image}"/>
Here one could then change the DataContext to simplify the bindings a bit:
<uc:ImageButton DataContext="{Binding ButtonInfo}"
Caption="{Binding Caption}"
ImageSource="{Binding Image}"/>
If on the other hand the UserControl is a view for a viewmodel i would expect the UserControl to bind to viewmodel properties internally, relative to the DataContext.
So in a DataTemplate where the current DataContext is already the viewmodel of that view a simple instance without anything should do, i.e.
<v:StatisticsView />
If the viewmodel to be passed is in a property of the current DataContext you may bind the DataContext as well:
<v:StatisticsView DataContext="{Binding StatisticsViewModel}"/>
2.
This can be handled either way i would say, especially if you have only three properties its not too much of a hassle to create those. You might want to consider some aspects like dependency, e.g. does it make sense to group all three propeties in an object?
3.
As noted in 1. this should be apparent from the UserControl itself, if it's a StatisticsView the consumer should pass in a StatisticsViewModel (either implicitly by inheriting the current DataContext or by binding it explicitly).

Related

Binding Parent View DataContext to DataTemplate Child View DataContexts

I've been experimenting with WPF, Xaml, MVVM, and DependencyInjection lately. Consequently, I am creating a UI using MVVM principles. A certain portion of the UI is designed to act like a wizard wherein not all of the available options are presented to the user at the same time. Each section of options is its own View (sub-View) with a single View (Parent View) hosting these sub-Views in a ContentControl. The user sets certain options and uses buttons to move from one section to the other.
View Navigation
To switch between these views I'm using a DataTemplateSelector with each sub-View defined as a DataTemplate in my Xaml resources.
Content Control in the Main View:
<ContentControl Content="{Binding ElementName=ParentViewControl, Path=ViewState, Mode=TwoWay}"
ContentTemplateSelector="{StaticResource MyTemplateSelector}" />
Example sub-View Data Template:
<DataTemplate x:Key="SubViewATemplate">
<local:SubViewAView x:Name="SVAView" DataContext="{Binding ElementName=ParentViewControl, Path=DataContext}" ViewState="{Binding ElementName=ParentViewControl, Path=ViewState, Mode=TwoWay }" />
</DataTemplate>
On the Parent View and each sub-View I've created a Dependency Property called ViewState (an enum). These bind to each other through the DataTemplates. In each View's code-behind I update this ViewState Property based off user input and it propagates up to the Parent View which in turn triggers the DataTemplateSelector. So far, so good. The navigation works beautifully.
ViewModel Info
The Parent View has a ViewModel which implements INotifyPropertyChanged as its DataContext. I'm attempting to use this single ViewModel to bind Properties to the Parent View and the sub-Views. The problem is the DataContext binding in the DataTemplate snippet above does not work. (Which is odd to me since the ViewState binding does.) After various attempts to get this to work, the DataContext on a sub-View is either null or the ViewState control variable.
I am currently using the UnityContainer as my dependency injector.
Various Attempts
Here are the various other things I've tried that have all failed:
1) Registered the ViewModel as a singleton in the UnityContainer thereby using Constructor Injection on the sub-Views to set the DataContext. (Does not work because there must be a Parameter-less Constructor for the DataTemplate resource.)
2) Registered the ViewModel as a singleton in the UnityContainer and then using Property Injection on the sub-Views to set the DataContext. (Does not work. I think this is due to the UnityContainer not working when an object is instantiated in Xaml.)
3) Creating sub-ViewModels for each sub-View that needs a ViewModel to display properties that would have existed on the Parent ViewModel. I've used this before to get around the Xaml instantiation problem with the UnityContainer. I then replace the DataContext binding in the DataTemplate with the associated sub-ViewModel. (Does not work because for some reason the DataContext of my Parent View is getting set to the ViewState variable instead of remaining my ViewModel which I've set in the view's Constructor. This in turn means the sub-ViewModel property on my Parent ViewModel can't be found to bind to the DataContext of the sub-View.) Are the Content and DataContext of UserControls the same thing? Does setting one affect the other?
4) Moving the ViewState dependency property from the View to the ViewModel and then setting the ContentControl's Content to bind to the ViewModel. This violates MVVM principles but by this time I was trying anything to get this to work. (It doesn't work because when ViewState is changed in the code-behind of the view the ViewModel does not trigger as changed.) I haven't gone any further with this one because I didn't want to go deeper violating MVVM.
Conclusion
I've found most of these attempted solutions on this site over the last couple days. I haven't had any formal training in WPF, Xaml, and MVVM so I suspect I'm missing something obvious, or am attempting to do something that isn't possible. I'm going to keep attempting variations on the above and researching until I find something that works, but I thought I would tap into the collective knowledge here to help me find a solution.
What I'd prefer is to have the group of Views use the single ViewModel as their DataContext so I can bind properties to their controls. And have the Views' navigation be controlled by a DataTemplateSelector. Is there a way to do this that I'm not seeing?
Thank you for your time!
I have had similar issues before, I have had good luck using a RelativeSource binding. Maybe try something like this:
DataContext="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=ContentControl},Path=DataContext}"
Just a thought.

When creating VMs for UserControls, who should create and set the VM?

I'm getting a little confused about how to layout my code in MVVM - if I have a UserControl with a corresponding VM class, how should other controls consume my UserControl?
Should consumers bind directly to the VM or should I duplicate only a subset of these properties I want to actually be used as DependencyProperties of the UserControl?
For that matter, should the UserControl's VM be injected into the UserControl's code-behind or should the VM of any control that uses this UserControl contain it as a dependency and bind it to the UserControl instead?
Just to make it clear: Suppose I have a ListBox in a UserControl and use it in a Window that is already implemented with MVVM. But I'm confused about the implementation of the UserControl VM and the corresponding bindings.
I would think the ideal solution would be to expose the SelectedItems of the ListBox via dependency properties in the UserControl, and then the Window which uses the UserControl would bind to these.
Or should the Window's VM have a reference to the VM as a property, have it injected and bind directly to the properties on that instead?
Should dependency properties only be defined in UserControls or can / should they be defined in the VM?
I'm thinking the Window would bind, from within the XAML of the Window, either via
{Binding ElementName=myUserControl, Path=SelectedItems}
or
{Binding Path=MyViewModel.SelectedItems}
It just seems to make more sense to do it via the former, since the latter requires that the View knows about another VM?
> how should other controls consume my UserControl?
Via exposed dependancy properties on the usercontrol only.
>Should consumers bind directly to the VM or should I duplicate only a subset of these properties I want to actually be used as DependencyProperties of the UserControl?
Each control should be a stand alone entity, there shouldn't be any secret handshake (either to or from) to use the control. Think of the design like you are Microsoft and many different users will use your controls. So answer #1 is just as relevant; think a stand alone entity.
>I would think the ideal solution would be to expose the SelectedItems of the ListBox via dependency properties in the UserControl, and then the Window which uses the UserControl would bind to these.
The window which hosts your controls will have a View Model which contains a Observable list of data items. That will hold the data which the user control(s) will bind via their dependency properties. THink of it as a producer pattern with many consumers. The consumers are the controls. Whether the controls have VMs or not is immaterial to the running of the main program; for each control is its own island.
Keep in mind when working with WPF and MVVM that your View layer is merely a user-friendly way of drawing your Models and ViewModels, and that your View is not actually your application. Your View actually has to know the basics about your data layer so it can define how to draw it.
So if your application needs to display a list of Items and maintain a SelectedItem, than that should be in your ViewModel or Model somewhere, not in the actual View layer.
Typically for me UserControls are one of two things:
Either a standalone UserControl that can be used anywhere without a specific DataContext, and that expose DependencyProperties for any control-specific values. Examples are things like a Calendar control or a Popup control
<local:MyUserControl Items="{Binding SomeItemList}"
SelectedItem="{Binding SomeItem}" />
Or they are a UserControl that is meant to be used with a specific ViewModel only. This is far more common for me. The ViewModel is a property somewhere in the data layer, and I usually have an implicit DataTemplate in the application somewhere to tell WPF to use that UserControl anytime it needs to render that specific ViewModel
<DataTemplate DataType="{x:Type local:SomeViewModel}">
<local:MyUserControl />
</DataTemplate>
<ContentPresenter Content="{Binding SomeViewModelProperty}" />
Also, at no time should you set the DataContext of a UserControl from inside the UserControl itself, because the UI layer is only meant to be a UI representation of your data layer (your Models/ViewModels), and by setting the data layer from inside the UserControl you are making it so that the UserControl cannot be used to draw any other data object.

Binding instances being re-used in DataTemplates when ValidationRules can't be shared

I'm looking for a solution and/or the rationality behind why a Binding instance is shared within a DataTemplate. This ultimately comes down to the fact that within a DataTemplate, there's seemingly no way to force a new instance of a Binding on a DependencyProperty for each control generated. That is probably a fair and good assumption in all cases except when there are ValidationRules representing something specific about the instance of that control.
To elaborate (I can provide code, but I don't believe it's necessary), I use a DependencyPropertyDescriptor on IsEnabled to update one or more ValidationRules that belong to a TextBox.Text Binding, DatePicker.Text Binding, or a ComboBox.SelectedValue Binding, etc. The idea is that validation will be different or undesired when a control is not enabled.
Because of this, the ValidationRule's state of IsEnabled is specific to the individual control and because the ValidationRule Collection is a part of the Binding and the Binding instance is being shared - each control that ends up sharing that binding will update/override the previous IsEnabled value that was applied by the previously generated control's IsEnabled value.
IsEnabled is only one of at least two properties in the ValidationRule (another custom IsRequired DependencyProperty) that represent the state of the control to which the Binding is applied. When working outside of a DataTemplate (IE: The Binding instance is not shared), this works very well and ignores/alters the validation logic based on the state of the control. I'm not closed off to alternatives but do feel this has been (this issue aside) a very flexible and dynamic option that allows the Binding instance ValidationRule's and the control-changed state of the Rules to evolve effortlessly. This has also allowed me to avoid other obvious but much uglier options such as creating several Bindings, each representing one of the combinations of the ValidationRule's control properties and switching the whole binding out at the time the DependencyPropertyDescriptor fires. shivers
Any thoughts are MUCH appreciated!
I suggest you to use x:Shared attribute in the DataTemplate definition.
<DataTemplate x:Key="DataTemplateKey" DataType="{x:Type YourType}" x:Shared="False">
...
</DataTemplate>
Since you use it WPF will create a new DataTemplate instance on every request.

How to expose view-model properties as externally bindable dependency properties?

I'm writing a WPF user control which internally uses MVVM. In order to work out-of-the-box, it creates its own view-model (this part is not relevant to the question).
My control exposes a number of dependency properties. The host view (whoever uses my control) can set these DPs or bind to them. Some of these DPs have corresponding view-model properties.
This is what I want to achieve (see diagram): the DPs in my control's view are bound to the corresponding properties of the view-model. Additionally, the host view needs to be able to set or bind these DPs, too - the same way you use any other control.
However, as soon as the host view sets or binds the DP's value, the original binding (binding it internally to the control's view-model) is lost. This is because every DP can only have a single binding set. So this doesn't work:
One solution is to make the view-model a DependencyObject and its properties DPs. This way, I could bind the control's view-model to its view rather than the other way around, effectively reversing the left arrow. However, for various reasons, I want my view-model to use plain C# properties and INotifyPropertyChanged.
Any ideas on how to expose view-model properties as externally bindable dependency properties?
Edit:
To clarify on the requirements: the view does not in fact create the view-model itself (I realize that would violate the separation of concerns); rather, it uses a view-model locator to do so. (It's a terribly modular enterprise application.) I know this is not a typical approach (see Rachel's answer). However, accepting the described set-up, is there any way to achieve the desired result?
So the situation is that you have VM and View, they must be data bound to each other and VM can`t be DependencyObject? Well, triple binding VM-V-Host is not possible, AFAIK.
One possible approach is to make duplicate set of DependencyProperties in View. Original properties will be bound to VM and will affect the look of View, duplicate properties will be bound to Host and somehow affect the VM (with DP changing logic, for example).
If I'm creating a ViewModel for a CustomControl, it is expected that the user will use that ViewModel as the CustomControl.DataContext. Any other time, my UserControl's don't have ViewModels associated with them.
For example, suppose I wanted a PopupControl.
I would either create a UserControl with all custom properties and logic in the code-behind the UserControl (no ViewModel)
<local:Popup Header="{Binding PopupHeader}"
IsVisible="{Binding IsVisible}" />
OR
I would create a PopupViewModel and build my PopupUserControl expecting that the DataContext will be of type PopupViewModel
<DataTemplate DataType="{x:Type local:PopupViewModel}">
<local:PopupView />
</DataTemplate>
<ContentControl Content="{Binding MyPopupViewModel}" />
Properties like IsVisible or PopupHeader would exist in the ViewModel, not in the PopupUserControl

Binding Silverlight UserControl to a complex object

What is the "best" (or the most-often-used) approach when binding a complex class to a user control for re-use?
I am trying to create some reusable libraries for classes, and I am not sure which approach I should use. Example: I want to create an Address library that defines and Address class (with properties Line1, Line2 etc.), it's validation logic and an AddressControl which acts as the viewer/editor with bound fields for each property.
In use I might have a customer class with BillingAddress, DeliveryAddress properties and I would want to bind these in my customer control thus:
<addressLib:AddressControl [xxx]="{Binding BillingAddress}" />
So the question is what do I put in XXX?
Initially I thought of creating a DependencyProperty 'Address' on the control:
<addressLib:AddressControl Address="{Binding BillingAddress}" />
But now I am thinking surely I could just use the existing DataContext property?
<addressLib:AddressControl DataContext="{Binding BillingAddress}" />
Is this the best approach? Are there any issues e.g. updates or NotifyPropertyChange issues?
many thanks for your help!
One difference to remember is that with dependency property you get change notification and with datacontext you don't (at least not untill SL5 is out)
Setting the DataContext of your control to your model (in this case a BillingAddress) is the way to go. If your Control is used in a DataTemplate for an ItemsControl then the DataContext would be of course just "{Binding}".

Resources