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>