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

浪尽此生 提交于 2019-12-30 10:34:17

问题


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

This has become a problem because the event uses the 'parent' of the added tab to remove that tab from the tabcontrol. 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.

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)

回答1:


That doesn't sound like MVVM to me. We work with data, not UI elements. 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 DataTemplates. 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.

For example, you have a TabControl that we want to add or remove TabItems from... in a proper MVVM way. First, we need a collection of items that can represent each 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 strings for simplicity. You'll need to create a class that contains all of the data required for the whole TabItem content. Next, let's see the 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. 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. For simplicity's sake, our strings are just data bound to the TabItem.Header property. 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:

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. You'll need more DataTemplates too. 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:

<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 TabItems with Headers set and Content that comes from SomeUserControl which you could design. You don't need to use UserControls, you could just add more UI controls to either DataTemplate. But you will need to add more controls somewhere... and more classes and properties, always remembering to correctly implement the essential INotifyPropertyChanged interface.

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. Simple... or it would have been if you really had been using MVVM like you claim. It's really worth learning MVVM properly as you'll soon see the benefits. 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. The MVVM way is to use commands in the view model. In particular, you should investigate the RelayCommand. I have my own version, but these commands enable us to perform actions from data bound Buttons and other UI controls using methods or inline delegates in the view model (where action and canExecute in this example are the CommandParameter values):

<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. But just to be clear, for this to work properly, your ClosableTab class should be a data class and not a UI control class. Use a DataTemplate to specify it if it is a UI control.

You can find out about the RelayCommand from this article on MSDN.



来源:https://stackoverflow.com/questions/21006682/getting-parent-of-new-tab-after-adding-to-bound-tabcontrol-mvvm

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!