WPF MVVM:设置选项卡视图的DataContext

时间:2016-05-09 07:15:06

标签: wpf mvvm datatemplate tabcontrol datacontext

我遇到了一种奇怪的绑定行为,描述为here。我做了很多故障排除,我得出结论,最可能的问题在于我如何设置每个标签视图的DataContext

我有一个TabControlItemsSource绑定到ViewModels列表。

MainView:
<TabControl ItemsSource="{Binding TabList}">
    <TabControl.Resources>
        <DataTemplate DataType="{x:Type vm:Tab1ViewModel}">
            <v:Tab1 />
        </DataTemplate>
    </TabControl.Resources>
...
</TabControl>

MainViewModel:
public ObservableCollection<TabViewModelBase> TabList { get; set; }
public MainViewModel()
{
    this.TabList = new ObservableCollection<TabViewModelBase>();

    // Tab1ViewModel is derived from TabViewModelBase
    this.TabList.Add(new Tab1ViewModel()); 
}

所以,现在MainViewModel有一个TabViewModelBase列表,我认为这是正确的MVVM方式。 Tab1的视图(TabViewModelBase)是使用DataTemplate定义的。

这就是问题所在:

Tab1:
<UserControl.Resources>
    <vm:Tab1ViewModel x:Key="VM" />
</UserControl.Resources>
<UserControl.DataContext>
    <StaticResourceExtension ResourceKey="VM" />
</UserControl.DataContext>

我想大多数人也会这样做,但...... 使用这种方法有一些非常错误

MainViewModel中,我手动实例化了Tab1ViewModel。在MainView中,我使用DataTemplate告诉视图在看到Tab1时使用Tab1ViewModel。这意味着MainView将实例化Tab1类的对象。

现在,Tab1需要其DataContext与自己的Tab1ViewModel绑定,因此我们使用StaticResource添加一个Tab1ViewModel,但这是一个全新的实例!

我需要将DataContext设置回我在MainViewModel中实例化的原始版本。那么,如何在DataContext内设置Tab1的{​​{1}}?

2 个答案:

答案 0 :(得分:3)

您不必在XAML中指定vm:Tab1ViewModel个新实例。您也不需要明确定义DataContext。只要ViewModel的类型与ViewModel中定义的类型DataTemplate相匹配,view的类型将会呈现DataContext ViewModel public ObservableCollection<TabViewModelBase> TabList { get; set; } public MainViewModel() { this.TabList = new ObservableCollection<TabViewModelBase>(); this.TabList.Add(new Tab1ViewModel1()); this.TabList.Add(new Tab1ViewModel2()); } {1}} DataTemplate。例如,如果list有两个对象,如下所示:

<TabControl ItemsSource="{Binding TabList}">
<TabControl.Resources>
    <DataTemplate DataType="{x:Type vm:Tab1ViewModel}">
        <v:Tab1 />
    </DataTemplate>
   <DataTemplate DataType="{x:Type vm:Tab1ViewModel2}">
        <v:Tab2 />
    </DataTemplate>
</TabControl.Resources>

并且您的Tab1是:

Tab2

...

  

然后两个标签将呈现Tab1&amp; Tab1ViewModel1(原因列表有2个项目)。 DataContext Tab2Tab1ViewModel2DataContextDataContext   var randDate = new Date(2016, 04, 29, 10, 15); randDate.setHours(0); randDate.setMinutes(0); randDate.setSeconds(0); randDate.setMilliseconds(0); var target = new Date(2016, 04, 29); target.setHours(0); target.setMinutes(0); target.setSeconds(0); target.setMilliseconds(0); console.log(target.getTime() === randDate.getTime()); //output: true var randDate = new Date(2016, 04, 29, 10, 15); //randDate is your now() on any day randDate.setHours(0,0,0,0); var now = new Date(); now.setHours(0,0,0,0); //strips h, m, s, ms from var now && var randDate var target = new Date(2016, 04, 29); //set up your target target.setHours(0,0,0,0); //strips h, m, s, ms if needed console.log(target.getTime() === randDate.getTime()); //output: true console.log(target.getTime() === now.getTime()); //output: false (Mon May 09 2016 00:00:00 GMT+0200 (Romance (zomertijd))) 。您无需指定import time now = time.clock() while now + 5 > time.clock(): print time.clock() time.sleep(1) print "Woke up"   明确。

答案 1 :(得分:2)

只是@KyloRen答案的补充:这就是所谓的&#34; ViewModel-First方法&#34;。根据您的viewmodel,选择视图 - &gt;你先拥有viewmodel。

但是,您甚至不需要为您的观看数据模板。为每个视图编写datatemplate会很烦人。

有相同的替代实现&#34; ViewModel-First&#34;原理:

<TabControl ItemsSource="{Binding TabList}">
  <TabControl.ItemTemplate>
     <DataTemplate>
        <ContentPresenter Content={Binding Converter={ViewModelToViewConverter}} />
     </DataTemplate>
  </TabControl.ItemTemplate>
</TabControl>

ViewModelToViewConverter接受ViewModel,并根据命名约定为其创建视图。这在基于页面的导航场景中尤其有用,但它是适用于许多情况的通用方法(导航,列表框,项目控件,动态内容演示者等)。

可以找到转换器的示例here - 只需将IocContainer替换为Activator.CreateInstance