|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
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.
|
|
|
|
|
|
|
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);
}
|
|
|
|
|
|
|
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);
}
|
|
|
|
|
|
|
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.
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
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();
}
}
|
|
|
|
|
|
|
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.Unicodepublic 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.
|
|
|
|
|
|
|
public ContainerControl Container
The object to which the docking manager instance is attached.
Default: nullpublic 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: nullpublic Control OuterControl
Docking windows will not be allowed to dock outside of the specified
control.
Default: nullpublic bool ZoneMinMax
If more than one Window is docked in the same column/row should
they have a maximize capability.
Default: truepublic 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: falsepublic 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: truepublic 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: -1public Color BackColor
Background drawing color used in the caption bar when the docking window is
not selected.
Default: SystemColors.Controlpublic Color ActiveColor
Background drawing color used in the caption bar when the docking window is
selected.
Default: SystemColors.ActiveCaptionpublic Color ActiveTextColor
Text drawing color used in the caption bar when the docking window is
selected.
Default: SystemColors.ActiveCaptionTextpublic Color InactiveTextColor
Text drawing color used in the caption bar when the docking window is not
selected.
Default: SystemColors.ControlTextpublic Color ResizeBarColor
Background color used to draw the resize bar controls between docking windows.
Default: SystemColors.Controlpublic Font CaptionFont
Used when drawing text in the caption bars.
Default: SystemInformation.MenuFontpublic Font TabControlFont
Used when drawing text in the TabControl that appears when multiple content is
shown inside the same docking window.
Default: SystemInformation.MenuFontpublic 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
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
|
Copyright 2003 Crownwood Consulting Ltd. All Rights Reserved |
|
|
|