Click here to Skip to main content
Click here to Skip to main content
Go to top

Collapsible Splitter control in C#

, 26 Aug 2003
Rate this:
Please Sign up or sign in to vote.
A Mozilla-style collapsing splitter control in C#

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:

  • Right-click on the VS.Net Toolbox and select "Customize Toolbox..."
  • Select the ".Net Framework Components" tab, and click the "Browse.." button
  • Browse to and open the "CollapsibleSplitter.dll" file from the demo application archive
  • Click OK to add Collapsible Splitter control to your Toolbox.

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:

  • OnPaint is now overridden instead of being a handled event, and the entire splitter is now painted rather than just the collapser control
  • The splitter rectangle is now correctly defined
  • The Collapsed property was renamed to IsCollapsed, and the code changed so that no value needs to be set
  • New visual styles added: Win9x, XP, DoubleDots and Lines

Version 1.11 Changes:

  • The OnMouseMove event handler was updated to address a flickering issue discovered by John O'Byrne

Version 1.2 Changes:

  • Added support for Horizontal Splitters

Version 1.3 Changes: (24 Aug 2003)

  • Added an optional 3D border
  • General code and comment cleaning
  • Flagged assembly with the CLSCompliant attribute
  • Added a simple designer class to filter unwanted properties
  • Added support for inclusion as a VS.Net ToolBox control
  • Added a ToolBox bitmap
  • Removed extraneous overrides
  • Added summaries
  • Removed the ParentFolder from public properties - this is now set automatically in the OnHandleCreated event
  • Added expand/collapse animation code

License

This article, along with any associated source code and files, is licensed under The Microsoft Public License (Ms-PL)

Share

About the Author

Furty
Web Developer
Thailand Thailand
Furty will code for food.

Comments and Discussions

 
GeneralMy vote of 5 PinmemberYanZhiwei27-Aug-13 21:08 
QuestionNice, but how to use? Pinmemberb845985@rmqkr.net8-Jan-13 3:08 
GeneralMy vote of 5 Pinmembermanoj kumar choubey3-Jul-12 23:46 
QuestionDo not dispose e.Graphics in Paint event PinmemberMember 143012617-Aug-11 7:09 
GeneralLicense Pinmemberaldo hexosa6-Jul-09 16:08 
Generalupdate to new version please Pinmembercvckillern8-May-09 2:27 
Questionmemory management bug Pinmemberzxcv093851248522-Sep-07 9:57 
AnswerRe: memory management bug PinmemberMember 342740326-Jan-09 12:02 
AnswerRe: memory management bug Pinmembertr02178-Feb-11 17:58 
GeneralRe: memory management bug PinmemberMember 143012617-Aug-11 7:12 
GeneralNew implementation for SplitterContainter Pinmembermarioz814-Sep-07 8:05 
GeneralRe: New implementation for SplitterContainter PinmemberFurty14-Sep-07 13:10 
GeneralAnimationDelay does nothing Pinmemberjbshaler11-Jul-07 4:36 
GeneralRe: AnimationDelay does nothing PinmemberorientalCSharp20-Jun-10 21:49 
GeneralNEWBE PinmemberLonnie117-Nov-06 9:04 
GeneralAccess Violation Exception PinmemberLouis-Philippe Carignan2-Nov-06 5:01 
GeneralNice one dude !! Pinmemberscanner77716-Sep-06 21:48 
GeneralA problem used in MDI application Pinmembergrrrrr14-Dec-05 13:32 
GeneralI added an event &quot;StateChanged&quot; into the control PinmemberWayne Wood13-Sep-05 4:28 
GeneralMemory problem Pinmemberkanaan8118-Jul-05 4:09 
GeneralTabPages PinmemberJon Lea17-Mar-05 11:21 
GeneralRe: TabPages PinmemberFurty17-Mar-05 16:13 
GeneralWorks fine on Form, buggy on Control PinsussLee Grissom3-Mar-05 8:08 
GeneralRe: Works fine on Form, buggy on Control PinmemberPunCha13-Feb-07 22:52 
GeneralRe: Works fine on Form, buggy on Control Pinmemberaldo hexosa15-Feb-09 16:26 
Questioncollapsible splitter button - able to drag? PinmemberJohnny O23-Feb-05 11:04 
AnswerRe: collapsible splitter button - able to drag? PinmemberPunCha13-Feb-07 22:40 
GeneralOffscreen Drawing... PinmemberTJoe23-Dec-04 0:56 
Questionhow to work with it PinsussAnonymous16-Dec-04 14:06 
AnswerRe: how to work with it PinmemberFurty16-Dec-04 14:15 
"It" itself doesn't collapse, rather it collapses the control you've specified in the ControlToHide property. In all other respects it works the same as the standard Splitter control. For more info read the article or the comments in the code.
GeneralRe: how to work with it PinsussAnonymous17-Dec-04 6:03 
GeneralCollapsible Splitter in Combination with DockPanelSuite Pinmemberrascreek25-Oct-04 3:25 
GeneralRe: Collapsible Splitter in Combination with DockPanelSuite Pinmemberrobdogg117-Nov-04 8:22 
GeneralRe: Collapsible Splitter in Combination with DockPanelSuite Pinmemberrobdogg117-Nov-04 9:14 
GeneralRe: Collapsible Splitter in Combination with DockPanelSuite PinmemberFurty17-Nov-04 13:26 
GeneralRe: Collapsible Splitter in Combination with DockPanelSuite Pinmemberrobdogg117-Nov-04 17:59 
GeneralRe: Collapsible Splitter in Combination with DockPanelSuite PinmemberFurty17-Nov-04 19:05 
GeneralRe: Collapsible Splitter in Combination with DockPanelSuite Pinmemberrobdogg118-Nov-04 8:09 
GeneralRe: Collapsible Splitter in Combination with DockPanelSuite Pinmemberrascreek22-Nov-04 2:47 
GeneralSplitter with tabControls PinmemberRoadRage26-Sep-04 9:57 
GeneralRe: Splitter with tabControls PinmemberFurty1-Oct-04 14:55 
GeneralMozilla PinsussDeeWorld9-Sep-04 19:23 
GeneralRe: Mozilla PinmemberFurty9-Sep-04 21:33 
QuestionHow to make the grippie shorter? Pinmemberrobdogg126-Aug-04 11:48 
AnswerRe: How to make the grippie shorter? PinmemberFurty9-Sep-04 21:41 
GeneralRe: How to make the grippie shorter? Pinmemberrobdogg110-Sep-04 9:07 
GeneralRe: How to make the grippie shorter? PinmemberFurty10-Sep-04 17:50 
GeneralRe: How to make the grippie shorter? PinmemberFurty1-Oct-04 15:02 
GeneralDude... Pinmemberrobdogg116-Aug-04 14:33 
GeneralAutomatically detection of controlToHide Pinmemberdon!marco21-Jul-04 19:39 

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 | Mobile
Web03 | 2.8.140926.1 | Last Updated 27 Aug 2003
Article Copyright 2002 by Furty
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid