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

问题描述

我的 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;          
        }        
    }
}

我想像这样从 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 是正确的,但它应该加载视图而不是显示此文本,当然:

Screenshot of the View

一个选项卡的 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}" />

这一行只显示绑定到的对象的 ToString() 值 它。 (在这种情况下是虚拟机)。

相反,您可以尝试使用 ContentTemplateSelection,您可以根据绑定到它的对象类型在运行时选择 ContentTemplate 的类型。

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/ 检查此以了解如何动态更改模板。在模板中,您可以使用任何类型的用户控件。