Skip to main content
Email Password   helpLost your password?

Introduction

This control was modeled from the collapsible splitter used in the Mozilla web browser, but with some added functionality. This control allows a linked form control to be dynamically expanded and collapsed by clicking on the splitter control button, and resized by dragging the splitter. Additionally, there is an option to expanded or contract the parent form so that the control to expand doesn't take any further form area.

How it works

The CollapsibleSplitter derives from the System.Windows.Forms.Splitter class, working with 2 overrides, mouse event handlers, and exposing 4 new properties. The code is relatively straight-forward, and is well commented. The only code that I will cover here is the control painting and interaction with the base splitter control.

In creating the control, I first started by creating a new derived class with Stephen Toub's DeriveClass utility. The next step was to draw the control surface, first by finding the clip rectangle for the base splitter and then defining a new rectangle located in the vertical center of the splitter for the collapser control.

protected override void OnPaint(PaintEventArgs e)
{
    // force the width to 8px so that everything

    // always draws correctly

    this.Width = 8;

    // create a Graphics object

    Graphics g = e.Graphics;

    // find the rectangle for the splitter and paint it

    Rectangle r = this.ClientRectangle; // fixed in version 1.1

    g.FillRectangle(new SolidBrush(this.BackColor), r);

    // create a new rectangle in the vertical center 

    // of the splitter for our collapse control button

    rr = new Rectangle(r.X, (int) r.Y + ((r.Height - 115)/2),
        8, 115);

    // draw the background color for our control image

    if(hot)
        g.FillRectangle(new SolidBrush(hotColor), 
            new Rectangle(rr.X + 1, rr.Y, 6, 115));
    else
        g.FillRectangle(new SolidBrush(this.BackColor), 
            new Rectangle(rr.X + 1, rr.Y, 6, 115));

    // draw the top & bottom lines for our control image

    g.DrawLine(new Pen(SystemColors.ControlDark, 1), 
        rr.X + 1, rr.Y, rr.X + rr.Width - 2, rr.Y);
    g.DrawLine(new Pen(SystemColors.ControlDark, 1), 
        rr.X + 1, rr.Y + rr.Height, rr.X + rr.Width - 2, 
        rr.Y + rr.Height);

    // draw the arrows for our control image

    // the ArrowPointArray is a point array that 

    // defines an arrow shaped polygon

    g.FillPolygon(new SolidBrush(SystemColors.ControlDarkDark), 
        ArrowPointArray(rr.X + 2, rr.Y + 3));

    g.FillPolygon(new SolidBrush(SystemColors.ControlDarkDark), 
        ArrowPointArray(rr.X + 2, rr.Y + rr.Height - 9));

    // draw the dots for our control image using a loop

    int x = rr.X + 3;
    int y = rr.Y + 14;

    // Visual Styles added in version 1.1

    switch(visualStyle)
    {
    case VisualStyles.Mozilla:
        for(int i=0; i < 30; i++)
        {
            // light dot

            g.DrawLine(
                new Pen(SystemColors.ControlLightLight), 
                x, y + (i*3), x+1, y + 1 + (i*3));

            // dark dot

            g.DrawLine(
                new Pen(SystemColors.ControlDarkDark), 
                x+1, y + 1 + (i*3), x+2, y + 2 + (i*3));

            // overdraw the background color as we actually 

            // drew 2px diagonal lines, not just dots..

            if(hot)
                g.DrawLine(new Pen(hotColor), 
                    x+2, y + 1 + (i*3), x+2, y + 2 + (i*3));
            else
                g.DrawLine(new Pen(this.BackColor), 
                    x+2, y + 1 + (i*3), x+2, y + 2 + (i*3));
        }
        break;

    case VisualStyles.DoubleDots:
        for(int i=0; i < 30; i++)
        {
            // light dot

            g.DrawRectangle(
                new Pen(SystemColors.ControlLightLight), 
                x, y + 1 + (i*3), 1, 1 );

            // dark dot

            g.DrawRectangle(
                new Pen(SystemColors.ControlDark), 
                x - 1, y +(i*3), 1, 1 );

            i++;

            // light dot

            g.DrawRectangle(
                new Pen(SystemColors.ControlLightLight), 
                x + 2, y + 1 + (i*3), 1, 1 );

            // dark dot

            g.DrawRectangle(
                new Pen(SystemColors.ControlDark), 
                x + 1, y  + (i*3), 1, 1 );
        }
        break;

    case VisualStyles.Win9x:
        g.DrawLine(new Pen(SystemColors.ControlLightLight), 
            x, y, x + 2, y);
        g.DrawLine(new Pen(SystemColors.ControlLightLight), 
            x, y, x,y + 90);
        g.DrawLine(new Pen(SystemColors.ControlDark), 
            x + 2, y, x + 2, y + 90);
        g.DrawLine(new Pen(SystemColors.ControlDark), 
            x, y + 90, x + 2, y + 90);
        break;

    case VisualStyles.XP:
        for(int i=0; i < 18; i++)
        {
            // light dot

            g.DrawRectangle(
                new Pen(SystemColors.ControlLight), 
                x, y + (i*5), 2, 2 );

            // light light dot

            g.DrawRectangle(
                new Pen(SystemColors.ControlLightLight), 
                x + 1, y + 1 + (i*5), 1, 1 );

            // dark dark dot

            g.DrawRectangle(
                new Pen(SystemColors.ControlDarkDark), 
                x, y +(i*5), 1, 1 );

            // dark fill

            g.DrawLine(
                new Pen(SystemColors.ControlDark), 
                x, y + (i*5), x, y + (i*5) + 1);

            g.DrawLine(
                new Pen(SystemColors.ControlDark), 
                x, y + (i*5), x + 1, y + (i*5));
        }
        break;

    case VisualStyles.Lines:
        for(int i=0; i < 44; i++)
        {
            g.DrawLine(new Pen(SystemColors.ControlDark), 
                x, y + (i*2), x + 2, y + (i*2));
        }

        break;
    }

    // dispose the Graphics object

    g.Dispose();
}

The control rectangle is used in the MouseMove event to determine if the cursor is within the control area (hot), or in the base controls splitter area.  Once we know whether the cursor is in the 'hot' area we can change various aspects of the control:  use a highlighted background color, ignore or pass on the MouseDown event, and also set the appropriate mouse cursor.

// this method was updated in version 1.11 to fix 

// a flickering problem discovered by John O'Byrne

private void OnMouseMove(object sender, MouseEventArgs e)
{
    // check to see if the mouse cursor position is within 

    // the bounds of our control

    if(e.X >= rr.X && e.X <= rr.X + rr.Width && 
        e.Y >= rr.Y && e.Y <= rr.Y + rr.Height)
    {
        if(!hot)
        {
            hot = true;
            this.Cursor = Cursors.Hand;
            this.Refresh();
        }
    }
    else
    {
        if(hot)
        {
            hot = false;
            this.Refresh();
        }

        if(!controlToHide.Visible)
            this.Cursor = Cursors.Default;
        else
            this.Cursor = Cursors.VSplit;
    }
}

protected override void OnMouseDown(MouseEventArgs e)
{
    // if the hider control isn't hot, 

    // let the base resize action occur

    if(!hot && !collapsed)
        base.OnMouseDown(e);
}

Using the control

The CollapsibleSplitter control can be added to the toolbox using the pre-compiled dll in the demo application, or simply copy the class into your project and manually reference it in your code.

To Add the Collapsible Splitter to your VS.Net toolbox, follow these steps:

Once you have it on a form, set the Dock property, and then set the ControlToHideproperty so that the splitter knows which form control to interact with. All properties specific to the collapsing behaviour can be found under the Collapsing Options group in the properties window. Once created on your form, the view state can be programmatically toggled by calling the ToggleState method, and the current state can be retrieved from the IsCollapsed property.

I hope you find this control useful, and if you improve this control, please email me the updated source. If you have any comments or suggestions, please post your thoughts in the feedback section below.

Updates

Version 1.1 Changes:

Version 1.11 Changes:

Version 1.2 Changes:

Version 1.3 Changes: (24 Aug 2003)

You must Sign In to use this message board.
 
 
Per page   
 FirstPrevNext
GeneralLicense Pin
aldo hexosa
17:08 6 Jul '09  
Generalupdate to new version please Pin
cvckillern
3:27 8 May '09  
Questionmemory management bug Pin
zxcv0938512485
10:57 22 Sep '07  
AnswerRe: memory management bug Pin
Member 3427403
13:02 26 Jan '09  
GeneralNew implementation for SplitterContainter Pin
marioz8
9:05 14 Sep '07  
GeneralRe: New implementation for SplitterContainter Pin
Furty
14:10 14 Sep '07  
GeneralAnimationDelay does nothing Pin
jbshaler
5:36 11 Jul '07  
GeneralNEWBE Pin
Lonnie1
10:04 17 Nov '06  
GeneralAccess Violation Exception Pin
Louis-Philippe Carignan
6:01 2 Nov '06  
GeneralNice one dude !! Pin
scanner777
22:48 16 Sep '06  
GeneralA problem used in MDI application Pin
grrrrr
14:32 14 Dec '05  
GeneralI added an event "StateChanged" into the control Pin
Wayne Wood
5:28 13 Sep '05  
GeneralMemory problem Pin
kanaan81
5:09 18 Jul '05  
GeneralTabPages Pin
Jon Lea
12:21 17 Mar '05  
GeneralRe: TabPages Pin
Furty
17:13 17 Mar '05  
GeneralWorks fine on Form, buggy on Control Pin
Lee Grissom
9:08 3 Mar '05  
GeneralRe: Works fine on Form, buggy on Control Pin
PunCha
23:52 13 Feb '07  
GeneralRe: Works fine on Form, buggy on Control Pin
aldo hexosa
17:26 15 Feb '09  
Generalcollapsible splitter button - able to drag? Pin
Johnny O
12:04 23 Feb '05  
GeneralRe: collapsible splitter button - able to drag? Pin
PunCha
23:40 13 Feb '07  
GeneralOffscreen Drawing... Pin
TJoe
1:56 23 Dec '04  
Generalhow to work with it Pin
Anonymous
15:06 16 Dec '04  
GeneralRe: how to work with it Pin
Furty
15:15 16 Dec '04  
GeneralRe: how to work with it Pin
Anonymous
7:03 17 Dec '04  
GeneralCollapsible Splitter in Combination with DockPanelSuite Pin
rascreek
4:25 25 Oct '04  


Last Updated 26 Aug 2003 | Advertise | Privacy | Terms of Use | Copyright © CodeProject, 1999-2009