Click here to Skip to main content
Click here to Skip to main content

PullApartTabPage: Docking and Undocking Your TabPages

, 31 Aug 2006 CPOL
Rate this:
Please Sign up or sign in to vote.
A TabPage you can dock and undock

Introduction

Have you ever made a nice stable WinForm application that had several tabs? Did it contain a nice clear distinction of workflow or just had a real nice component separation? Then did you find out some time later (weeks or months) that some of your users want to see some of the data side by side and that they have been flipping back and forth furiously just to see the information? If this describes a situation that you find yourself in and you don't want to go through all the effort of an MDI interface, then the PullApartTabPage might be just right.

A Look at the Code

There are really 2 main parts of the code we need to look at: an artifact of using VS 2005 designer and the dock/undock.

When you use the VS 2005 designer, it creates a lot of your code on your behalf. This is usually a convenient thing, however, this can have unintended side affects. The location of our dock/undock button is one of these side affects. Ideally we want to put the button in the upper right hand corner, so that it looks like the minimize / maximize / close buttons on Windows. Since we are adding the button in our constructor, System.Windows.Forms.TabPage is not a System.Windows.Forms.UserControl and doesn't have a designer of its own, we would like to do this:

public PullApartTabPage()
{
    ...
    // make the button we use to tell the app to dock/undock
    btnchangemode = new Button();
    btnchangemode.Image = Resources.arrow;
    btnchangemode.Size = btnchangemode.Image.Size;            
    btnchangemode.Click += new EventHandler(btnchangemode_Click);
    btnchangemode.Anchor = AnchorStyles.Top | AnchorStyles.Right;
    // note this
    btnchangemode.Location = new System.Drawing.Point(Width - 
                                 btnchangemode.Width, 0);           
    Controls.Add(btnchangemode);
    mode = TabMode.Dock;            
}

However, when VS2005 constructs our object, it calls the constructor first, then sets the size to whatever the designer tells it to. The problem is that the btnchangemode.Anchor doesn't kick in till after this, so the button appears kind of in the middle of the top of the System.Windows.Forms.TabPage. This is clearly undesirable. To fix this, we change the constructor a little and add an event.

public PullApartTabPage()
{
    ...

    // make the button we use to tell the app to dock/undock
    btnchangemode = new Button();
    btnchangemode.Image = Resources.arrow;
    btnchangemode.Size = btnchangemode.Image.Size;            
    btnchangemode.Click += new EventHandler(btnchangemode_Click);
    btnchangemode.Anchor = AnchorStyles.Top | AnchorStyles.Right;
    // note no btnchangemode.Location
    Controls.Add(btnchangemode);
    mode = TabMode.Dock;
    // note this
    this.SizeChanged += new EventHandler(PullApartTabPage_SizeChanged);
}
private void PullApartTabPage_SizeChanged(object sender, EventArgs e)
{
    if (btnchangemode.Parent == this)
        btnchangemode.Location = new System.Drawing.Point(Width - 
                                     btnchangemode.Width, 0);
}

What we are doing is telling the button to move to the edge of the System.Windows.Forms.TabPage if the tabpage's size is changed. Remember that if we are undocked, then the button's parent will be this.myform, not this, and this.myform could have a very different size.

Now looking at the property PullApartTabPage.TabMode.
First off, it has the [System.ComponentModel.Browsable(false)] attribute. This is another designer issue. If you allow yourself to set the TabMode = TabMode.Float in the designer at design time, you have an issue. The problem is that all of a sudden, you loose your PullApartTabPage and then can't set any of its controls or set the PullApartTabPage back to dock. Basically it's here to prevent me from shooting myself in the foot.

public TabMode TabMode
{
    ...
    set
    {
        ... 
        mode = value;
        if (value == TabMode.Float)
        {
            myform.Size = this.Size;
            myform.Text = Text; 
            mytabparent = (TabControl)Parent;
            mytabparent.TabPages.Remove(this); 
            ... 
            btnchangemode.Location = 
               new System.Drawing.Point(myform.Width - 
                   btnchangemode.Width - 8, 0);
            myform.Show();
            myform.Focus();
        }
        else if (value == TabMode.Dock)
        {
            if (mytabparent != null)
            {
                mytabparent.TabPages.Add(this);
                myform.Size = this.Size;
            }
            myform.Hide(); 
            ... 
            btnchangemode.Location = 
               new System.Drawing.Point(this.Width - btnchangemode.Width, 0);
        }
        else
            throw new ApplicationException("unknown TabMode enum");
    }
}

We have four things to look at here:

1st

mytabparent = (TabControl)Parent;
mytabparent.TabPages.Remove(this);

Technically, you could move the tabpage from one System.Windows.Forms.TabControl to another while the application is running. What this dose is it allows you to put the PullApartTabDemo.PullApartTabPage back on the System.Windows.Forms.TabControl that it was last on.

2nd

if (mytabparent != null)
{
mytabparent.TabPages.Add(this);
...
}

Yes, it is possible to not have a parent. This comes from how the VS2005 designer works. It creates the System.Windows.Forms.TabPage, then assigns the tab to a System.Windows.Forms.TabControl. Depending on what order VS2005 decides, tabpage.TabMode first or tabcontrol.tabpages.add(tabpage)first, you could be without a parent for a little while.

3rd

myform.Size = this.Size;

In order to avoid a lot of control location / anchoring issues, we resize myform to the size of the tabpage, then move the controls back and forth. This really helps when you decide that one of your tabs is more important and needs to be bigger. I encounter this a lot when one tabpage has a System.Windows.Forms.DataGridView on it.

4th

btnchangemode.Location = new System.Drawing.Point
	(myform.Width - btnchangemode.Width - 8, 0);

vs

btnchangemode.Location = new System.Drawing.Point(this.Width - btnchangemode.Width, 0);

As it turns out, you don't always have all the space a control advertises to use. On the System.Windows.Forms.Form there is a little beveled edge. If you leave both as:

btnchangemode.Location = new System.Drawing.Point(this.Width - btnchangemode.Width, 0); 

the button will appear off the form just a little. Not really a huge deal, but it looks poor. You just need to know to make the adjustment.

Setup and Suggested Use

Integration of the PullApartTabDemo.PullApartTabPage is very easy. Simply make your WinForm application as you normally would. When it is completed, go into your <form>.Designer.cs file (VS2005) and find where you construct your tabpage. It is probably in your private void InitializeComponent() function. Just replace

this.tabPage1 = new System.Windows.Forms.TabPage();

with

this.tabPage1 = new PullApartTabDemo.PullApartTabPage();

It's that easy. Now a couple things of note. After a couple of recompiles, VS2005 will add the following line for you without your consent.

this.tabPage1.TabMode = PullApartTabDemo.TabMode.Dock; 

This comes from the fact that vs2005 does a little object creation under the covers and likes to get properties and set them for you. This is not necessarily what you want, but it shouldn't hurt anything. I just wanted to bring it to your attention. Also, I find that when I use this control, I like to make all the pages but one PullApartTabDemo.PullApartTabPages. This way it doesn't look silly when you have a System.Windows.Forms.TabControl with nothing on it. It is just personal preference.

Demo Application

Docked Screenshot

Undocked Screenshot

The demo application is nothing fancy. The controls don't even do anything. No, I did not remake the Outlook functionality. It is just to show that you can put controls on the pullaparttabpage and then put it back together. A word of note, though. When you re-dock the page, it will add itself to the end of the tab control. This could be solved by keeping a unique order id and sorting the tab pages on dock. I, however, have never found this necessary as the pages that are undocked are usually left there for most of the application lifetime.

Conclusion

There are several issues with designing a PullApartTabDemo.PullApartTabPage most having to deal with size and the VS2005 designer, but all in all it's worth it to just change 1 line and have the ability to pull apart your tabpages from your tabcontrols.

References

History

  • 31st August, 2006: Initial post

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Mark Newman #2
Web Developer
United States United States
No Biography provided

Comments and Discussions

 
Questionprogram in vb.net PinmemberMember 1104044628-Nov-14 13:22 
QuestionCan you remove form without re-instantiating it? Pinmembertvproducer327-Apr-07 11:35 
GeneralKeeping the tab order Pinmemberdvazriel27-Feb-07 14:22 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web03 | 2.8.141220.1 | Last Updated 31 Aug 2006
Article Copyright 2006 by Mark Newman #2
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid