如何将TabControl绑定到ViewModels集合?

mic*_*ael 66 wpf tabcontrol mvvm tabitem prism-4

基本上我在我的MainViewModel.cs中:

ObservableCollection<TabItem> MyTabs { get; private set; }
Run Code Online (Sandbox Code Playgroud)

但是,我需要以某种方式不仅能够创建选项卡,而且还可以在维护MVVM的同时加载选项卡内容并将其链接到适当的视图模型.

基本上,我如何将用户控件作为tabitem的内容加载并将该usercontrol连接到适当的viewmodel.使这个困难的部分是ViewModel不应该构造实际的视图项,对吧?或者可以吗?

基本上,这将是MVVM合适的:

UserControl address = new AddressControl();
NotificationObject vm = new AddressViewModel();
address.DataContext = vm;
MyTabs[0] = new TabItem()
{
    Content = address;
}
Run Code Online (Sandbox Code Playgroud)

我只是问,因为我正在ViewModel中构建一个View(AddressControl),这对我来说听起来像MVVM禁忌.

小智 129

这不是MVVM.您不应该在视图模型中创建UI元素.

您应该将Tab的ItemsSource绑定到ObservableCollection,并且应该包含有关应该创建的选项卡的信息的模型.

以下是VM和表示标签页的模型:

public sealed class ViewModel
{
    public ObservableCollection<TabItem> Tabs {get;set;}
    public ViewModel()
    {
        Tabs = new ObservableCollection<TabItem>();
        Tabs.Add(new TabItem { Header = "One", Content = "One's content" });
        Tabs.Add(new TabItem { Header = "Two", Content = "Two's content" });
    }
}
public sealed class TabItem
{
    public string Header { get; set; }
    public string Content { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

以下是绑定在窗口中的外观:

<Window x:Class="WpfApplication12.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Window.DataContext>
        <ViewModel
            xmlns="clr-namespace:WpfApplication12" />
    </Window.DataContext>
    <TabControl
        ItemsSource="{Binding Tabs}">
        <TabControl.ItemTemplate>
            <!-- this is the header template-->
            <DataTemplate>
                <TextBlock
                    Text="{Binding Header}" />
            </DataTemplate>
        </TabControl.ItemTemplate>
        <TabControl.ContentTemplate>
            <!-- this is the body of the TabItem template-->
            <DataTemplate>
                <TextBlock
                    Text="{Binding Content}" />
            </DataTemplate>
        </TabControl.ContentTemplate>
    </TabControl>
</Window>
Run Code Online (Sandbox Code Playgroud)

(注意,如果你想在不同的标签中使用不同的东西,请使用DataTemplates.每个标签的视图模型应该是它自己的类,或者创建一个自定义DataTemplateSelector来选择正确的模板.)

哦,看看,数据模板中的UserControl:

<TabControl
    ItemsSource="{Binding Tabs}">
    <TabControl.ItemTemplate>
        <!-- this is the header template-->
        <DataTemplate>
            <TextBlock
                Text="{Binding Header}" />
        </DataTemplate>
    </TabControl.ItemTemplate>
    <TabControl.ContentTemplate>
        <!-- this is the body of the TabItem template-->
        <DataTemplate>
            <MyUserControl xmlns="clr-namespace:WpfApplication12" />
        </DataTemplate>
    </TabControl.ContentTemplate>
</TabControl>
Run Code Online (Sandbox Code Playgroud)

  • 花了一段时间才将其标记为答案,但我终于找到了DataTemplates部分的含义.只要我定义DataTemplate,WPF就会根据选项卡中ViewModel的类型自动连接Views/ViewModel. (5认同)
  • 好吧,选项卡的内容虽然是一个用户控件,所以我不会在我的ViewModel中创建新的UI实例吗? (4认同)
  • @michael:在您的示例中,您实际上是在ViewModel中创建UI元素.在我的例子中,我正在创建一个TabItem类型的模型.在您的示例中,TabControl(假设)将采用ViewModel实例化的TabItem并将其显示给用户.在我的应用程序中,它会看到其ItemsSource,为每个选项卡创建一个选项卡,并根据视图中元素的配置以及它所显示的项目类型来绑定每个选项卡的各个部分.它是一个主要的区别.你懂这个了吗? (4认同)
  • 如果你问我,'TabItem`是一个UI元素.为什么要在视图模型中创建? (3认同)
  • @Gusdor称之为你想要的任何东西."集团","foo","迂腐评论".无论您的设计需要什么. (3认同)
  • @将会感谢我没有对`sealed`的使用作出判断;) (3认同)
  • 我真的没有在你的答案中得到你的代码的第三部分.我没有得到的是<DataTemplate> <MyUserControl xmlns ="clr-namespace:WpfApplication12"/> </ DataTemplate>只保留TabControl的一个特定TabItem.如果我有另一个TabItem-UserControl类怎么办?我们来说MyUserControl2.我是否需要编写另一个TabControl来托管MyUserControl2? (2认同)

PVi*_*itt 20

在Prism中,您通常会将选项卡控件设置为一个区域,这样您就不必控制绑定的选项卡页面集合.

<TabControl 
    x:Name="MainRegionHost"
    Regions:RegionManager.RegionName="MainRegion" 
    />
Run Code Online (Sandbox Code Playgroud)

现在可以通过将自身注册到MainRegion区域来添加视图:

RegionManager.RegisterViewWithRegion( "MainRegion", 
    ( ) => Container.Resolve<IMyViewModel>( ).View );
Run Code Online (Sandbox Code Playgroud)

在这里你可以看到Prism的特色.视图由ViewModel实例化.在我的情况下,我通过Inversion of Control容器(例如Unity或MEF)解析ViewModel.ViewModel通过构造函数注入获取View注入,并将自身设置为View的数据上下文.

另一种方法是将视图的类型注册到区域控制器中:

RegionManager.RegisterViewWithRegion( "MainRegion", typeof( MyView ) );
Run Code Online (Sandbox Code Playgroud)

使用此方法允许您稍后在运行时创建视图,例如通过控制器:

IRegion region = this._regionManager.Regions["MainRegion"];

object mainView = region.GetView( MainViewName );
if ( mainView == null )
{
    var view = _container.ResolveSessionRelatedView<MainView>( );
    region.Add( view, MainViewName );
}
Run Code Online (Sandbox Code Playgroud)

由于您已注册View的类型,因此视图将放置在正确的区域中.