How to access properties of a UserControl - wpf

I have an application that uses Caliburn.Micro. My View contains a user control which contains e.g. a tab control. I want to be able to access that tab control from the outer ViewModel to select a particular tab. Is it possible?
Thanks.

The standard MVVM way is to have the TabControls SelectedItem property bound to a property on your viewModel.
<TabControl ItemsSource="{Binding PropertyToYourViews}"
SelectedItem="{Binding PropertyToYourSelectedView}">
</TabControl>
If you do it this way your ViewModel does not have to know about the existence of the TabControl.
The next step is dependant on your implementation. Your outer ViewModel could simply keep a reference to the child viewModels SelectedView property and access it directly however,
If you want to keep your ViewModels decoupled then you will need to implement some sort of notification system. I'm not sure of the specifics of Caliburn.Micro but most MVVM frameworks offer some kind of solution for this.

Implementation would depend on exactly how you have it set up, but you can bind a variable in your view model to the SelectedItem of the TabControl

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.

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

working with WPF usercontrol and MVVM

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).

Binding WPF TreeView with generic viewmodel

I have a usercontrol which contains a TreeView control. I am using MVVM pattern.
I want to reuse this user control in different windows, each time binding the usercontrol to a different datacontext.
<UserControl Name="UserControl1".......>
..............
<TreeView ItemSource={Binding ...}...>
<HierarchicalDataTemplate...........\>
</TreeView>
..............
</UserControl>
In window 1, I want to bind a List<ObjectA> to the TreeView.
In Window 2, I want to bind a List<ObjectB> to the TreeView.
Is it possible to write a generic ViewModel for this usercontrol, so that I can bind different Types of data to the TreeView??
In case my question is not understood, please do let me know.
If I am reading this correctly, you have a UserControl which you wish to reuse, setting its DataContext to be different ViewModel's in different parts of your application...
that being so, yes you can certainly specify Lists of different types as an ItemsSource for your TreeView, but:
The list property must be consistently named within each ViewModel
You will need to describe a DataTemplate (or HierarchicalDataTemplate) for each type you expect to pass into the TreeView within your control's xaml
You may find that binding to an ObservableCollection<T> brings greater reward than a List<T> if you wish to add/remove items to/from the collection and hope to see those changes reflected in the UI
Hope this helps :)

Resources