Click here to Skip to main content
15,879,184 members
Articles / Programming Languages / C#

Add Docking and Floating Support Easily and Quickly with DockExtender

Rate me:
Please Sign up or sign in to vote.
4.84/5 (69 votes)
4 Jul 2007CPOL7 min read 198.2K   7.7K   212   48
DockExtender allows you to add docking / floating / resizing behaviour to any control on your Winform with just a few lines of code, making it very easy for your application to support basic docking features.
If you are looking to add 'control docking' features to your winform application, this solution will allow you to practically make any control dockable/floatable in just a few lines of code!

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:

C#
//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.:

C#
// Attach the 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, you 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 (i.e., 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:

C#
// 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:

C#
// 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 areas. 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 develop. 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.

Originally, 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 triggered 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 APIs, but there you have it.

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

History

  • 23rd February, 2007: Version 1.0 of the DockExtender published

License

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


Written By
Architect Rubicon
Netherlands Netherlands
Currently Herre Kuijpers is employed at Rubicon. During his career he developed skills with all kinds of technologies, methodologies and programming languages such as c#, ASP.Net, .Net Core, VC++, Javascript, SQL, Agile, Scrum, DevOps, ALM. 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

 
PraiseExcellent Pin
warra warra20-Dec-21 23:52
warra warra20-Dec-21 23:52 
QuestionDocking and floating windows in win forms Pin
Member 1228519411-Feb-19 22:46
Member 1228519411-Feb-19 22:46 
QuestionWorked Pin
Amit Kumar27-Feb-18 0:22
Amit Kumar27-Feb-18 0:22 
GeneralThank You So Much!!! Pin
Larx8325-May-15 22:55
Larx8325-May-15 22:55 
GeneralMy vote of 5 Pin
teng33887-Aug-13 15:12
teng33887-Aug-13 15:12 
GeneralMy vote of 5 Pin
Member 816903429-Oct-12 16:30
Member 816903429-Oct-12 16:30 
QuestionUrgent help is required on how to do docking on LOAD Event Pin
bosercv10-May-12 1:09
bosercv10-May-12 1:09 
QuestionUrgent help is required on how to do docking on LOAD Event Pin
bosercv10-May-12 1:08
bosercv10-May-12 1:08 
QuestionImplementation using wpf Pin
akstemptation7-Nov-11 22:32
akstemptation7-Nov-11 22:32 
AnswerRe: Implementation using wpf Pin
Herre Kuijpers8-Nov-11 21:32
Herre Kuijpers8-Nov-11 21:32 
QuestionGreat job. How to make it work on Mono? Pin
zengdj12-Mar-11 23:52
zengdj12-Mar-11 23:52 
GeneralSwapping control into a different parent [modified] Pin
Nick Alexeev30-Jan-11 10:28
professionalNick Alexeev30-Jan-11 10:28 
GeneralRe: Swapping control into a different parent Pin
Herre Kuijpers30-Jan-11 10:49
Herre Kuijpers30-Jan-11 10:49 
GeneralRe: Swapping control into a different parent [modified] Pin
Nick Alexeev30-Jan-11 12:09
professionalNick Alexeev30-Jan-11 12:09 
GeneralRe: Swapping control into a different parent Pin
Herre Kuijpers30-Jan-11 21:01
Herre Kuijpers30-Jan-11 21:01 
GeneralRe: Swapping control into a different parent Pin
Nick Alexeev1-Feb-11 20:43
professionalNick Alexeev1-Feb-11 20:43 
GeneralDock Indicator Pin
Nick Alexeev15-Jan-11 16:46
professionalNick Alexeev15-Jan-11 16:46 
GeneralRe: Dock Indicator Pin
Herre Kuijpers16-Jan-11 0:03
Herre Kuijpers16-Jan-11 0:03 
Generalmy vote of 4 Pin
tgis.top17-Dec-10 21:31
tgis.top17-Dec-10 21:31 
GeneralFloaty Pin
DrJBN18-Nov-10 22:33
DrJBN18-Nov-10 22:33 
QuestionSome unusual action and request solution Pin
Sports Kuo4-Jul-10 22:41
Sports Kuo4-Jul-10 22:41 
AnswerRe: Some unusual action and request solution Pin
Herre Kuijpers5-Jul-10 3:24
Herre Kuijpers5-Jul-10 3:24 
GeneralRe: Some unusual action and request solution Pin
Sports Kuo5-Jul-10 16:17
Sports Kuo5-Jul-10 16:17 
GeneralRe: Some unusual action and request solution Pin
Herre Kuijpers6-Jul-10 3:14
Herre Kuijpers6-Jul-10 3:14 
GeneralRe: Some unusual action and request solution Pin
Sports Kuo7-Jul-10 16:00
Sports Kuo7-Jul-10 16:00 

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

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