Magic Logo
top
contents
show / hide
accurate
same window
same column
inner / outer
persistence
methods
properties
events
Class DockingManager
An instance of the DockingManager class needs to be created and associated 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.

The following code shows how to add docking support to a Form

C#

   
using Crownwood.Magic.Common;
using Crownwood.Magic.Docking;

public class MyForm : Form
{
   protected Crownwood.Magic.Docking.DockingManager _dockingManager = null;

   public MyForm()
   {
      InitializeComponent();

      // Create the object that manages the docking state
      _dockingManager = new DockingManager(this, VisualStyle.IDE);
   }
}

VB.NET
   
Imports Crownwood.Magic.Common
Imports Crownwood.Magic.Docking

Public Class MyForm
   Inherits Form

   Protected _dockingManager As Crownwood.Magic.Docking.DockingManager

   Public Sub New()
      InitializeComponent()

      ' Create the object that manages the docking state
      _dockingManager = New DockingManager(Me, VisualStyle.IDE)
   End Sub
End Class

Contents
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 before continuing, use this link.

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.

C#


   public MyForm()
   {
      InitializeComponent();

      _dockingManager = new DockingManager(this, VisualStyle.IDE);

      Content notePad = new Content(_dockingManager);
   
      notePad.Title = "Notepad";
      notePad.Control = new RichTextBox();
      notePad.ImageList = _internalImages;
      notePad.ImageIndex = _imageIndex;

      _dockingManager.Contents.Add(notePad);
   }
   
VB.NET

   Public Sub New()
      InitializeComponent()

      _dockingManager = New DockingManager(Me, VisualStyle.IDE)

      Dim notePad As New Content(_dockingManager)
   
      notePad.Title = "Notepad"
      notePad.Control = new RichTextBox()
      notePad.ImageList = _internalImages
      notePad.ImageIndex = _imageIndex

      _dockingManager.Contents.Add(notePad)
   End Sub
   
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.

C#

   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);
   }

VB.NET

   Public Sub New()
   {
      InitializeComponent()

      _dockingManager = New DockingManager(Me, VisualStyle.IDE)

      ' This will create and add a new Content object all in one operation
      _dockingManager.Contents.Add(New RichTextBox(), "Notepad", 
                                   _internalImages, _imageIndex)
   End Sub

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: -

   public MyForm()
   {
      InitializeComponent();

      _dockingManager = new DockingManager(this, VisualStyle.IDE);

      _dockingManager.Contents.Add(new RichTextBox(),
                                   "Notepad", 
                                   _internalImages, 
                                   _imageIndex);

      // Make the content with title 'Notepad' visible
      _dockingManager.ShowContent(_dockingManager.Contents["Notepad"]);
   }

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: -

   public MyForm()
   {
      InitializeComponent();

      _dockingManager = new DockingManager(this, VisualStyle.IDE);

      Content notePad = _dockingManager.Contents.Add(
        new RichTextBox(), "Notepad", _internalImages, _imageIndex);

      _dockingManager.ShowContent(notePad);
   }

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: -


   public MyForm()
   {
      InitializeComponent();

      _dockingManager = new DockingManager(this, VisualStyle.IDE);

      Content notePad = _dockingManager.Contents.Add(
        new RichTextBox(), "Notepad", _internalImages, _imageIndex);

      // Request new Docking window be created and displayed on the right edge
      _dockingManager.AddContentWithState(notePad, State.DockRight);
   }

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: -


   public MyForm()
   {
      InitializeComponent();

      _dockingManager = new DockingManager(this, VisualStyle.IDE);

      Content notePad1 = _dockingManager.Contents.Add(
        new RichTextBox(), "Notepad1", _internalImages, _imageIndex);

      Content notePad2 = _dockingManager.Contents.Add(
        new RichTextBox(), "Notepad2", _internalImages, _imageIndex);

      WindowContent wc = _dockingManager.AddContentWithState(
                            notePad1, State.DockRight) as WindowContent;

      // Add the second notePad2 to the same WindowContent
      _dockingManager.AddContentToWindowContent(notePad2, wc);
   }

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.


   public MyForm()
   {
      InitializeComponent();

      _dockingManager = new DockingManager(this, VisualStyle.IDE);

      Content notePad1 = _dockingManager.Contents.Add(
        new RichTextBox(), "Notepad1", _internalImages, _imageIndex);

      Content notePad2 = _dockingManager.Contents.Add(
        new RichTextBox(), "Notepad2", _internalImages, _imageIndex);

      Content notePad3 = _dockingManager.Contents.Add(
        new RichTextBox(), "Notepad3", _internalImages, _imageIndex);

      WindowContent wc = _dockingManager.AddContentWithState(
                            notePad1, State.DockRight) as WindowContent;

      _dockingManager.AddContentToWindowContent(notePad2, wc);

      // Add a new WindowContent to the existing Zone already created
      _dockingManager.AddContentToZone(notePad3, wc.ParentZone, 0);
   }

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.

C#


   public MyFormConstructor()
   {
      // This block would normally occur inside a call to:-
      // InitializeComponent();
      {
         RichTextBox filler = new RichTextBox();
         filler.Dock = DockStyle.Fill;
         Controls.Add(filler);

         StatusBar status = new StatusBar();
         status.Dock = DockStyle.Bottom;
         Controls.Add(status);

         MenuControl menu = new MenuControl();
         menu.Dock = DockStyle.Top;
         Controls.Add(menu);
      }

      _dockingManager = new DockingManager(this, VisualStyle.IDE);

      _dockingManager.InnerControl = filler;
      _dockingManager.OuterControl = status;

      // Now create and setup my Content objects
      ...
   }

VB.NET

   Public Sub New()
      ' This block would normally occur inside a call to:-
      ' InitializeComponent();

         Dim filler As New RichTextBox()
         filler.Dock = DockStyle.Fill
         Controls.Add(filler)

         Dim status As New StatusBar()
         status.Dock = DockStyle.Bottom
         Controls.Add(status)

         Dim menu As New MenuControl()
         menu.Dock = DockStyle.Top
         Controls.Add(menu)

      _dockingManager = New DockingManager(Me, VisualStyle.IDE)

      _dockingManager.InnerControl = filler
      _dockingManager.OuterControl = status

      ' Now create and setup my Content objects
      ...
   End Sub

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:-


   public void InitialiseComponent()   
   {
      ...
      
      // Setup custom config handling at appropriate place in code
      _manager.SaveCustomConfig += 
         new DockingManager.SaveCustomConfigHandler(OnSaveConfig);
         
      _manager.LoadCustomConfig += 
         new DockingManager.LoadCustomConfigHandler(OnLoadConfig);
   }
   
   protected void OnSaveConfig(XmlTextWriter xmlOut)
   { 
      // Add an extra node into the config to store some example information
      xmlOut.WriteStartElement("MyCustomElement");
      xmlOut.WriteAttributeString("ExampleData1", "Hello");
      xmlOut.WriteAttributeString("ExampleData2", "World!");
      xmlOut.WriteEndElement();
   }

   protected void OnLoadConfig(XmlTextReader xmlIn)
   {
      // We are expecting our custom element to be the current one
      if (xmlIn.Name == "MyCustomElement")
      {
         // Read in both the expected attributes
         string attr1 = xmlIn.GetAttribute(0);
         string attr2 = xmlIn.GetAttribute(1);

         // Must move past our element
         xmlIn.Read();
      }
   }
   
Methods
public virtual void ShowAllContents()
Make all hidden Content objects visible.

public virtual void HideAllContents()
Make all visible Content objects hidden.

public virtual bool ShowContent(Content c)
If the provided object is hidden then it will be restored to become visible.

public virtual bool HideContent(Content c)
If the provided object is visible then it will become hidden.

public virtual void HideContent(Content c, bool record, bool reorder)
Provides greater control over actions taken when a Content becomes hidden.
This is provided for internal use by the docking windows framework code.
If record is set then it will record the current Content position before hiding window.
If reorder is set then it will move the Content to the end of the Contents collection.

public virtual Window CreateWindowForContent(Content c)
Used to create an appropriately configured Window object for the given Content object.
Currently this ensures the caption bar matches the requested style specified in the constructor.

public virtual Zone CreateZoneForContent(State zoneState)
Creates an appropriate Zone object and adds it to the managed control.

public WindowContent AddContentWithState(Content c, State newState)
Will create an appropriate Window for hosting the provided Content and place inside a new Zone which is placed according to the State given.

public WindowContent AddContentToWindowContent(Content c, WindowContent wc)
If you already have a reference to a WindowContent object then this method will add the provided Content into that object.

public Window AddContentToZone(Content c, Zone z, int index)
If you already have a reference to a Zone object then this will create a new Window to host the provided Content and then add the Window to the given relative position inside the Zone.

public void ReorderZoneToInnerMost(Zone zone)
Repositions the given reference within the list of Form controls to the innermost valid point.

public void ReorderZoneToOuterMost(Zone zone)
Repositions the given reference within the list of Form controls to the outermost valid point.

public byte[] SaveConfigToArray()
Saves layout information into an array of bytes using Encoding.Unicode.

public byte[] SaveConfigToArray(Encoding encoding)
Saves layout information into an array of bytes using caller provided encoding object.

public void SaveConfigToFile(string filename)
Saves layout information into a named file using Encoding.Unicode

public void SaveConfigToFile(string filename, Encoding encoding)
Saves layout information into a named file using caller provided encoding object.

public void SaveConfigToStream(Stream stream)
Saves layout information into a stream object Encoding.Unicode.

public void SaveConfigToStream(Stream stream, Encoding encoding)
Saves layout information into a stream object using caller provided encoding object.

public void LoadConfigFromArray(byte[] buffer)
Loads layout information from given array of bytes.

public void LoadConfigFromFile(string filename)
Loads layout information from given filename.

public void LoadConfigFromStream(Stream stream)
Loads layout information from given stream object.

public Rectangle InnerResizeRectangle(Control source)
Used to compute the rectangle of space left over after taking into account the size of all the docking windows. When a valid source is provided it will reduce the rectangle by twice the size of those controls inside of it.

public void ResetColors()
Use this to reset all colors to the defaults used when the docking manager is first created.

public void UpdateInsideFill()
Used internally to update the correct inner most docking window with the correct docking style when a change has occured to docking layout.

public void RemoveShowingAutoHideWindows()
If any of the AutoHide windows is currently being shown then this will remove it from display immediately.

public void BringAutoHideIntoView(Content c)
If the given content instance is in the AutoHide mode then it will be selected and slide out into view.

public virtual void OnShowContextMenu(Point screenPos)
Use this method to initiate the docking context menu at given screen location.

Properties
public ContainerControl Container
The object to which the docking manager instance is attached.
Default: null

public ManagerContentCollection Contents
The collection of Content objects that the manager is responsible for docking.

public Control InnerControl
Docking windows will not be allowed to dock inside of the specified control.
Default: null

public Control OuterControl
Docking windows will not be allowed to dock outside of the specified control.
Default: null

public bool ZoneMinMax
If more than one Window is docked in the same column/row should they have a maximize capability.
Default: true

public bool InsideFill
When defined the innermost docking window will assume the Fill docking style so that you can create applications whose client area consists only of docking windows.
Default: false

public bool AutoResize
When defined a resizing of the control will cause the docking windows to be resized smaller if they would start overlapping. Note that the windows will not be sized bigger again when the control is increased in size.
Default: true

public Size InnerMinimum
Defines the minimum size of the inner control, if resizing the application would cause the inner control to become smaller than this then the docking windows are resized smaller instead.
Default: Size(20, 20)

public VisualStyle Style
Read only property returning the style used when the object was created.

public int ResizeBarVector
Defines the width/height of resize bars used between docking windows. A value of -1 will cause the appropriate default value for the selected style to be used instead.
Default: -1

public Color BackColor
Background drawing color used in the caption bar when the docking window is not selected.
Default: SystemColors.Control

public Color ActiveColor
Background drawing color used in the caption bar when the docking window is selected.
Default: SystemColors.ActiveCaption

public Color ActiveTextColor
Text drawing color used in the caption bar when the docking window is selected.
Default: SystemColors.ActiveCaptionText

public Color InactiveTextColor
Text drawing color used in the caption bar when the docking window is not selected.
Default: SystemColors.ControlText

public Color ResizeBarColor
Background color used to draw the resize bar controls between docking windows.
Default: SystemColors.Control

public Font CaptionFont
Used when drawing text in the caption bars.
Default: SystemInformation.MenuFont

public Font TabControlFont
Used when drawing text in the TabControl that appears when multiple content is shown inside the same docking window.
Default: SystemInformation.MenuFont

public bool PlainTabBorder
If the appearance is defined as VisualStyle.Plain and this PlainTabBorder property is defined then a full dumped border is drawn around the docking window content.
Default: false

Events
public event ContextMenuHandler ContextMenu
Fired when the user right clicks on the caption area of a docking window which will attempt to show a context menu. Hook into this event to either cancel it altogether or to customize the menu commands presented to the user.

public event ContentHidingHandler ContentHiding
Fired when the user causes a content object to be hidden. Either when the user presses the close button on the caption bar or closes a floating form in which case the event is fired for each content in the floating form. Hook into this event to cancel the close operation from occuring and so preventing the user from hiding a content.

public event ContentHandler ContentHidden
Fired after a content object has been hidden and occurs from either user or programmatic actions. You cannot prevent the operation occuring as this event is generated after the content had already been hidden but can be used to perform extra processing.

public event ContentHandler ContentShown
Fired after a content object has been made visible and occurs from either user or programmatic actions. You cannot prevent the operation occuring as this event is generated after the content had already been shown but can be used to perform extra processing.

public event TabControlCreatedHandler TabControlCreated
Fired whenever the docking windows code needs to create a TabControl instance for use inside a docking window. When generated the control has already been created and setup with the default properties, but use this event to customize the properties as required.

public event SaveCustomConfigHandler SaveCustomConfig
Fired when persisting out configuration information about the current docking windows layout. The event is fired at the end of the process and allows the developer to add custom XML information into the output stream.

public event LoadCustomConfigHandler LoadCustomConfig
Fired when persisting in configuration information to restore the docking configuration to a previously saved state. The event is fired at the end of the loading process and allows the developer to read back the custom data they saved previously.

Delegate Signatures

                        
   void ContextMenuHandler(PopupMenu pm, CancelEventArgs cea);
   void ContentHidingHandler(Content c, CancelEventArgs cea);
   void ContentHandler(Content c, EventArgs cea);
   void TabControlCreatedHandler(Magic.Controls.TabControl tabControl);
   void SaveCustomConfigHandler(XmlTextWriter xmlOut);
   void LoadCustomConfigHandler(XmlTextReader xmlIn);