【问题标题】:Add TabItems to an existing TabControl WPF/MVVM将 TabItems 添加到现有 TabControl WPF/MVVM
【发布时间】:2018-02-07 09:48:09
【问题描述】:

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

如果我使用ItemSource,我会得到一个异常Items collection must be empty before using ItemsSource.

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

其他解决方案是将TabControl 添加为属性并使用代码隐藏将其绑定到 ViewModel,我想避免这种情况。

所以,我只是想知道是否有办法只使用 XAML 和 MVVM 来做到这一点。

编辑:

ItemSource 尝试,这是有效的。

XAML:

<TabControl Grid.Row="1"
        Grid.Column="0"
        VerticalAlignment="Stretch"
        BorderThickness="0.5"
        BorderBrush="Black"
        ItemsSource="{Binding Model.TabItems, Mode=TwoWay}">
    <!--<TabControl.Items>
    </TabControl.Items>-->
</TabControl>

型号

public ObservableCollection&lt;TabItem&gt; TabItems {get;set;}

虚拟机

TabItem tabItem = new TabItem { Content = new DetailedViewModel((MyObject)inCommandParameter) };
Model.TabItems.Add(tabItem);

【问题讨论】:

  • 我认为使用XAML 的唯一方法是设置所有TabItems,但将其可见性设置为false,然后在需要时显示它们。如果你使用ItemSource,那么我建议你检查this.
  • 是的,ItemSource 可以正常工作,但我有一些修复 TabItem 和其他我必须动态创建的,对我来说在 XAML 固定的上创建是有意义的,以及 ViewModel 中的其他人,但似乎我将不得不做“一切或无”的风格。感谢您的回答!
  • 你能告诉我们ItemsSource尝试的XAML吗?
  • 当然@XAMlMAX,但就像我说的那样,它工作正常。问题是当我在XAML 和 VM 中混合使用 TabItems 的定义时
  • 你听说过CompositeCollectionCollectionViewSource吗?

标签: c# wpf xaml mvvm


【解决方案1】:

你在这里做的是不是 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(); }
        }
    }
}  

现在我们有了可以使用的基础。之后,我假设您知道如何在您的 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 中实现的命令,然后将profile 添加到其中即可享受表演。

【讨论】:

  • 我会试一试,但是,根据你的第一句话,你在这里做的不是 MvvM。其背后的想法是保持应用程序的各个部分分开,即模型不应该返回任何 UI 元素。 我已经读过这是具有 UI 属性的模型和管理这些属性的 ViewModel,其他方式,如果我理解你,如果 UI 的属性出现在 ViewModel 上,那么 Model 中会出现什么?
  • 模型是您的数据库表示。所以无论你用什么来存储数据。就我而言,它通常是 MS SQL。我会将实体框架类归类为模型。现在这些类具有 UI 使用的属性,即&lt;TextBlock Text="{Binding Name}"/&gt;
  • 您的答案按预期工作。关于您的最后一条评论,您会将我现在在模型中的ObservableCollection&lt;TabItem&gt; 移动到 ViewModel 中,对吗?如果我需要模型中的项目,请使用绑定 SelectedItem 或类似的东西。
  • 你应该有ObservableCollection,但NOTTabItem。将TabItem 替换为您的模型类(看看我的答案),这就是它的工作方式。 DataTemplate 将用于在 UI 中呈现内容,您只在 Model 和 View Model 中提供数据,NEVER UI ELEMENTS
  • 明白了,唯一的问题是TabItemContentUserControl ViewModel,我想我必须创建一个类,我将VM作为属性并绑定它就像在您的示例中一样,但 TextBlock.Text 使用 ContentPresenter.Content
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-11-23
  • 2011-02-09
  • 1970-01-01
  • 2012-01-29
  • 1970-01-01
  • 2011-04-29
相关资源
最近更新 更多