Binding to ViewModels that are displayed using a ContentPresenter and DataTemplates? - wpf

I am currently using an ItemsControl at the top of my application that contains a few buttons, bound to a list of ViewModelBases. These VMs are the pages of my app. When clicked, they set the current page to the appropriate page. A content presenter shows the current view model, and a DataTemplate converts it into the correct view.
How can I use Data Binding in these views? Since the view is being created by the ViewModel, doesn't that mean I can't create a new instance of that view model in the view? How can it find the correct instance to bind to? Or, is it automatically bound?

WPF ItemsControls automatically set the DataContext of each Item they render on screen to their corrsponding Item in the underlying collection. This means that, if you have an IEnumerable set as the ItemsSource of an ItemsControl (or any other ItemsControl-derived class, such as ListBox), it will render every item on screen (using any available DataTemplates) and automatically assign the DataContext property of the resulting visual elements to each of the ViewModelBase Items.

Related

"Nested" MVVM Question

I have a WPF window that has a datagrid and a user control for a form for the fields in that datagrid. The user control and the WPF window have view models.
The user control's DataContext is bound to one of the window's view model's member field, whose value changes during the data grid's Selection Changed event.
I'm not sure if this is the right way to do it because I am unable to create references from the inner view model to the outer view model for some reason. Constructor injection won't work because I'm required to use default constructor only, and I can't seem to put a property injector in the right place (always getting null reference when I try to use it).
I am also unable to get my property change notification to work properly in the inner view model.
Is there a better way to wire my view models so that they automatically change the values in the user control when a new row is selected in the datagrid? I have a feeling that binding to the control's DataContext is not the way to go.
This doesn't seems a complex/nested scenario. Looks like pretty much a normal master details scenario. Suppose you want to edit Customer data, I would have an ObservableCollection instance bind to the DataGrid, and there will be a SelectedCustomer property in the VM also. In the DataGrid you can set SelectedItem twoway bind to the SelectedCustomer property which makes SelectedCustomer always updated with your selection. Since the usercontrol has the same instance of customer as in the DataGrid row, whenever you change anything in the UC those data will get reflected in the grid. Ofcourse all those properties should fire NotifypropertyChanged.

Master View implementation using MVVM in Silverlight

I have a silverlight datagrid to which I am binding an observable collection from the viewmodel. There is a detail view page which will display different properties of the object in the collection when user selects a row of the datagrid. My requirement is when user updates any properties in the detail view; the data should be updated in the data grid also. How to implement this functionality?
Well, the answer is simply to bind both the datagrid row and the control displaying the selected object. The simplest way is to use a ICollectionView (returned by a CollectionViewSource from the original ObservableCollection), bind the grid's ItemsSource to that, and then bind the control's DataContext to the ICollectionView's CurrentItem. That way, when the grid's selected item changes, the CurrentItem of the ICollectionView is updated, and that item is displayed in the detail view.
I think it's quite easy but if you need additional details or sample source code I'll elaborate.

WPF TabControl add extra tabs to a bound control

I have a Tab Control with a ItemsSource Binding.
...
I want to add a predefined tab to the front called All that has an aggregate of all the other tabs, and I would also like to add a button at the end called Add so I can add a new tab.
Is there an easy way of doing this ?
Thanks,
Raul
The easiest way is to go with MVVM (the example in the url acutally contains TabControl bound to a ViewModel). Your ViewModel that you bind your TabPages against could expose an observablecollection of items where the first item is always a ViewModel instance that holds you aggregate data. All follwing items are the ViewModel instances for the rest of the tabpages. Your ViewModel would also expose a ICommand AddTabPage wich adds a new item to the obeservablecollection. The TabPage will pick up this change automatically. You'd have a button whose Command property is bound to this command.

How to set a value in a ViewModel from binding?

Kind of an odd question- if I'm thinking of this the wrong way please let me know. I am using an infragistics dock manager, which manages tabs as well. So I can create a TabGroupPane, and then add multiple ContentPanes, each of which has its own tab.
In each content pane, I set my viewmodel:
<ContentPane>
<viewmodels:MyViewModelForTab1 />
</ContentPane>
So here's the problem- while using a mediator pattern for communication, my viewmodels have no idea if they are on the visible tab or not, so they are always working even if hidden. The TabGroupPane does have a SelectedTab property, as well as each ContentPane having an IsActive property.
So the question is how do I set that information in my ViewModel? Making my VM a dependency object seems like a bad idea since I already implement INotifyPropertyChanged. Using a CLR prop in my VM also doesnt work, since you cannot bind to it.
How can I get my VM to know if it is the datacontext of an active tab?
Thanks!
I would put an IsSelected property on my ViewModel and bind it to the TabItem's IsSelected dependency property.
This should allow you to hook into when it is updated and perform whatever you need. You don't need a mediator pattern here since you are communicatining from the View to the ViewModel.
Make sure your ViewModel is bound to the DataContext property of the view (specifically, that the Tab's DataContext is the ViewModel). The way you have it now, your ViewModel is the content of the element, not bound to the DataContext, as it should be:
<Tab.Resources>
<viewmodels:MyViewModelForTab1 x:Key="Tab1ViewModel" />
</Tab.Resources>
<ContentPane DataContext="{StaticResource Tab1ViewModel}" />
Or something like that...
I don't know the Infragistics model, so my apologies if this is inapposite, but here's how I implement this with regular items controls - tab control, list box, whatever.
Create a container view model class that includes an observable collection of items and exposes a SelectedItem property. Make the container class the data context of the items control. Bind the items control's SelectedItem property to the container class's.
Hook the item objects up to the PropertyChanged event of the container. So now when the selected item in the UI changes, the container view model notifies all of the items that SelectedItem has changed. Each item object's event handler can determine for itself whether or not it's now the selected item.
The item objects thus don't know any implementation details of the UI - you can unit test your classes outside of a UI and the logic will still work correctly.

Sharing state between ViewModels

I have two ViewModels that present the same Model to different Views. One presents the model as an item in a ListBox, the other presents it as a tab in a TabControl. The TabControl is to display tabs for the items that are selected in the ListBox, so that the tabs come and go as the selection changes.
I can easily synchronise the two controls by adding an IsSelected property to the Model and binding the ViewModels to it (a bit like this), but this will clutter the Model with presentation details that don't really belong there.
It seems like I need something between the Model and ViewModels to hold this extra state. Are there any patterns or examples of a good way to do this?
Use a ViewModel.
You've got a View that contains the two controls. Have a view model that will contain a list of ViewModels for the ListBox control to bind to. Also within this view model bind the listbox selection to a second list of viewmodels that the TabControl then also binds to.
That way your listbox drives what the tab control shows without this information entering the model which should stay oblivious to the existence of the view.
TabControl is ItemsControl, so you shouldn't be shy to bind its ItemsSource to ListBox.SelectedITems.
Obviously ViewModel for List should have a property that would produce ViewModel for Tabs:
public TabViewModel ItemTabModel { get { ... } }
And because TabControl is a bit funny, you'd need to add ItemContainerStyle to populate Content for TabControlItem, because the normal ItemTemplate for TableControl only affects headers for tabs.

Resources