What are Docking Windows?

One of the first things you notice when using Visual Studio .NET is the clever
docking windows implementation. This allows the user to reposition various tool
windows such as the Solution Explorer and Properties to dock
against different application edges. You can even make them float or become
tabbed within the same docking window.

The Magic Library provides an implementation that mimics this clever behaviour
and allows you to quickly and easily add the same feature to your own
applications.

Downloads

The first download Docking Sample contains a example application that
uses the docking windows implementation from the Magic Library. This allows you
to experiment and test out the feature. The actual source code is inside the
separate second download. At nearly 1MB in size, I thought people would prefer
to download the sample before deciding if they want to install the entire
source code project!

How do I add docking windows to my own application?

First you need to create an instance of the DockingManager class to be
and associate it with each ContainerControl derived object you want to
have a docking capability. Most of the time this will be your applications
top-level application Form.

As well as a ContainerControl reference the constructor takes a
parameter indicating the visual style required. Currently two display styles
are supported, VisualStyle.IDE for the Visual Studio .NET appearance and VisualStyle.Plain
for the older Visual C++ Version 6 appearance.

Now we need to provide the docking manager with descriptions of each dockable
unit. This is the purpose of the Content class. Each Content object
creates an association between a title, image and Control derived
object. You should read the full documentation of this important class which
can be found in the full download.

The following code shows how to create a Content instance and add it to
the docking manager inside the form constructor. It creates a RichTextBox
control that will act as a dockable notepad for use by the user.

As this is such a common operation the process has been streamlined. There are
several overrides of the Contents.Add method that will create the
required Content instance for you during the Add process. Here is
the recommended approach.

public MyForm()
{
InitializeComponent();
_dockingManager = new DockingManager(this,
VisualStyle.IDE);
// This will create and add a new Content
// object all in one operation
_dockingManager.Contents.Add(
new RichTextBox(), "Notepad",
_internalImages, _imageIndex);
}

Showing and Hiding Contents

Just adding a Content instance will not make it visible to the user. We
want our application to make this instance visible immediately, so we use the ShowContent
method as shown below: -

This shows how to find a reference to a Content object by using the
string indexer of the Contents collection. In this particular case it is
a little inefficient as we could have stored the Content reference that
is returned from the call to Contents.Add. Here is a more efficient
example: -

At some point in the future you may want to hide this instance again in which
case you can use the HideContent method. To make all the Content instances
visible or invisible use the ShowAllContent and HideAllContent methods
respectively.

Accurate Creation

Three lines of code and we have a docking window made visible to the user which
can be redocked and resized. However, at no point so far have we specified
exactly where the new Content gets shown. The docking position for a Content
made visible is the saved position from when it was last hidden. In our case
the instance has never been hidden because it has just been created.

The constructor for the Content will default the saved docking position
to be the left edge. Therefore our last example above will display the content
inside a docking window which is docked against the left edge of the
application Form. The value of the Content.DisplaySize will be
used to decide how wide the docking window should be, this defaults to 150, 150.

If you want to dock against a different edge or even begin in the floating
state then you need to do a little more work. The following code shows the use
of the AddContentWithState method to show the content with a defined
initial state: -

Create in same window

Using the above method allows a docking window to be made visible and its
position defined. But it does have the drawback that it will always create a
new docking window to host the Content instance. What if we want two or
more Content objects to be hosted inside the same docking window? To
achieve this we need to bring another method called AddContentToWindowContent
into use.

Each content is always hosted inside a WindowContent derived object. We
can remember the reference of the newly created WindowContent object and
reuse it as the destination for other Content instances. The following
example creates notePad instances that are placed inside the same
docking window, when this happens the docking window will adopt a tabbed
appearance: -

Create in same Column/Row

There is only one more ability we need to add so that any docking configuration
can be constructed at start-up. We need the ability to place docking windows in
the same column or row. To do this we have to understand more about the actual
structure of objects maintained by the docking code.

Docking is supported by providing three levels of object. Each Content object
exists inside a Window derived object which itself exists inside a Zone
derived object. The WindowContent class is a specialization of the Window
base class that has special knowledge about how to handle Content objects.
It is easiest to explain by providing some examples.

The AddContentWithState method creates a new WindowContent instance
and adds to it the provided Content parameter. Next a Zone is
created and the WindowContent instance placed inside it. The Zone
is then added to the hosting Form and positioned according to the State
parameter.

The AddContentToWindowContent adds the provided Content parameter
to the existing WindowContent instance.

The AddContentToZone method creates a new WindowContent instance
and adds to it the provided Content parameter. It then adds the new WindowContent
to the provided Zone in the correct relative position.

The following example shows how to create three notePad objects, where
the first two are added to the same WindowContent causing a tabbed
appearance to occur. The final notePad is created in its own WindowContent
and then added to the same Zone. The position value of 0 will
make the second WindowContent be positioned first in the Zone.

You can use the Content.ParentWindowContent property to discover which WindowContent
a Content instance is currently placed inside. Likewise, the WindowContent.ParentZone
property indicates the Zone a WindowContent instance is inside.
Using these and the above-described methods should allow any start up
configuration to be constructed. Note that the Content.ParentWindowContent
property returns null if the Content is not currently visible.

Control where docking can occur

If you use the SampleDocking application you will notice that you cannot
redock a docking window between a Form edge and either the MenuControl
or StatusBar controls. In order to achieve this effect we need to define
a couple of the docking manager properties.

The OuterControl property needs to be set to the first control in the Forms.Control
collection that represents the group of controls that the manager must not dock
between. Remember that the order of controls in the Form.Control collection
determines the sizing and positioning of them. So the last control in the
collection is the one that is positioned and sized first, the second to last
control will be positioned and sized in the space that remains.

As the MenuControl is the most important and needs to be positioned
first it will be last in the collection. The StatusBar is the next most
important and so is second to last in the collection. In this scenario the OuterControl
would be set to a reference of the StatusBar control. This will prevent
the docking manager from reordering any window after the StatusBar in
the collection. If the StatusBar was last in the list and the MenuControl
second to last then the OuterControl would need to reference the MenuControl
instead.

The InnerControl property needs to be set to the last control in the Forms.Control
collection that represents the group of controls that the manager must not dock
after. This might seem odd, as there is unlikely to be a docked window you
would not want the docking windows to be placed inside of. However there is a
situation where this becomes important.

If you have a control that is defined as having the Dock property of DockStyle.Fill
then this control must always occur in the Form.Control collection
before any docking windows. Otherwise you can get the situation where the
control with the DockStyle.Fill value is not sized according to the
actual space left over when all docking windows have been placed. Because this
control is further up the list of controls it calculates its size without
taking into account any docking windows that occur earlier in the collection.

The following shows a MenuControl, StatusBar and a RichTextBox
being created and added to the Form.Control collection. It then sets the
correct InnerControl and OuterControl values to generate the
expected runtime operation.

Persistence

Many applications need to be able to remember several different docking
configurations of the Content objects and be able to switch between them
at runtime. You might also want to save the configuration when the application
is shutdown so that it can be restored at start-up. The code to save and load
configurations is as follows:-

// Save the current configuration to a named file
_dockingManager.SaveConfigToFile(
"MyFileName.xml");
...
// Load a saved configuration and apply
// immediately
_dockingManager.LoadConfigFromFile(
"MyFileName.xml");

There are a couple of issues to remember though. The saving process does not
save the actual Content objects but just the state information it needs
in order to restore that Content to the same docking size/position
later. So the Content object must already exist and be part of the
docking manager when the load operation takes place because loading will not
recreate those objects.

The second point is that the save and load use the Title property of the Content
to identify the information. If you change the Title of a Content
object between saving and loading then the latter process will fail to
associate the saved information to the object. This will not cause an exception
but that Content will not be updated with the required configuration.

If you need to save the configuration information into a database or simply
save it internally then you do not have to save into a file. There are matching
methods for saving and loading into byte arrays which are easy to store within
your application or to a database. For even greater control use the methods
that take a stream object instance, in which case you must create and provide
the stream object instance, but this gives the developer complete control over
the storage medium.

Some developers might find it useful to save and load some additional custom
details inside the configuration data. This prevents the need for two sets of
saved data which then need to be maintained in parallel. The SaveCustomConfig
event is generated when all the docking information has been written and allows
you to add additional information at the end.

On loading the LoadCustomConfig is event is generated so that the
corresponding information can be read back in again. The following sample code
shows a trivial example of this:-

Known problems

One of nice features of the docking windows is the ability to take a Form
derived class and use it as the content of a docking window. You need to be
careful though, as sometimes a Form you generate will have the AutoScaleBaseSize
property define. This can cause the Form instance to be sized
incorrectly when it is shown for the first time. If someone comes up with an
answer to this problem then please let me know!

A second problem is the use of the RichTextBox. If you use this as the
content for the docking window and then move the docking window to a different
edge it will sometimes cause the control to be recreated. In this case it loses
any coloring information. So your blue/red text inside the RichTextBox suddenly
becomes the default black. Again, if anyway knows how to fix this issue then
please let me know.

Revision History

20 Sept 2002 - Initial revision

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

Share

About the Author

I am a big fan of .NET and have been working on developing a free user interface library to enhance the very basic controls that come out-of-the-box. Download the free source code project from http://www.dotnetmagic.com. I often carry out bespoke development work for companies, so feel free to email me for a quote on your .NET needs!

Comments and Discussions

hi,
i want to set maximum and minimum for the content,i done it but when i
undock the content and restore it ,the maximum minimum i set for the content
changed(i.e) it can be resized to any extent,how to avoid this .its very urgent

Hi, I have a litlle problem. I create two dockable contents in may form and they are AutoHidden, and docked on the left side of the form. When I pin up one of them, it pin's well, but the other content moove's, and now it's docked on the right side of my pined content. Here is my code...

dear you did very nice work.but i have still one problem that is , i want to change location of the docked form.like1-i want to display "Test Form" on dockable window2-it will display form(dockable window) at location (X=500,Y=100)3-and size of the dockable window is (width=250,height=100)

A few years ago, I was trying to learn why docking in floating windows is possible only to top or bottom, and not to the right or left edge. This took place in the forum of the Crownwood Magic Library, operational at that time.

My question now is whether it may be possible to restore floating contents which are docked (against the bottom or top, of course). Somehow I was not even able to restore two contents that are in the same floating window, using the save/load mechanism provided by the class DockingManager: If two contents are afloat, they are restored in two different windows!

I have also tried with my own save/restore mechanism. I can restore contents that float and are in the same window, but undocked. I tried to add to class DockingManager my own function AddContentWithState, to use the ContainerControl of the window that holds the contents, but all functions involved in docking work only in relation with the private ContainerControl _container. So before writing a new set of functions which accept a ContainerControl as parameter, I decided to give it a try and post this message here.

(Anyway, I find it confusing that mouse-driven docking in floating windows is allowed, but programatically seems impossible. But then, it may be my insufficient knowledge about your code.)

I hope this goes trhough, and that you find the question interesting enough to post a reply.

I've been asked a few times recently where people can download the last available version of the Magic Library that I created some time ago. You can still get hold of version 1.7.4 and use the library in your commerical apps. Just use the following link to download it...

http://www.dotnetmagic.com/downloads/MagicLibrary174.msi

Note that this was developed under VS2003 and so I am not sure how well it will work under VS2005. In theory any changes should be pretty minor. I have recently built a free library that is aimed at .NET2. You can try this new one out at...

I know how to make a control visible or invisible so that it doesn't even appear on the screen, but if I want to have to control start off collapsed (like you unpushed the pin) how do you do that?

Additionally, how do you set the size of the width of the docking panel? I've tried setting the width on the WindowContent control and that seems to resize it internally (so the control within thinks it is in a control of the width I specified), but does not actually affect the WindowContent's borders, which results in my control getting cut off and the WindowContent not being resizable.

I think what you are trying to do is set the size of the window that appears when you mouse over the tab of the hidden content. There is a property you must set in the contect class called AutoHideSize.

HI
There is a feature to take controls out from the form. I want to remove that feature. Could you please tell me how to remove that feature. Or
can I have to source code of the dll.
Thanks!
You have done a great job

I have tried to use the DockingManager to make a Panel on one of my forms dock to the side of the form so that it can be shown/hidden at will. So rather than use the new keyword in the Contents.Add method I just specifiy the name of the panel...

This seems to work, but there are databound controls on the panel and it seems to screw up the databinding such that when I try to fetch data to populate the controls it throws an exception and says that there is already "An open DataReader associated with this connection that must be closed first". The moment I remove the ShowContent line it works as expected but the panel simply appears on the form as it does at design time. Any ideas how I can get around this at all?

i have one zone with a windowcontent which have two contents in it. One content is ListView and other is Panel. These are two things i require docking with. But there are one more panel on Form.

I require dockable contents to synchronize with other control on form. I want to trap the event where Size and location of dockable contents changes So that i can set proper size and location of other controls according to my requirement. Your help would greatly help me.