简体   繁体   English

添加到绑定的TabControl(mvvm)之后,获取新标签页的父级

[英]Getting parent of new tab after adding to bound TabControl (mvvm)

I'm adding a close button to my tabs using the following guide: 我使用以下指南在标签中添加了关闭按钮:

http://www.codeproject.com/Articles/84213/How-to-add-a-Close-button-to-a-WPF-TabItem http://www.codeproject.com/Articles/84213/How-to-add-a-Close-button-to-a-WPF-TabItem

This has become a problem because the event uses the 'parent' of the added tab to remove that tab from the tabcontrol. 这已成为一个问题,因为该事件使用添加的选项卡的“父级”从tab控件中删除该选项卡。 I'm binding the tab control using mvvm, so the parent property is apparently not being set and giving me a null reference exception for the parent when the event tries to remove from it. 我正在使用mvvm绑定选项卡控件,因此显然未设置parent属性,并且在事件尝试从中删除该属性时,为我提供了null引用异常。

Here's the binding so you get the idea: 这是绑定,因此您可以了解一下:

<TabControl Name="tabControl" Margin="0,22,0.2,-5.2" ItemsSource="{Binding Tabs}" Background="#FF4C76B2"/>

Heres where the tabs are being added. 在此处添加选项卡。

private void AddTab(object tabName)
{
    ClosableTab newTab = new ClosableTab();
    newTab.Title = "title?";
    //newTab.Header = tabName;
    TextBox test = new TextBox();

    test.Text = "CONTENT (" + tabName + ") GOES HERE";
    newTab.Content = test;

    Tabs.Add(newTab);
    OnPropertyChanged("Tabs");
}

Here is the event where the null reference is taking place: 这是发生空引用的事件:

void button_close_Click(object sender, RoutedEventArgs e)
{
    ((TabControl)this.Parent).Items.Remove(this);
}

As I see it there are two options: 在我看来,有两种选择:

  • try to find another way to remove the tab (without the parent property) 尝试找到另一种删除选项卡的方法(没有父属性)
  • try to find a way to somehow set the parent property (which cant be done directly, it throws a compiler error) 尝试找到一种以某种方式设置父属性的方法(无法直接完成,它会引发编译器错误)

That doesn't sound like MVVM to me. 在我看来,这听起来不像MVVM。 We work with data , not UI elements . 我们使用数据 ,而不是UI元素 We work with collections of classes that contain all of the properties required to fulfil some requirement and data bind those properties to the UI controls in DataTemplate s. 我们使用的类集合包含满足某些要求所需的所有属性,并且数据将这些属性绑定到DataTemplate的UI控件。 In this way, we add UI controls by adding data items into these collections and let the wonderful WPF templating system take care of the UI. 通过这种方式,我们通过将数据项添加到这些集合中来添加UI控件,并让出色的WPF模板系统处理UI。

For example, you have a TabControl that we want to add or remove TabItem s from... in a proper MVVM way. 例如,您有一个TabControl ,我们想要以适当的MVVM方式从...添加或删除TabItem First, we need a collection of items that can represent each TabItem : 首先,我们需要一个可以代表每个TabItem的项目集合:

public static DependencyProperty ItemsProperty = DependencyProperty.Register("Items", typeof(ObservableCollection<string>), typeof(TestView));

public ObservableCollection<string> Items
{
    get { return (ObservableCollection<string>)GetValue(ItemsProperty); }
    set { SetValue(ItemsProperty, value); }
}

I'm just using a DependencyProperty because I knocked this up in a UserControl and I'm just using a collection of string s for simplicity. 我只是使用DependencyProperty因为我在UserControl中将其UserControl并且为了简单起见,我仅使用string的集合。 You'll need to create a class that contains all of the data required for the whole TabItem content. 您需要创建一个包含整个TabItem内容所需的所有数据的类。 Next, let's see the TabControl : 接下来,让我们看看TabControl

<TabControl ItemsSource="{Binding Items}" ItemTemplate="{StaticResource ItemTemplate}" />

We data bind the collection to the TabControl.ItemsSource property and we set the TabControl.ItemTemplate to a Resource named ItemTemplate . 我们将数据绑定到TabControl.ItemsSource属性,并将TabControl.ItemTemplate设置为名为ItemTemplateResource Let's see that now: 让我们现在来看:

xmlns:System="clr-namespace:System;assembly=mscorlib"
...
<DataTemplate x:Key="ItemTemplate" DataType="{x:Type System:String}">
    <TabItem Header="{Binding}" />
</DataTemplate>

This DataTemplate defines what each item in our collection will look like. 这个DataTemplate定义了我们集合中每个项目的外观。 For simplicity's sake, our string s are just data bound to the TabItem.Header property. 为简单起见,我们的string s只是绑定到TabItem.Header属性的数据。 This means that for each item we add into the collection, we'll now get a new TabItem with its Header property set to the value of the string : 这意味着对于添加到集合中的每个项目,我们现在将获得一个新的TabItem ,其Header属性设置为string的值:

Items.Add("Tab 1");
Items.Add("Tab 2");
Items.Add("Tab 3");

Note that I included the System XML Namespace Prefix for completeness, but you won't need that because your DataType will be your own custom class. 请注意,为了完整起见,我包括了System XML命名空间前缀,但是您不需DataType ,因为您的DataType将是您自己的自定义类。 You'll need more DataTemplate s too. DataTemplate需要更多的DataTemplate For example, if your custom class had a Header property and a Content property, which was another custom class, let's say called Content , that contained all of the properties for the TabItem.Content property, you could do this: 例如,如果您的自定义类具有Header属性和Content属性(这是另一个自定义类),则称为Content ,其中包含TabItem.Content属性的TabItem.Content属性,则可以执行以下操作:

<DataTemplate x:Key="ItemTemplate" DataType="{x:Type YourPrefix:YourClass}">
    <TabItem Header="{Binding Header}" Content="{Binding Content}" />
</DataTemplate> 
<DataTemplate DataType="{x:Type YourPrefix:Content}">
    <YourPrefix:SomeUserControl DataContext="{Binding}" />
</DataTemplate>

So this would give you TabItem s with Header s set and Content that comes from SomeUserControl which you could design. 因此,这将为您提供带有Header集的TabItem和来自SomeUserControl Content可以设计)。 You don't need to use UserControl s, you could just add more UI controls to either DataTemplate . 您无需使用UserControl ,只需将更多UI控件添加到任一DataTemplate But you will need to add more controls somewhere... and more classes and properties, always remembering to correctly implement the essential INotifyPropertyChanged interface . 但是您将需要在某处添加更多控件...以及更多类和属性,始终记住正确实现基本的INotifyPropertyChanged接口

And finally, to answer your question in the proper MVVM way... to remove a TabItem , you simply remove the item that relates to that TabItem from the collection. 最后,以正确的MVVM方式回答您的问题...删除TabItem ,您只需从集合中删除与该TabItem相关的项目即可。 Simple... or it would have been if you really had been using MVVM like you claim. 很简单...或者如果您确实像您声称的那样一直在使用MVVM,那就应该如此。 It's really worth learning MVVM properly as you'll soon see the benefits. 正确学习MVVM确实值得,因为您很快就会看到好处。 I'll leave you to find your own tutorials as there are many to chose from. 我将让您找到自己的教程,因为有很多可供选择的教程。


UPDATE >>> 更新>>>

Your event handling is still not so MVVM... you don't need to pass a reference of any view model anywhere. 您的事件处理仍然不是MVVM ...您不需要在任何地方传递任何视图模型的引用。 The MVVM way is to use commands in the view model. MVVM方法是在视图模型中使用命令。 In particular, you should investigate the RelayCommand . 特别是,您应该调查RelayCommand I have my own version, but these commands enable us to perform actions from data bound Button s and other UI controls using methods or inline delegate s in the view model (where action and canExecute in this example are the CommandParameter values): 我有自己的版本,但是这些命令使我们能够使用视图模型中的方法或内联delegate从数据绑定的Button和其他UI控件执行操作(此示例中的actioncanExecuteCommandParameter值):

<Button Content="Close Tab" Command="{Binding CloseTabCommand}" 
    CommandParameter="{Binding}" />

...

public ICommand CloseTabCommand
{
    get { return new ActionCommand(action => Items.Remove(action), 
        canExecute => canExecute != null && Items.Contains(canExecute)); }
}

So whatever view model has your Tabs collection should have an AddTabCommand and a CloseTabCommand that add and remove items from the Tabs collection. 因此,无论您的Tabs集合具有AddTabCommand视图模型,都应具有AddTabCommandCloseTabCommand来添加和删除Tabs集合中的项目。 But just to be clear, for this to work properly, your ClosableTab class should be a data class and not a UI control class. 但需要明确的是,为了使其正常工作,您的ClosableTab类应该是数据类,而不是UI控件类。 Use a DataTemplate to specify it if it is a UI control. 如果它是一个UI控件,请使用DataTemplate进行指定。

You can find out about the RelayCommand from this article on MSDN. 您可以从MSDN上的本文中找到有关RelayCommand信息。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM