Docking Control

Dragging

Our next child control DockingHandle has three tasks to perform. It must first of all ensure that it is sized correctly to reflect the current orientation of the parent DockingControl. One of our dimensions will always be calculated determined for us as we are docked to one of the parent control edges. However, the other dimension should always be fixed to reflect the space needed for drawing and allowing the user to grab it. The routine SizeToOrientation performs this decision.
class DockingHandle : UserControl 
{
	// Class constants
	private const int _fixedLength = 12;
	private const int _hotLength = 20;
	private const int _offset = 3;
	private const int _inset = 3;

	// Instance variables
	private DockingControl _dockingControl = null;

	public DockingHandle(DockingControl dockingControl, DockStyle ds)
	{
		_dockingControl = dockingControl;
		this.Dock = DockingControl.HandleStyleFromControlStyle(ds);
		SizeToOrientation(ds);
	}	

	public void SizeToOrientation(DockStyle ds)
	{
		if ((ds == DockStyle.Top) || (ds == DockStyle.Bottom))
			this.ClientSize = new Size(_fixedLength, 0);
		else
			this.ClientSize = new Size(0, _fixedLength);
	}

The second task and the most interesting is performed inside OnMouseMove. Here we need to convert the mouse position from our own client position to the client position in the host form. By testing how near the cursor is to each edge of the form we can decide which edge should become the new docking position of the parent DockingControl. At the moment the code uses a constant value of _hotLength to decide if the mouse is close enough to an edge for the docking edge to be changed. Actually causing the docking to change is trivial, just change the Dock property on the DockingControl.

	protected override void OnMouseMove(MouseEventArgs e)
	{
		// Can only move the DockingControl is we have captured the
		// mouse otherwise the mouse is not currently pressed
		if (this.Capture)
		{
			// Must have reference to parent object
			if (null != _dockingControl)
			{
				this.Cursor = Cursors.Hand;

				// Convert from client point of DockingHandle to client of DockingControl
				Point screenPoint = PointToScreen(new Point(e.X, e.Y));
				Point parentPoint =  _dockingControl.HostForm.PointToClient(screenPoint);

				// Find the client rectangle of the form
				Size parentSize = _dockingControl.HostForm.ClientSize;

				// New docking position is defaulted to current style
				DockStyle ds = _dockingControl.Dock;

				// Find new docking position
				if (parentPoint.X < _hotLength)
				{
					ds = DockStyle.Left;
				}
				else if (parentPoint.Y < _hotLength)
				{
					ds = DockStyle.Top;
				}
				else if (parentPoint.X >= (parentSize.Width - _hotLength))
				{
					ds = DockStyle.Right;
				}
				else if (parentPoint.Y >= (parentSize.Height - _hotLength))
				{
					ds = DockStyle.Bottom;
				}

				// Update docking position of DockingControl we are part of
				if (_dockingControl.Dock != ds)
					_dockingControl.Dock = ds;
			}
		}
		else
			this.Cursor = Cursors.Default;

		// Ensure delegates are called
		base.OnMouseMove(e);
	}

Lastly the control needs to draw the two lines that decorate the control area.

	protected override void OnPaint(PaintEventArgs pe)
	{
		Size sizeClient = this.ClientSize;
		Point[] ptLight = new Point[4];
		Point[] ptDark = new Point[4];
			
		// Depends on orientation
		if ((_dockingControl.Dock == DockStyle.Top) || 
			(_dockingControl.Dock == DockStyle.Bottom))
		{
			int iBottom = sizeClient.Height - _inset - 1;
			int iRight = _offset + 2;

			ptLight[3].X = ptLight[2].X = ptLight[0].X = _offset;
			ptLight[2].Y = ptLight[1].Y = ptLight[0].Y = _inset;
			ptLight[1].X = _offset + 1;
			ptLight[3].Y = iBottom;
		
			ptDark[2].X = ptDark[1].X = ptDark[0].X = iRight;
			ptDark[3].Y = ptDark[2].Y = ptDark[1].Y = iBottom;
			ptDark[0].Y = _inset;
			ptDark[3].X = iRight - 1;
		}
		else
		{
			int iBottom = _offset + 2;
			int iRight = sizeClient.Width - _inset - 1;
			
			ptLight[3].X = ptLight[2].X = ptLight[0].X = _inset;
			ptLight[1].Y = ptLight[2].Y = ptLight[0].Y = _offset;
			ptLight[1].X = iRight;
			ptLight[3].Y = _offset + 1;
		
			ptDark[2].X = ptDark[1].X = ptDark[0].X = iRight;
			ptDark[3].Y = ptDark[2].Y = ptDark[1].Y = iBottom;
			ptDark[0].Y = _offset;
			ptDark[3].X = _inset;
		}
	
		Pen lightPen = DockingControl.LightPen;
		Pen darkPen = DockingControl.DarkPen;

	 	pe.Graphics.DrawLine(lightPen, ptLight[0], ptLight[1]);
	 	pe.Graphics.DrawLine(lightPen, ptLight[2], ptLight[3]);
	 	pe.Graphics.DrawLine(darkPen, ptDark[0], ptDark[1]);
	 	pe.Graphics.DrawLine(darkPen, ptDark[2], ptDark[3]);

		// Shift coordinates to draw section grab bar
		if ((_dockingControl.Dock == DockStyle.Top) || 
			(_dockingControl.Dock == DockStyle.Bottom))
		{
			for(int i=0; i<4; i++)
			{
				ptLight[i].X += 4;
				ptDark[i].X += 4;
			}
		}
		else
		{
			for(int i=0; i<4; i++)
			{
				ptLight[i].Y += 4;
				ptDark[i].Y += 4;
			}
		}

	 	pe.Graphics.DrawLine(lightPen, ptLight[0], ptLight[1]);
	 	pe.Graphics.DrawLine(lightPen, ptLight[2], ptLight[3]);
	 	pe.Graphics.DrawLine(darkPen, ptDark[0], ptDark[1]);
	 	pe.Graphics.DrawLine(darkPen, ptDark[2], ptDark[3]);

		// Ensure delegates are called
		base.OnPaint(pe);
	}
}

You might also like...

Comments

Contribute

Why not write for us? Or you could submit an event or a user group in your area. Alternatively just tell us what you think!

Our tools

We've got automatic conversion tools to convert C# to VB.NET, VB.NET to C#. Also you can compress javascript and compress css and generate sql connection strings.

“In order to understand recursion, one must first understand recursion.”