DataContext for binding a UserControl with ViewModel within DataTemplate
What I'm trying to achieve is:
- Have a ListView bound to an ObservableCollection of ItemRecords.
- Have a TabControl that contains detailed view for all the ItemRecords in the ListView that were selected for editing.
- Each TabItem contains a UserControl ("ItemInfo") that uses ItemInfoViewModel as its VM (and, not so coincidentally, DataContext).
- ItemInfo UserControl needs to be populated with the开发者_如何学运维 data from the corresponding ItemRecord.
To achieve that, I'm trying to pass the ItemRecord (selected in the ListView) to ItemInfoViewModel.
Finally, the question: what do you think would be the best way to do this, without breaking the MVVM pattern?
The not-so-elegant way that I see (and it actually doesn't exactly follow the MVVM principles) is to have a DependencyProperty ItemRecord defined in the UserControl, provide its value via binding, and in the constructor (in the UserControl's code-behind) pass the ItemRecord to the VM (which we get by casting the DataContext).
The other problem is with how to actually pass the ItemRecord via binding.
Once I set the VM as the UserControl's DataContext, I cannot just use {Binding}
to specify the current item in TabControl's source collection.
At the moment I am binding to the TabControl's SelectedItem using ElementName - but this doesn't sound too robust :-(
<localControls:TabControl.ContentTemplate>
<DataTemplate>
<ScrollViewer>
<localControls:ItemInfo ItemRecord="{Binding ElementName=Tabs, Path=SelectedItem}"/>
</ScrollViewer>
</DataTemplate>
</localControls:TabControl.ContentTemplate>
Any good advice will be greatly appreciated!
Alex
I think your problem is you're not quite understanding the MVVM pattern here; you're still looking at this as the different controls talking to each other. Where in MVVM, they should not be, each control is communicating with the view model independently of all the others. And the view model controls (and supplies) the logic which tells the controls how to behave.
So, ideally you would have something like:
public ObservableCollection<ItemRecord> ListViewRecords
{
get { ... }
set { ... }
}
public IEnumerable<ItemRecord> SelectedListViewRecords {
{
get { ... }
set { ... }
}
The ListViewRecords
would be bound to the ItemsSource
property of your ListView
(the actual properties might vary based on the specific controls you're using, I'm used to the Telerik suite at the moment so that's where my head is). And the SelectedListViewRecords
would be bound to the SelectedItems
property of the ListView
. Then for your TabControl you would have:
public ObservableCollection<MyTabItem> Tabs
{
get { ... }
set { ... }
}
public MyTabItem SelectedTab
{
get { ... }
set { ... }
}
Again, you would bind the Items
property to the Tabs
and SelectedItem
to the SelectedTab
on your TabControl
. Now your view model contains all the logic, so in your SelectedListViewRecords
you might do something like this:
public IEnumerable<ItemRecord> SelectedListViewRecords {
{
get { ... }
set
{
_selectedRecords = value;
NotifyPropertyChanged("SelectedListViewRecords");
Tabs.Clear(); // Clear the existing tabs
// Create a new tab for each newly selected record
foreach(ItemRecord record in value)
Tabs.Add(new MyTabItem(record));
}
}
So the idea here is that the controls do nothing more than send and receive property changes, they know nothing of the underlying data, logic, etc. They simply show what their bound properties tell them to show.
精彩评论