C# 将TabItems添加到现有的TabControl WPF/MVVM

C# 将TabItems添加到现有的TabControl WPF/MVVM,c#,wpf,xaml,mvvm,C#,Wpf,Xaml,Mvvm,我有TabControl,它已经在XAML上定义了一些TabItems。我需要创建新的TabItems并添加到其中 如果我使用ItemSource我会得到一个异常Items集合在使用ItemSource之前必须为空。 到目前为止,我找到的解决方案是创建那些我已经在XAML上定义过的TabItems,但是是在ViewModel上以编程方式定义的,这样我就可以创建我真正需要的其他项,但这似乎不是一个好的解决方案 另一种解决方案是添加选项卡控件作为属性,并使用代码隐藏将其绑定到ViewModel,我希

我有
TabControl
,它已经在
XAML
上定义了一些
TabItems
。我需要创建新的
TabItems
并添加到其中

如果我使用
ItemSource
我会得到一个异常
Items集合在使用ItemSource之前必须为空。

到目前为止,我找到的解决方案是创建那些我已经在
XAML
上定义过的
TabItems
,但是是在ViewModel上以编程方式定义的,这样我就可以创建我真正需要的其他项,但这似乎不是一个好的解决方案

另一种解决方案是添加
选项卡控件
作为属性,并使用代码隐藏将其绑定到ViewModel,我希望避免这种情况

所以,我只是想知道是否有一种方法可以只使用
XAML
和MVVM来实现这一点

编辑:

ItemSource尝试,正在运行

XAML:


您在这里所做的是而不是MvvM。其背后的想法是将应用程序的各个部分分开,即模型不应返回任何UI元素。如果您想将其与任何其他UI框架(例如
WinForms
)一起使用,那么它将失败并需要额外的工作。
您需要的是这样的东西,请记住这是一个示例,您需要修改它以符合您的要求。
模型类:

namespace Model
{
    public class Profile
    {
        public string Name { get; set; }

        public static int I { get; set; } = 2;
    }
}  
在此之后,您将需要ViewModel:

namespace VM
{
    public class MainViewModel : BaseViewModel
    {
        public MainViewModel()
        {
            ProfilesCollection = new List<Profile>();
            for (int i = 0; i < 100; i++)
            {
                ProfilesCollection.Add(new Profile() {Name = $"Name {i}"});
            }
        }

        private List<Profile> profilesCollection;   

        public List<Profile> ProfilesCollection
        {
            get { return profilesCollection; }
            set { profilesCollection = value; OnPropertyChanged(); }
        }
    }
}  
命名空间虚拟机
{
公共类MainViewModel:BaseViewModel
{
公共主视图模型()
{
ProfilesCollection=新列表();
对于(int i=0;i<100;i++)
{
添加(新配置文件(){Name=$“Name{i}});
}
}
私人列表档案收集;
公共列表配置文件集合
{
获取{return profiles集合;}
设置{profilesCollection=value;OnPropertyChanged();}
}
}
}  
现在我们有了一个合作的基地。在此之后,我假设您知道如何在xaml中添加相关引用,但其他人可能会看到这一点,所以我还是会将其包括在内。
以下是完整的MainWindow.xaml:

<Window x:Class="SO_app.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:vm="clr-namespace:VM;assembly=VM"
    xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity" 
    xmlns:converter="clr-namespace:SO_app.Converters"
    xmlns:validation="clr-namespace:SO_app.Validation"
    xmlns:scm="clr-namespace:System.ComponentModel;assembly=WindowsBase"
    xmlns:local="clr-namespace:SO_app"
    xmlns:sys="clr-namespace:System;assembly=mscorlib"
    xmlns:model="clr-namespace:Model;assembly=Model"//reference to my model
    mc:Ignorable="d"
    Title="MainWindow" Height="452.762" Width="525" Closing="Window_Closing">
<!-- d:DataContext="{d:DesignInstance Type=vm:MainViewModel, IsDesignTimeCreatable=True}" -->
<Window.Resources>
    <CollectionViewSource Source="{Binding ProfilesCollection}" x:Key="profiles"/> // this corresponds to our collection in VM
</Window.Resources>
<Window.DataContext>
    <vm:MainViewModel/>//Data Context of the Window
</Window.DataContext>

<Window.Background>
    <VisualBrush>
        <VisualBrush.Visual>
            <Rectangle Width="50" Height="50" Fill="ForestGreen"></Rectangle>
        </VisualBrush.Visual>
    </VisualBrush>
</Window.Background>
<TabControl>
    <TabControl.Resources>
        <DataTemplate DataType="{x:Type model:Profile}">//this data template will be used by the TabControl
            <Grid>
                <TextBlock Text="{Binding Name}"/>
            </Grid>
        </DataTemplate>
    </TabControl.Resources>
    <TabControl.ItemsSource>
        <CompositeCollection>
            <TabItem Header="First Item"/>
            <TabItem Header="SecondItem"/>
            <CollectionContainer Collection="{Binding Source={StaticResource profiles}}"/>
        </CompositeCollection>
    </TabControl.ItemsSource>
</TabControl>

//这与我们在VM中的集合相对应
//窗口的数据上下文
//TabControl将使用此数据模板


如果您想添加更多的项目,那么只需使用将在VM中实现的命令,只需向其中添加
profile
,即可享受节目。

使用
XAML
的唯一方法是设置所有
TabItems
,但将其可见性设置为
false
,然后在需要时显示它们。如果您使用的是
ItemSource
,那么我建议您使用
ItemSource
检查是的,但是我有一些固定的
TabItem
,以及其他我必须动态创建的,我有必要在
XAML
上创建固定的,以及ViewModel中的其他,但似乎我将不得不做“一切或没有”的风格。谢谢你的回答!你能给我们展示一下
ItemsSource
尝试的XAML吗?当然可以@XAMlMAX,但就像我说的,这很好。问题是当我混合使用这两种方法时,
XAML
和vm中TabItems的定义您是否听说过
CompositeCollection
CollectionViewSource
?我会尝试一下,但是,根据您的第一句话,您在这里做的不是MvvM。其背后的想法是将应用程序的各个部分分开,即模型不应返回任何UI元素。我已经读过,模型拥有UI的属性,而ViewModel管理这些属性,如果我理解你,如果UI的属性在ViewModel上,那么模型中有什么?模型是你的数据库表示。所以不管你用什么来存储数据。在我的例子中,通常是MS SQL。我将实体框架类分类为模型。现在,这些类具有UI使用的属性,即
。您的答案与预期一样有效。关于您最后的评论,您将把我现在在模型中的
ObservableCollection
移动到ViewModel,对吗?如果我需要模型中的项目,请使用绑定SelectedItem或类似的东西。您应该使用
ObservableCollection
,但不能使用
选项卡项
。用模型类替换
TabItem
(看我的答案),这就是它的工作方式。
DataTemplate
将用于在UI中显示内容,您只提供模型和视图模型中的数据,而不提供UI元素。要知道,唯一的问题是
TabItem
content
是一个
UserControl
ViewModel,我想我必须创建一个类,在这个类中,我将VM作为一个属性,并像您的示例中那样绑定它,而是使用
TextBlock.Text
使用
ContentPresenter.Content
namespace VM
{
    public class MainViewModel : BaseViewModel
    {
        public MainViewModel()
        {
            ProfilesCollection = new List<Profile>();
            for (int i = 0; i < 100; i++)
            {
                ProfilesCollection.Add(new Profile() {Name = $"Name {i}"});
            }
        }

        private List<Profile> profilesCollection;   

        public List<Profile> ProfilesCollection
        {
            get { return profilesCollection; }
            set { profilesCollection = value; OnPropertyChanged(); }
        }
    }
}  
<Window x:Class="SO_app.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:vm="clr-namespace:VM;assembly=VM"
    xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity" 
    xmlns:converter="clr-namespace:SO_app.Converters"
    xmlns:validation="clr-namespace:SO_app.Validation"
    xmlns:scm="clr-namespace:System.ComponentModel;assembly=WindowsBase"
    xmlns:local="clr-namespace:SO_app"
    xmlns:sys="clr-namespace:System;assembly=mscorlib"
    xmlns:model="clr-namespace:Model;assembly=Model"//reference to my model
    mc:Ignorable="d"
    Title="MainWindow" Height="452.762" Width="525" Closing="Window_Closing">
<!-- d:DataContext="{d:DesignInstance Type=vm:MainViewModel, IsDesignTimeCreatable=True}" -->
<Window.Resources>
    <CollectionViewSource Source="{Binding ProfilesCollection}" x:Key="profiles"/> // this corresponds to our collection in VM
</Window.Resources>
<Window.DataContext>
    <vm:MainViewModel/>//Data Context of the Window
</Window.DataContext>

<Window.Background>
    <VisualBrush>
        <VisualBrush.Visual>
            <Rectangle Width="50" Height="50" Fill="ForestGreen"></Rectangle>
        </VisualBrush.Visual>
    </VisualBrush>
</Window.Background>
<TabControl>
    <TabControl.Resources>
        <DataTemplate DataType="{x:Type model:Profile}">//this data template will be used by the TabControl
            <Grid>
                <TextBlock Text="{Binding Name}"/>
            </Grid>
        </DataTemplate>
    </TabControl.Resources>
    <TabControl.ItemsSource>
        <CompositeCollection>
            <TabItem Header="First Item"/>
            <TabItem Header="SecondItem"/>
            <CollectionContainer Collection="{Binding Source={StaticResource profiles}}"/>
        </CompositeCollection>
    </TabControl.ItemsSource>
</TabControl>