【问题标题】:c# mvvm bind views to tabcontrol with headerc# mvvm 将视图绑定到带有标题的 tabcontrol
【发布时间】:2015-05-26 01:23:18
【问题描述】:

我有一个带有主视图 (Window) 的 wpf 程序,其中包含一个 TabControl 以显示几个不同的 UserControl 视图(子视图,每个选项卡中都有一个)。每个 View 都有一个关联的 ViewModel。

我希望绑定TabControl,这样我只需将一个新的子视图加载到ApplicationViewModel中,它就会出现在TabControl上。

我已成功将 子视图 绑定到内容,但似乎无法在标题中获取任何内容。我希望将标头绑定到 sub-view 的 ViewModel 中的属性,特别是 TabTitle

应用程序视图(DataTemplate 绑定不起作用):

<Window ...>
    <DockPanel>
        <TabControl ItemsSource="{Binding PageViews}" SelectedIndex="0"> <!--Working-->
            <TabControl.ItemTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding DataContext.TabTitle}, Path=DataContext.TabTitle}" /> <!--Not Working-->
                </DataTemplate>    
            </TabControl.ItemTemplate>
        </TabControl>
    </DockPanel>
</Window>

Application ViewModel(ObservableObject基本上实现了INotifyPropertyChanged`):

class ApplicationViewModel : ObservableObject
{
    private DataManager Data;
    private ObservableCollection<UserControl> _pageViews;

    internal ApplicationViewModel()
    {
        Data = new DataManager();
        PageViews.Add(new Views.MembersView(new MembersViewModel(Data.DataSet)));
    }

    public ObservableCollection<UserControl> PageViews
    {
        get
        {
            if (_pageViews == null)
            {
                _pageViews = new ObservableCollection<UserControl>();
            }
            return _pageViews;
        }
    }

MembersView 背后的代码:

public partial class MembersView : UserControl
{
    public MembersView(MembersViewModel ViewModel)
    {
        InitializeComponent();
        DataContext = ViewModel;
    }
}

MembersViewModel(截断):

public class MembersViewModel : INotifyPropertyChanged
{
    public TabTitle { get; protected set; }

    public MembersViewModel(DataSet BBDataSet)
    {
        TabTitle = "Members";
    }

    //All view properties
}

我确定这很简单......

【问题讨论】:

  • 你不应该创建ObservableCollection&lt;UserControl&gt;,但ObservableCollection&lt;MembersViewModel&gt;ItemTemplate/ContentTemplate应该是MembersView
  • 如何告诉程序需要显示一个视图?
  • 如果您要绑定到ObservableCollection&lt;MembersViewModel&gt;,那么每个标题和内容的DataContext 将是MembersViewModel 的一个实例,它会被转换为带有DataTemplate 的UI 元素(ItemTemplate for标题和ContentTemplate 用于内容)所以标题中的TextBlock 看起来像&lt;TextBlock ... Text="{Binding TabTitle}"/&gt;ContentTemplate 将是 - 我假设 - MembersView 在同一显示 所有视图属性方式与标题相同。

标签: c# wpf mvvm tabcontrol


【解决方案1】:

您将 TabControl 绑定到 UserControl 类型的集合。这意味着每个项目的数据上下文将是 UserControl 类型。 UserControl 中没有名为“TabTitle”的属性,因此绑定不起作用。

我认为您想要做的事情可以通过以下更改来完成:

  1. 让 ApplicationViewModel 公开成员视图模型类型的集合,而不是 UserControl,并适当地填充它。
  2. 设置 ContentTemplate 以在 TabControl 中为您的项目创建视图:

    <TabControl.ContentTemplate>
        <DataTemplate DataType="{x:Type namespace:MembersViewModel}">
            <namespace:MembersView />
        </DataTemplate>
    </TabControl.ContentTemplate>
    

    (将“命名空间:”替换为包含控件的 xaml 导入命名空间。)

  3. 更新 TabControl 中的 ItemTemplate,使其正确绑定到视图模型:

    <TabControl.ItemTemplate>
        <DataTemplate>
            <TextBlock Text="{Binding TabTitle}}" />
        </DataTemplate>    
    </TabControl.ItemTemplate>
    
  4. 更新 MembersView 以具有无参数构造函数。视图上的 DataContext 将由 TabControl 为您设置。如果您需要从您的代码隐藏访问视图模型,它应该在 InitializeComponent() 调用之后通过 DataContext 属性可用。

任何时候您使用 ItemsControl(及其扩展,如 ListBox、TreeView、TabControl 等),您都不应该实例化您自己的项目视图。您总是希望设置一个基于数据(或视图模型)实例化视图并直接绑定到 ItemsSource 属性中的数据(或视图模型)的模板。这允许为您设置所有项目的数据上下文,以便您可以绑定到它们。

编辑:由于您有多个视图/视图模型配对,您需要稍微不同地定义您的模板:

<TabControl ItemsSource="{Binding PageViews}" SelectedIndex="0">
    <TabControl.Resources>
        <DataTemplate DataType="{x:Type namespace:MembersViewModel}">
            <namespace:MembersView />
        </DataTemplate>
        <DataTemplate DataType="{x:Type namespace:ClassesViewModel}">
            <namespace:ClassesView />
        </DataTemplate>
        <DataTemplate DataType="{x:Type namespace:SessionsViewModel}">
            <namespace:SessionsView />
        </DataTemplate>
    </TabControl.Resources>
    <TabControl.ItemTemplate>
        <DataTemplate>
            <TextBlock Text="{Binding TabTitle}}" />
        </DataTemplate>    
    </TabControl.ItemTemplate>
</TabControl>

不同之处在于您要在资源中定义多个数据模板,每种类型一个。这意味着它每次遇到这些类型时都会使用这些模板。您仍然希望设置 ItemTemplate 以强制选项卡标题使用特定模板。但是,不要设置 ContentTemplate,允许内容使用资源中定义的数据模板。

我希望这是有道理的。

附:您还可以在更高级别的资源字典中定义这些数据模板,例如在您的主窗口或您的应用程序中,如果您希望它们应用于您使用这些视图模型的每个地方的内容呈现器,而不仅仅是在这个TabControl .

【讨论】:

  • 好的,所以我现在(在 ApplicationView 中)得到:&lt;DockPanel&gt; &lt;TabControl ItemsSource="{Binding PageViewModels}" SelectedIndex="0"&gt; &lt;TabControl.ContentTemplate&gt; &lt;DataTemplate DataType="{x:Type viewmodels:TabbedViewModelBase}"&gt; &lt;views:MembersView /&gt; &lt;/DataTemplate&gt; &lt;/TabControl.ContentTemplate&gt; &lt;TabControl.ItemTemplate&gt; &lt;DataTemplate&gt; &lt;Label Content="{Binding TabTitle}" /&gt; &lt;/DataTemplate&gt; &lt;/TabControl.ItemTemplate&gt;。这显示了第一个选项卡的视图 ok (MemberView),如何为每个选项卡设置视图而无需单独定义它们?(PageViewModels 返回 VM 集合)
  • 您的课程设置方式一定是我遗漏了一些东西。您在内容模板中指定的TabbedViewModelBase 类型是什么?我以为类型是MembersViewModel。仅部分了解您在做什么,很难回答您的问题。
  • ApplicationViewModelApplicationView 是主要应用程序。目前,ApplicationView 只持有一个TabControl。我希望每个选项卡都包含一个 子视图MemberViewClassesViewSessionsView),每个选项卡都有一个关联的 ViewModel(MemberViewModel 等)。为了拥有这些子视图模型的集合,每个模型都扩展TabbedViewModelBase(这只是实现了INotifyPropertyChanged并定义了一个TabTitle属性)。 ApplicationViewModel 现在为 TabControl 提供一个 ObservableCollection&lt;TabbedViewModelBase&gt; PageViews 以绑定到
  • 好的,现在我明白了。所以你有三个不同的视图/视图模型对。因此,我们需要稍微不同地定义内容模板。我更新了我的答案。
猜你喜欢
  • 2018-10-25
  • 2018-11-25
  • 1970-01-01
  • 2018-12-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多