Query about where to start with....

54 views
Skip to first unread message

Sacha Barber

unread,
Apr 22, 2009, 3:48:50 PM4/22/09
to wpf-di...@googlegroups.com
Hello gents,

At work we are using the standard WPF TabControl, and are far from happy with it, we hate the way the VisualTree ONLY contains the selected Tab. This is not cool at all, as we have some pretty heavy views, that are expensive to recreate and making the app a bit sticky.

So I would like to try and craft a new TabControl, I understand how to do this apart from one part.

Let me explain what we would be doing for a given TabItem (we will not be using TabItems either) we may have a button and a Grid (the content).


So we may have something like



<DockPanel LastChildFill="True">

<!-- Headers For Tabs-->
<StackPanel Orientation="Horizontal" DockPanel.Dock="Top">

   <!-- Shows tabContent1 by making it Visible -->
  <Button Content="Tab1">

   <!-- Shows tabContent2 by making it Visible -->
  <Button Content="Tab2">

</StackPanel>

<!-- Actual tab contents that are hidden/collapsed based on which button is clicked-->
<Grid>
  <ContentControl Margin="0" x:Name="tabContent1" Visibility="Visible">
  <ContentControl Margin="0" x:Name="tabContent2" Visibility="Collapsed">
</Grid>


</DockPanel>


I have tried this in a proof of concept, actually writing the XAML pretty much as above, and it rips, very fast, much better then that standard WPF tab (Shame on Microsoft).

Problem is I do not know how to accept some sort of item, that will represent a Button and some content for a given tab. I can of course, just have a Grid exposed as the Content property, which is where new Tab content can be added, and then for each tab (child within the Grid Content property) I could (in code) create a new button and add it and make sure it is wired up to control the visibility of the associated child within the Grid Content property.

Do you get me, is this clear? Does it sound ok? Is there a better way?


Obviously writing a new TabControl is no small undertaking so I will only be catering for Tabs at the Top or Bottom not left or right, too different a layout for me to do in the time I have allocated for the task.

Any ideas would be greatfully recieved.

Thanks folks







" Upgrade to Internet Explorer 8 Optimised for MSN. " Download Now

Jeremiah Morrill

unread,
Apr 22, 2009, 3:55:05 PM4/22/09
to wpf-di...@googlegroups.com
Maybe you want to check out the tab control in the Silverlight toolkit? It may not be big daddy WPF, but it may be similar enough to get a good grasp of all you'll need.

-Jer

Mark Smith

unread,
Apr 22, 2009, 3:56:10 PM4/22/09
to wpf-di...@googlegroups.com
Hey Sacha,

Have you considered just disconnecting the visuals from the tab when it is de-selected?  I.e. keep your own hard-referenced to the child content created and when the tab is activated or deactivated, connect and disconnect the content to the tabitem?  I've not tried it, but it shouldn't be hard to do and it would allow you to use the existing tab control while keeping visuals alive..

mark

Sacha Barber

unread,
Apr 22, 2009, 3:59:51 PM4/22/09
to wpf-di...@googlegroups.com
Mark

Ah ok, you know I had not considered that, Ill try that for sure.

Jer

Thanks for your advice, ill try what Mark says 1st and then yours





From: mark....@gmail.com
To: wpf-di...@googlegroups.com
Subject: [WPF Disciples] Re: Query about where to start with....
Date: Wed, 22 Apr 2009 14:56:10 -0500

Windows Live Messenger just got better. Find out more!

Sacha Barber

unread,
Apr 22, 2009, 4:24:56 PM4/22/09
to wpf-di...@googlegroups.com
Actually Mark, I think this may work out, ill try tomorrow and let you know

Thanks


        IList items;
        object selectedItem = null;
        List<Object> deselectedItems = new List<Object>();

        void tab_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
           
            if (selectedItem == this.tab.SelectedItem)
                return;

            if (e.RemovedItems.Count > 0)
            {
                if (!deselectedItems.Contains(e.RemovedItems[0]))
                    deselectedItems.Add(e.RemovedItems[0]);
            }

            if (e.AddedItems.Count > 0)
            {
                if(deselectedItems.Contains(e.AddedItems[0]))
                {
                selectedItem = e.AddedItems[0];
                this.tab.SelectedItem = selectedItem;

                }
            }
        }





From: mark....@gmail.com
To: wpf-di...@googlegroups.com
Subject: [WPF Disciples] Re: Query about where to start with....
Date: Wed, 22 Apr 2009 14:56:10 -0500


Share your photos with Windows Live Photos – Free. Try it Now!

Eric Burke

unread,
Apr 22, 2009, 4:27:22 PM4/22/09
to wpf-di...@googlegroups.com
Try using a Grid as your ItemsPanel and set Panel.ZIndex = 1 when the item is selected.  The normal Template for TabControl uses a ContentPresenter, so it will replace the VisualTree.  Using a Grid should preserve it.

e.g.,

    <Style x:Key="ics">
        <Style.Triggers>
            <Trigger Property="IsSelected" Value="True">
                <Setter Property="Panel.ZIndex" Value="1" />
            </Trigger>
        </Style.Triggers>
    </Style>

    <TabControl ItemContainerStyle="{StaticResource ics}"
        ...>
        <TabControl.ItemsPanel>
            <ItemsPanelTemplate>
                <Grid IsItemsHost="True" />
            </ItemsPanelTemplate>
        </TabControl.ItemsPanel>
    </TabControl>


From: Sacha Barber <sacha...@hotmail.com>
To: "wpf-di...@googlegroups.com" <wpf-di...@googlegroups.com>
Sent: Wednesday, April 22, 2009 3:59:51 PM

Mark Smith

unread,
Apr 22, 2009, 4:49:21 PM4/22/09
to wpf-di...@googlegroups.com
Will that work?  I seem to recall that TabControl pretty much blows off the ItemsPanel and does it's own layout..  it would be fantastically cool if it did this!  Now I'm intrigued to try it!

mark

Eric Burke

unread,
Apr 22, 2009, 4:54:31 PM4/22/09
to wpf-di...@googlegroups.com
We were able to do it on Messenger.  Looking back at old source now...

 

From: Mark Smith <mark....@gmail.com>
To: wpf-di...@googlegroups.com
Sent: Wednesday, April 22, 2009 4:49:21 PM

Eric Burke

unread,
Apr 22, 2009, 5:05:32 PM4/22/09
to wpf-di...@googlegroups.com
Oh, now I remember what we did.  Rather than use a TabControl we did this:
 
+ data source was a CollectionViewSource (though I don't think that's a requirement)
+ ListBox bound to the data source (IsSynchronizedWithCurrentItem = true) using Grid as ItemsPanel; set Panel.ZIndex = 1 in the ListBoxItem style when IsSelected = true
+ TabPanel bound to the data source.  As the user selects new items, the ListBoxItems will change as well because they are bound to the same thing that manages the "CurrentItem"
 
So basically glomming together 2 parts to make a TabControl.  It's not as flexible as the TabControl but it works pretty well.
 
There was a bug in .NET 3.0 where sometimes the content of the ListBox would disappear when using this technique but I believe that's been fixed now in 3.5.
 
 
However, my original trick may still work if you simply replace the ContentPresenter in the TabControl Template with an ItemsPresenter.  I"ll play with it.


From: Mark Smith <mark....@gmail.com>
To: wpf-di...@googlegroups.com
Sent: Wednesday, April 22, 2009 4:49:21 PM

Eric Burke

unread,
Apr 22, 2009, 6:51:26 PM4/22/09
to wpf-di...@googlegroups.com
Attached is a working example.  Also attached is a screenshot showing snoop and all the "TabItems" (which are really ListBoxItems).

Basically I did this:

DockPanel
    TabControl templated to omit the ContentPresenter; bound to a source with IsSynchronizedToCurrentItem=true
    ListBox bound to same source with IsSynchronizedToCurrentItem=true

Pretty simple but beware the performance if you have a lot of tabs and/or they are very complex.


From: Mark Smith <mark....@gmail.com>
To: wpf-di...@googlegroups.com
Sent: Wednesday, April 22, 2009 4:49:21 PM
tabcontrol.zi_
tabcontrol.png

Sacha Barber

unread,
Apr 23, 2009, 4:53:37 AM4/23/09
to wpf-di...@googlegroups.com
Eric

Thanks for this code, although it doesnt suit our need exactly, it has given me a good idea or 2, so thanks.

Ill keep you posted.




Date: Wed, 22 Apr 2009 15:51:26 -0700
From: ebu...@yahoo.com

Subject: [WPF Disciples] Re: Query about where to start with....

Eric Burke

unread,
Apr 23, 2009, 6:46:39 AM4/23/09
to wpf-di...@googlegroups.com
No worries.  Did you ever figure out your issues with the ICollectionView.MoveCurrentToPosition()?

Sent: Thursday, April 23, 2009 4:53:37 AM

Sacha Barber

unread,
Apr 23, 2009, 7:11:47 AM4/23/09
to wpf-di...@googlegroups.com
Oh man, that is a nightmare, I checked everything you said, and still no bannana. We found a work around but I am at a real loss to explain what was up with it.

I think Microsoft also need to make the ICollectionView act more like a Selector, and allow -1 to be a valid index. This is something we do need actually.

I also think that there needs to be a ICollectionView which supports multiple selection (for say ListView selection). I know you can do this by having a IsSelected property on the bound ViewModel objects, but this forced a very fine grained ViewModel approach, a bit to fine grained for most.

On the tab front, I have a rudimentary tab control working, which is attached. It uses your ideas, but we needed to be able to mark up what items we wanted as Tabs in XAML.

It is super fast though.

Its work in progress though.

I still need to allow type of TabItem (closeableTabItem (as special one we wrote) or normal TabItem), and also the header text to be set using attached properties on the added item.

Basically I create the tabitems based on the children being added.

Thanks for all your help

Rename attached to .zip.

What do you think????


Date: Thu, 23 Apr 2009 03:46:39 -0700

Get the New Internet Explore 8 Optimised for MSN. Download Now
TabControlTest.zip.doc

Peter O'Hanlon

unread,
Apr 23, 2009, 7:40:56 AM4/23/09
to wpf-di...@googlegroups.com
I think we'll soon have another Code Project article ;->
--
Peter O'Hanlon
Reply all
Reply to author
Forward
0 new messages