如何从主视图模型动态地用一个用户控件填充选项卡控件的项目列表的每个选项卡

How to fill each tab of the tab control's itemslist with one user control dynamically from the mainviewmodel

我的 MainView 包含一个带有 ItemTemplate 和 ContentTemplate 的 TabControl。 TabControl 的 ItemsSource 绑定到我的 MainViewModel.

中的 属性 ObservableCollection<TabViewModel> TabCollection

TabViewModel:

    namespace LuxUs.ViewModels
{
    public class TabViewModel
    {
        public string Name { get; set; }
        public object VM {get; set;}      
        
        public TabViewModel(string name)
        {
            Name = name;
        }
        public TabViewModel(string name, object vm)
        {
            Name = name;
            VM = vm;          
        }        
    }
}

我想用它们的标签 header 和来自 MainViewModel 的动态内容创建标签,如下所示...:

MainViewModel:

    using System.Collections.ObjectModel;

namespace LuxUs.ViewModels
{
    public class MainViewModel : ObservableObject, IPageViewModel
    {
        
        public ObservableCollection<TabViewModel> TabCollection { get; set; }

        public MainViewModel()
        {
            TabCollection = new ObservableCollection<TabViewModel>();
            TabCollection.Add(new TabViewModel("Dachdefinition", new DachdefinitionViewModel()));
            TabCollection.Add(new TabViewModel("Baukörperdefinition"));
            TabCollection.Add(new TabViewModel("Fassade"));
            TabCollection.Add(new TabViewModel("Raumdefinition"));
            TabCollection.Add(new TabViewModel("Treppenloch | Galerieöffnung"));
            
        }       
    }
}

查看:

<UserControl  x:Class="LuxUs.Views.MainView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:LuxUs.Views"
             xmlns:models="clr-namespace:LuxUs.Models"
             xmlns:vm="clr-namespace:LuxUs.ViewModels"
             mc:Ignorable="d">
    <Grid>
        <Grid>
            <TabControl Style="{DynamicResource TabControlStyle}" ItemsSource="{Binding TabCollection}" >
              
                <TabControl.ItemTemplate>
                    <DataTemplate>
                        <TextBlock Text="{Binding Name}"/>
                    </DataTemplate>
                </TabControl.ItemTemplate>
                <TabControl.ContentTemplate>
                    <DataTemplate>
                        <UserControl>
                            <ContentControl Content="{Binding VM}" />
                        </UserControl>
                    </DataTemplate>
                </TabControl.ContentTemplate>
            </TabControl>
        </Grid>
    </Grid>
</UserControl>

...但是每个选项卡的内容不会显示。相反,我得到了这段文字。 ViewModel 是正确的,但它应该加载视图而不是显示此文本,当然:

第一个选项卡的 ViewModel DachdefinitionViewModel 只有一个空构造函数:

    using System.Collections.ObjectModel;

namespace LuxUs.ViewModels
{
    public sealed class DachdefinitionViewModel : ObservableObject
    {       
        public DachdefinitionViewModel()
        {
  
        }  
    }
}

这是它的视图 Dachdefinition.xaml:

    <UserControl x:Class="LuxUs.Views.Dachdefinition"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:LuxUs.Views"
             xmlns:vm="clr-namespace:LuxUs.ViewModels"
             mc:Ignorable="d">
    <UserControl.DataContext>
        <vm:DachdefinitionViewModel></vm:DachdefinitionViewModel>
    </UserControl.DataContext>
    <Grid Margin="50">
...
...
...
 </Grid>
</UserControl>

这里的绑定是正确的还是我需要以不同的方式绑定?为什么视图没有显示在第一个选项卡中?

您的 TabControl 声明应如下所示:

<TabControl ItemsSource="{Binding TabCollection}">
    <TabControl.Resources>
        <DataTemplate DataType="{x:Type vm:DachdefinitionViewModel}">
            <local:Dachdefinition/>
        </DataTemplate>
    </TabControl.Resources>
    <TabControl.ItemContainerStyle>
        <Style TargetType="TabItem">
            <Setter Property="Header" Value="{Binding Name}"/>
            <Setter Property="Content" Value="{Binding VM}"/>
        </Style>
    </TabControl.ItemContainerStyle>
</TabControl>

并且 Dachdefinition UserControl 不能设置自己的 DataContext 属性,因为 DataContext 值应该继承自控件的父元素,即 TabItem。

<UserControl x:Class="LuxUs.Views.Dachdefinition" ...>
   <!--
       do not set UserControl.DataContext here
   -->
   <Grid Margin="50">
       ...
   </Grid>
</UserControl>

您需要通过 DataTemplate 将视图模型连接到正确的视图。

DataTemplate 为没有它的数据类型(您的视图模型)提供可视化表示。如果您不指定 DataTemplate,您将获得默认值:带有对象字符串表示形式的 TextBlock(ToString() 方法的结果,默认为类型名称)。

<Grid>
    <Grid.Resources>
         <DataTemplate DataType="{x:Type vm:DachdefinitionViewModel}">
              <views:Dachdefinition />
         </DataTemplate>
    </Grid.Resources>

    <TabControl Style="{DynamicResource TabControlStyle}" 
                ItemsSource="{Binding TabCollection}" >
      
        <TabControl.ItemTemplate>
            <DataTemplate>
                <TextBlock Text="{Binding Name}"/>
            </DataTemplate>
        </TabControl.ItemTemplate>
        <TabControl.ContentTemplate>
            <DataTemplate>
                <UserControl>
                    <ContentControl Content="{Binding VM}" />
                </UserControl>
            </DataTemplate>
        </TabControl.ContentTemplate>
    </TabControl>
</Grid>

是的,数据绑定有问题。<ContentControl Content="{Binding VM}" />

This line would just display ToString() value of the object bound to it. (VM in this case).

Instead you can try using ContentTemplateSelection where you can choose the type of ContentTemplate in run time based on the type of object bound to it.

class TabContentTemplateSelector:DataTemplateSelector
{
    public DataTemplate DefaultTemplate { get; set; }
    public DataTemplate DachdeTemplate { get; set; }
    public override DataTemplate SelectTemplate(object item, DependencyObject container)
    {
        if (item is TabViewModel tabViewModel)
        {
            if (tabViewModel.VM != null && tabViewModel.VM is DachdefinitionViewModel)
            {
                return DachdeTemplate;
            }
            else
            {
                return DefaultTemplate;
            }
        }
        return base.SelectTemplate(item, container);
    }
}


   <DataTemplate x:Key="DachdeTemplate">
      
    </DataTemplate>

    <DataTemplate x:Key="SomeOtherTemplate">
        <TextBlock Text="{Binding Name}"/>
    </DataTemplate>


    <local:TabContentTemplateSelector x:Key="myTabContentTemplateSelector"
                                      DachdeTemplate="{StaticResource DachdeTemplate}"
                                      DefaultTemplate="{StaticResource 
                                      SomeOtherTemplate}"
                                      />
</UserControl.Resources>
<Grid> 
        <TabControl  ItemsSource="{Binding TabCollection}" 
                    ContentTemplateSelector="{StaticResource 
                    myTabContentTemplateSelector}" > 
            <TabControl.ItemTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding Name}"/>
                </DataTemplate>
            </TabControl.ItemTemplate> 
        </TabControl>
    </Grid> 

https://www.c-sharpcorner.com/UploadFile/41e70f/dynamically-selecting-datatemplate-for-wpf-listview-way-1/ 检查此以了解如何动态更改模板。在模板中,您可以使用任何类型的用户控件。