To solve this problem we need to create a new composite control DockingControl
that is able to take a caller supplied control and manage its position and sizing.
Our composite control will need a resizing bar; a grab handle area that can be
used to move its docking position and a place for the caller supplied control
to be displayed.
class DockingControl : UserControl
{
private Form _form;
private DockingResize _resize;
private DockingHandle _handle;
private BorderControl _wrapper;
public DockingControl(Form form, DockStyle ds, Control userControl)
{
// Remember the form we are hosted
on
_form = form;
// Create the resizing bar, gripper
handle and border control
_resize = new DockingResize(ds);
_handle = new DockingHandle(this,
ds);
_wrapper = new BorderControl(userControl);
// Wrapper should always fill remaining
area
_wrapper.Dock = DockStyle.Fill;
// Define our own initial docking
position for when we are added to host form
this.Dock = ds;
Controls.AddRange(new Control[]{_wrapper,
_handle, _resize});
}
public Form HostForm { get { return _form; }
}
The final line of code in the instance constructor adds the three child controls _wrapper
, _handle
and _resize
. The order of the controls in the initializer list is absolutely crucial because when the DockingControl
(or any other Control
) has its Dock
style changed this ordering determines the position and size of the child controls. Calculations are made starting with the last control added (which equates to last entry in the initializer list) back towards the first, which is the exact opposite of what I would have expected.
The _resize
bar is first to be positioned (and so last in initializer list) as it should always be shown spanning the entire length of the docking control. Next is the _handle
as it should be positioned under the sizing bar and finally the _wrapper
control, this is last because it always has a Dock
style of Fill
and we want it to take up whatever space is leftover when all the other controls have finished being laid out.
Comments