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

Add docking and floating support easely and quickly with DockExtender

By , 4 Jul 2007
Rate this:
Please Sign up or sign in to vote.
  • Download demo + source files - 33.9 KB

    Sample Image - DockExtender1.png

    Introduction

    So you are looking to add 'control docking' features to your winform application? E.g. you want to have a floating toolbar or allow users to dock parts of your form at a different location at runtime? This tiny and lightweight control extender for C# 2.0 will help you enable these features.

    I wrote this solution with the following purposes/constraints in mind:

    • Docking/Floating behaviour must be easy to incorporate and use in new and existing winform projects
    • It may not pose too many constraints on the structuring of the form layout
    • The code must be lightweight. That is, have a small footprint and require few resources; i.e. may not be dependent on external, magic libraries.
    • Dockable controls must support resizing
    • The code must be easy to use: steep learning curve!
    • Use native C# where possible.

    In order to support these constraints I had to cut some features. E.g. this solution will not allow docking of single tab pages in tab controls. Also because of the basic programmatic interface, the client has little control over what happens with the floating/docking controls. So if you are looking for a more full blown (but also bloated and much heavier) solution that supports all types of docking you might want to consider Weifen Luo's DockManager on source forge or commercial products.

    Still interested in a low impact, easy to use, and lightweight approach? This solution will allow you to practically make any control dockable/floatable in just a few lines of code! So how does it work you ask? Just keep on reading.

    Using the code

    It's simple, first setup the project: create a new winform project, add the DockExtender.cs, Floaty.cs and Overlay.cs files to your project. Then, for the sake of the example, add a ToolStrip control to your form.

    Then add the ControlExtenders namespace to the form and declare the DockExtender object in the form. Then in the constructor of your form create a new DockExtender object and Attach the (container) control to it that you want make dockable/floatable (the toolstrip in this example). Once it is attached, the DockExtender class will extend the behaviour of your attached control and will take care of the floating/docking behaviour for you. The following code will make a toolbar control dockable/floating:

        //add namespace    
        using ControlExtenders; 
        
        ...
    
        // declare dockExtender as a member of the form
             DockExtender dockExtender;
    
        ...
    
        // in the Form1() constructor create the DockExtender to manage controls 
        // on for the 'dockHost'
             dockExtender = new DockExtender(this); // 'this' is Form1
                
             // Attach the toolStrip1 toolbar. The 2nd argument is the handle.  
             // This will make the whole toolstrip draggable. A floaty object is 
             // returned.
             IFloaty floaty = dockExtender.Attach(toolStrip1);

    There, that's it! Two lines of code to make your first dockable/floatable control!

    Drag and resize

    Basically all you need is what I call a 'Container' control. It represents the part of your form that can be made floatable/dockable. In order to drag the Container control around with the mouse you needs a 'Handle', this is basically the Container's caption/griphandle. Of course if you supply the container itself as the handle, the whole container will become the handlebar (as in the example above)!

    Now the beauty of this solution is that the 'Container' control and 'Handle' control can be of any type (as long as it is derived from ScrollableControl and Control respectively)! In my demo for simplicity's sake, I used a Panel for the container and a Label (hosted on the panel) for the handle. (Note: Obviously the handle looks rather plain, but it would be very easy to make it nicer looking. This is beyond the scope of this article though).

    To make it more interesting it would be very handy to allow a docked container control to be resized by the user. The DockExtender supports this, yet there is one restriction. You can only use a Splitter control (or a subclass of Splitter) with the DockExtender. E.g.:

    // Attach the a panel as container, a label as handlebar and a splitter to 
    // resize the control to make panel1 dockable/floatable and resizable
    IFloaty floaty = dockExtender.Attach(panel1, label1, splitter1);

    Now when executing this code, and docking the panel1 on the form your will notice that the splitter is automatically positioned on top of the panel, allowing the user to resize the panel.

    Docking (Z-)order

    By default containers are docked on the inside of the docking host (ie. the form). This means, that if other controls are already docked in the host, the container will be docked on the inside of the host (bring to front).

    In some cases you may not want to dock only on the inside, but rather on the outside. E.g. toolbars or menus are typically docked on the outside (sent to back). To allow this we can use the returned floaty object to set some additional properties: floaty.DockOnInside:

    // Attach the toolStrip1 toolbar as container and handle
    IFloaty floaty = dockExtender.Attach(toolStrip1);
    
    // supply false for 'dockOnInside'-flag. Allow docking on the outside of the 
    // docking host
    floaty.DockOnInside = false; // dock to outside

    If the DockOnInside is set to false the control will be set to the back, making it dock on the outside of the dockhost.

    Docking event

    So in this example the toolbar will be docked on the outside. This raises a problem though, because this means that for instance the menubar will be docked below the toolbar which is not desired. In order to cope with this problem, a 'Docking' event is raised when a control is docked. This allows the client to set the correct Z-Ordering of the non-dockable controls, such as the menubar, statusbar and the inner document.

    To capture this event, you need to do the following:

    // get a reference to the floating control to which the container is attached 
    // by the DockExtender
    IFloaty floaty = dockExtender.Attach(panel1, label1, splitter1); 
        
    // the floaty will raise a Docking event when it is docking.
    floaty.Docking += new EventHandler(floaty_Docking);
    
    ...
    
    void  floaty_Docking(object sender, EventArgs e)
    {
        // make sure the ZOrder remains intact for the menubar and statusbar
        menuStrip1.SendToBack();
        statusStrip1.SendToBack();
    }

    Now the menuStrip and statusStrip will always be positioned correctly with the correct Z-Order.

    More features

    A few more trivial features are available that I will not explain in depth here. In order to have more control over the display of the floaty/container, you can programmatically Show() and Hide() a IFloaty object, or use the dockManager's Show(container) /Hide(container) method to display the container control. Also you can iterate through the list of registered DockExtender.Floaties.

    Sources and Demo

    The sources project contains all sources of the DockExtender and of the demo project, no external or magic libraries are needed! Feel free to use and adapt this code for your own purposes.

    How does it work?

    For those that are interested I will explain here how the internals of the DockExtender work. The DockExtender consists of three main classes: 'DockExtender', 'Floaty' and 'Overlay'. DockExtender is more a management class that manages the floaties. For each container that is attached, a corresponding Floaty object is created to which the container is attached.

    The Floaty does most of the work. The Overlay class is used by the floaty to display the designated docking area's. Basically there are the following types of actions:

    • Attach the container
    • Go from docked state to floating state
    • Select designated docking area
    • Go from floating state to docked state

    Attach the container

    1. The state of the container is stored in the DockState struct. This allows to restore to the previous state if necessary.
    2. The Floaty will attach mouse move event handlers to the handle and track the movement of the mouse when it is hovering over the handle

    From Docked to Floating

    1. Mouse move events are triggered by the handle
    2. Resize the floaty form to the size of the docked control
    3. Send a Left-button-up windows message to the handle so it will stop firing mouse move events (requires Win API)
    4. Re-parent the control into the floaty and dock it as Filled
    5. Position the floaty under the mouse cursor
    6. Hide the handle
    7. Send a system command windows message to the floaty to start the size-move loop (requires Win API)
    8. The mouse is now dragging the floaty

    Select designated docking area

    1. Get the mouse position
    2. Check over which attached forms/controls the mouse is hovering
    3. Determine the maximum potential docking area.
    4. If the mouse is moving into a designated area, calculate the position and size of the overlay
    5. Present the overlay control for the designated area
    6. Dock the control

    From Floating to Docked

    1. Set the DockState parameters to the new docking area
    2. Re-parent the container into the designated control/form
    3. Show the handle
    4. Hide the floaty form
    5. Set the Z-Order of the container
    6. Fire the Docking event

    Points of Interest

    Using an 'extender' approach makes it so much easier to develope. It means you are no longer required to use specific controls or derive from them in order to extend their behaviour. This way any control can be extended fast and easy without a lot of code modifications, and is therefore very well suited to be incorporated into existing projects that require these extended features.

    Origionally the idea was to allow docking also inside containers, but this gave a number of new challanges to tackle, so I left it out of this demo by default. The code can however still be triggired by setting the floaty.DockOnHostOnly to false. Note that setting this property may result in undesired effects.

    Just one personal note I want to make here is that I saw no other way than to use the SendMessage API instead of pure c#. I prefer to write c# in pure c# and not resort to win API's, but there you have it.

    If you decide to use this code, feel free to post pointers and tips for improvements. Happy Coding!

    History

    Version 1.0 of the DockExtender was published on the 23th of Februari 2007.

  • License

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

    About the Author

    Herre Kuijpers
    Architect Capgemini
    Netherlands Netherlands
    Currently Herre Kuijpers is employed at Capgemini Netherlands for over 10 years, where he developed skills with all kinds of technologies, methodologies and programming languages such as c#, ASP.Net, Silverlight, VC++, Javascript, SQL, UML, RUP, WCF. Currently he fulfills the role of software architect in various projects.
     
    Herre Kuijpers is a very experienced software architect with deep knowledge of software design and development on the Microsoft .Net platform. He has a broad knowledge of Microsoft products and knows how these, in combination with custom software, can be optimally implemented in the often complex environment of the customer.

    Comments and Discussions

     
    GeneralVery informative contents PinmemberMember 102265801-Apr-14 15:58 
    GeneralMy vote of 5 Pinmemberteng33887-Aug-13 15:12 
    GeneralMy vote of 5 PinmemberMember 816903429-Oct-12 16:30 
    QuestionUrgent help is required on how to do docking on LOAD Event Pinmemberbosercv10-May-12 1:09 
    QuestionUrgent help is required on how to do docking on LOAD Event Pinmemberbosercv10-May-12 1:08 
    QuestionImplementation using wpf Pinmemberakstemptation7-Nov-11 22:32 
    AnswerRe: Implementation using wpf PinmemberHerre Kuijpers8-Nov-11 21:32 
    QuestionGreat job. How to make it work on Mono? Pinmemberzengdj12-Mar-11 23:52 
    GeneralSwapping control into a different parent [modified] PinmemberNick Alexeev30-Jan-11 10:28 
    GeneralRe: Swapping control into a different parent PinmemberHerre Kuijpers30-Jan-11 10:49 
    GeneralRe: Swapping control into a different parent [modified] PinmemberNick Alexeev30-Jan-11 12:09 
    GeneralRe: Swapping control into a different parent PinmemberHerre Kuijpers30-Jan-11 21:01 
    GeneralRe: Swapping control into a different parent PinmemberNick Alexeev1-Feb-11 20:43 
    GeneralDock Indicator PinmemberNick Alexeev15-Jan-11 16:46 
    GeneralRe: Dock Indicator PinmemberHerre Kuijpers16-Jan-11 0:03 
    Generalmy vote of 4 Pinmembertr021717-Dec-10 21:31 
    GeneralFloaty PinmemberDrJBN18-Nov-10 22:33 
    QuestionSome unusual action and request solution PinmemberSports Kuo4-Jul-10 22:41 
    AnswerRe: Some unusual action and request solution PinmemberHerre Kuijpers5-Jul-10 3:24 
    GeneralRe: Some unusual action and request solution PinmemberSports Kuo5-Jul-10 16:17 
    GeneralRe: Some unusual action and request solution PinmemberHerre Kuijpers6-Jul-10 3:14 
    GeneralRe: Some unusual action and request solution PinmemberSports Kuo7-Jul-10 16:00 
    GeneralMy vote of 5 PinmemberSports Kuo4-Jul-10 22:18 
    GeneralGood work , but there are some small issues with ToolStripContainer PinmemberMaciej Zbrzezny17-May-09 4:47 
    GeneralGood effort but PinmemberKam10-Apr-09 8:22 
    General^-^ good~ PinmemberNoHuGa16-Jan-09 5:02 
    QuestionCan use this tool to make a Form docked or floating not a simple Control Pinmembercwencool12-Jan-09 17:27 
    QuestionToolStrip to float only when mouse has left its container Pinmemberalias4716-Aug-07 20:01 
    AnswerRe: ToolStrip to float only when mouse has left its container PinmemberHerre Kuijpers16-Aug-07 20:59 
    QuestionRe: ToolStrip to float only when mouse has left its container Pinmemberalias4716-Aug-07 21:20 
    AnswerRe: ToolStrip to float only when mouse has left its container PinmemberHerre Kuijpers17-Aug-07 10:39 
    GeneralRe: ToolStrip to float only when mouse has left its container PinmemberVishal_Dattani9-Mar-09 21:24 
    QuestionLoad help file in left panel PinmemberZahrashahla7-Aug-07 2:37 
    AnswerRe: Load help file in left panel PinmemberHerre Kuijpers7-Aug-07 21:46 
    GeneralRe: Load help file in left panel PinmemberZahrashahla7-Aug-07 22:47 
    GeneralRe: Load help file in left panel PinmemberHerre Kuijpers7-Aug-07 23:28 
    GeneralRe: Load help file in left panel PinmemberZahrashahla8-Aug-07 0:26 
    GeneralRe: Load help file in left panel PinmemberHerre Kuijpers8-Aug-07 1:53 
    GeneralRe: Load help file in left panel PinmemberZahrashahla8-Aug-07 2:14 
    GeneralNice and easy to use PinmemberZapper.Net10-Jul-07 3:49 
    GeneralNice ! Pinmemberjim22620-Jun-07 22:54 
    GeneralExcellent work! Pinmemberjesuscheung18-May-07 11:46 
    GeneralNice... PinmemberPaul Selormey22-Feb-07 23:34 
    GeneralRe: Nice... PinmemberHerre Kuijpers22-Feb-07 23:52 

    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
    Web02 | 2.8.140415.2 | Last Updated 4 Jul 2007
    Article Copyright 2007 by Herre Kuijpers
    Everything else Copyright © CodeProject, 1999-2014
    Terms of Use
    Layout: fixed | fluid