Binding WPF TreeView with generic viewmodel - wpf

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

Related

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 do i access a combobox text in a usercontrol from the wpf forms viewmodel?

I have a UserControl with 4 combobox bound to collections in viewmodel for that usercontrol.
I have used this control in a wpf form. This wpf form has its own viewmodel.
How do i access the text from the 4 comboboxes within the wpf form's viewmodel?
EDIT: i saw that you have different viewmodels. now it depends of the use of your usercontrol and the use of mvvm:)
you can use messenger or eventaggregator to comunicate the seleteditems from usercontrolviewmodel to mainviewmodel.
you can also use RelativeSource binding in your usercontrol to bind the selecteditem to your mainviewmodel directly (usercontrol then is just a composition of controls).
you can can rid of the usercontrol viewmodel and put all in the mainviewmodel and take my old example
you can create DependencyProperties for the SelectedItems in your usercontrol!(not usercontrol viewmodel!) and bind these to the properties in your mainviewmodel. i think thats the cleanest way if the usercontrol should be a real usercontrol.
old example:
in your viewmodel: //the real code should of course implement INotifyPropertyChanged and raise it properly
public ObservableCollection<string> MyFirstCollection {get; set;}//init once, add,remove,clear to alter
public string MySelectedCombobox1Value {get;set;}
in your usercontrol:
<ComboBox ItemsSource="{MyFirstCollection }" SelectedItem="{Binding MySelectedCombobox1Value, Mode=TwoWay}" />
thats all relating to your question. be sure that you set the DataContext right. you can check this with tools like snoop. the code i posted expected that the dataconext for the combobox is the viewmodel.
The UserControl should inherit the data context of the form you're adding it to which would be the view model. Any bindings in the UserControl would then be relative to the inherited data context. Have you tried binding to a view model property to ComboBox.Text?
UPDATE
Sorry, misread your question. Didn't see that the user control already has its own view model.
While it seems like there's a better approach, you could expose dependency properties on the user control that exposé the text of each combobox. Just thinking out loud.
The only clean way to do this is with binding, and the only way that would be recommended is if the user control exposes a DependencyProperty for the ViewModel or the individual text properties (as was suggested by sellmeadog) for consumption. Then you can have a property in the parent ViewModel that binds directly to that Dependency Property.

How to access properties of a UserControl

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

Where should I put list of UserControls and not break MVVM?

I have a tab control where each TabItem is a UserControl. I'd like to hold the UserControls in the TabControl's ItemsSource. Does ItemsSource list go in the Window's ViewModel? If so, I feel like it's breaking MVVM since the ViewModel would now have GUI controls within it. Or do I put this list in the codebehind of the window that holds the tab control?
Any suggestions would be great!
With tab controls, more often than not the individual tabs are created statically in XAML rather than at run time by data binding. However there is no reason why you shouldn't do this. If you have a collection of views, they should definitely be stored in a view.
Bear in mind that you could also bind the ItemsSource to a list of ViewModels objects and WPF will generate a view for you with the ItemTemplate, with the ViewModel object set as the DataContext. This collection of ViewModels should be stored in a view model, although at some point a view model will obviously have to be stored in a view.
This can most likely be done in a number of ways, all which are up for debate on how "MVVM-friendly" they are.
My setup looks like the following.
My Main window has a DataContext bound to a MainWindowViewModel which contains an property
public ObservableCollection<Workspace> WorkspaceCollection{get;set;}
MainWindow has a TabControl which ItemsSource is bound to WorkspaceCollection
Workspace are all viewmodels and are bound to different views/usercontrols via DataTemplates
You might have a look at the Write sample application of the WPF Application Framework (WAF). It has a TabControl where each TabItem is a UserControl and it does this by applying the MVVM pattern.
Here's what I've done.
I created an interface that all of my controls implement, IMyAppControl, which has some information such as Title, Description, other metadata.
My Main Window has an ObservableCollection that the tab ItemsSource binds to.

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

Resources