简体   繁体   中英

WPF: Which solution? TabControl with close button and new tab button

I'm trying to find the best solution for a TabControl that both support a close button on each TabItem, and always show a "new tab button" as the last tab.

I've found some half working solutions, but i think that was for MVVM, that I'm not using. Enough to try to understand WPF =)

This is the best solution I've found so far: http://www.codeproject.com/Articles/493538/Add-Remove-Tabs-Dynamically-in-WPF

A solution that i actually understand. But the problem is that it is using the ItemsSource, and i don't want that. I want to bind the ItemsSource to my own collection without having to have special things in that collection to handle the new tab button.

I've been search for days now but cant find a good solution. And I'm really new to WPF, otherwise i could probably have adapted the half done solutions I've found, or make them complete. But unfortunately that is way out of my league for now.

Any help appreciated.

I have an open source library which supports MVVM and allows extra content, such as a button to be added into the tab strip. It is sports Chrome style tabs which can tear off.

http://dragablz.net

You could make a converter which appends the Add tab. This way the collection of tabs in you viewmodel will only contain the real tabs.

The problem is then how to know when the Add tab is selected. You could make a TabItem behavior which executes a command when the tab is selected. Incidentally I recommended this for another question just recently, so you can take the code from there: TabItem selected behavior

While I don't actually have the coded solution, I can give some insight on what is most likely the appropriate way to handle this in a WPF/MVVM pattern.

Firstly, if we break down the request it is as follows:

  1. You have a sequence of elements that you want to display.
  2. You want the user to be able to remove an individual element from the sequence.
  3. You want the user to be able to add a new element to the sequence.

Additionally, since you are attempting to use a TabControl , you are also looking to get the behavior that a Selector control provides (element selection), as well as an area to display the element (content) which is selected.

So, if we stick to these behaviors you'll be fine, since the user interface controls can be customized in terms of look and feel.


Of course, the best control for this is the TabControl , which are you already trying to use. If we use this control, it satisfies the first item.

<TabControl ItemsSource="{Binding Path=Customers}" />

Afterwards, you can customize each element, in your case you want to add a Button to each element which will execute a command to remove that element from the sequence. This will satisfy the second item.

<TabControl ...>
  <TabControl.ItemTemplate>
    <DataTemplate>
      <StackPanel Orientation="Horizontal">
        <TextBlock Text="{Binding Path=CustomerId}" />
        <Button Command="{Binding Path=RemoveItemCommand, Mode=OneTime,
                          RelativeSource={RelativeSource FindAncestor,
                                          AncestorType={x:Type TabControl}}"
                CommandParameter="{Binding}" />
      </StackPanel>
    </DataTemplate>
  <TabControl.ItemTemplate>
</TabControl>

The last part is a bit more difficult, and will require you to actually have to create a custom control that inherits from the TabControl class, add an ICommand DependencyProperty , and customize the control template so that it not only displays the TabPanel , but right next to it also displays a Button which handles the DependencyProperty you just created (the look and feel of the button will have to be customized as well). Doing all of this will allow you to display your own version of a TabControl which has a faux TabItem , which of course is your "Add" button. This is far far far easier said than done, and I wish you luck. Just remember that the TabPanel wraps onto multiple rows and can go both horizontally or vertically. Basically, this last part is not easy at all.

This is bit of a dirty way to achieve the Add (+) button placed next to the last TabItem without much work. You already know how to place a Delete button next to the TabItem caption so I've not included that logic here.

Basically the logic in this solution is

  1. To bind ItemsSource property to your own collection as well as the Add TabItem using a CompositeCollection.
  2. Disable selection of the Add(+) TabItem and instead perform an action to load a new tab when it is clicked/selected.

XAML bit

    <TextBlock x:Name="HiddenItemWithDataContext" Visibility="Collapsed" />
    <TabControl x:Name="Tab1" SelectionChanged="Tab1_SelectionChanged" >
        <TabControl.ItemsSource>
            <CompositeCollection>
                <CollectionContainer Collection="{Binding DataContext.MyList, Source={x:Reference HiddenItemWithDataContext}}" />
                <TabItem Height="0" Width="0"  />
                <TabItem  Header="+" x:Name="AddTabButton"/>
            </CompositeCollection>
        </TabControl.ItemsSource>
    </TabControl>

The code behind

    private void Tab1_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        if (e.AddedItems.Contains(AddTabButton))
        {
            //Logic for adding a new item to the bound collection goes here.
            string newItem = "Item " + (MyList.Count + 1);
            MyList.Add(newItem);
            e.Handled = true;
            Dispatcher.BeginInvoke(new Action(() => Tab1.SelectedItem = newItem));

        }
    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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