Docking Controls - Standard and Custom

Introduction

Visual Studio 2005 includes the Toolstrip container and Toolstrip controls; used together, these controls allow the end user to drag and drop toolstrips to the edges of the screen such that at run time, the user can reposition the toolbars to the top, bottom, left, or right edges of the work area.

Image1.jpg

Figure 1:  Dropping Toolstrips into side and top panels of the Toolstrip Container.
 

Using the control is simple enough; just drop the toolstrip container control into a panel or form; doing so will place the control on the panel or form and expose the toolstrip container tasks (Figure 2):

Image2.jpg

Figure 2:  Dropping the Toolstrip Container into a Form.
 

By default, the container will not dock to the form or panel when it is first added, but the option to dock the toolstrip container is available in the toolstrip container tasks. The developer is also able to limit the locations where the end user is permitted to drop the toolbars by either checking or un-checking each of the checkboxes (top, bottom, left, or right) displayed in the toolstrip container tasks. If the developer wanted to limit the toolbar to display on the top or bottom of the panel only, that may be accomplished but removing the checkmarks in the left and right panel visibility options.

The area inside the toolstrip container is itself a container; to that end, additional controls may be added to it in lieu of the underlying panel or form hosting the toolstrip container. Figure 3 (below) shows an example of a set of controls added to the panel. Naturally one could also just as easily add controls programmatically to this container at runtime.

Image3.jpg

Figure 3:  Adding Controls to the Toolstrip Container panel at Design Time.
 

The toolstrips that may be added to the toolstrip containers allow the user to add a variety of controls to the toolstrip; the available options are limited to the following control types:

  • Button
  • Label
  • Split Button
  • Dropdown Button
  • Separator
  • Combo Box
  • Textbox
  • Progress Bar

Image4.jpg

Figure 4:  Adding Controls to the Toolstrip at Design Time.
 

If the developer needs to add controls other than those shown in Figure 4 (such as a date time picker, checkboxes, radio buttons, etc.) a different approach will have to be adopted. There are certainly a number of approaches that one can adopt to build a dockable container that may be subsequently used as a tool palette. While building your own dockable container does offer you the opportunity to construct a palette that may contain controls other than those available in the standard toolstrip's optional controls; in most cases, the standard toolstrip control options are more than adequate. Figure 5 shows a notional custom dockable palette with a calendar control added and figure 6 shows a custom dockable palette with a date time picker control added; both items that can't be immediately added to the standard toolstrip control.

Image5.jpg

Figure 5:  Custom Dockable Palette with a Calendar Control.

Image6.jpg

Figure 6:  Custom Dockable Palette with a Date Time Picker Control.
 

Again, in most cases you can likely get along fine with a standard toolstrip control and you can provide the ability to dock the control through the use of the toolstrip container control. However, if you need to build a custom dockable palette of some sort, the rest of this article will address one approach to building just such a control. 

Getting Started

The solution contains a single project for a C# windows desktop application. The project is called "DockToolstrip" and the project contains two forms. The first form provides an example of a standard toolstrip container and a couple of toolstrips in use; the second form is used to show one approach to creating a custom dockable toolbar that may contain any other type of control.

Image7.jpg

Figure 7:  Solution Explorer with Project Visible.
 

Code:  Form 1

There really isn't any code necessary to drive the effects required to drag and drop the toolstrips onto different areas of the toolstrip container. In order to implement the controls, one only need add the toolstrip container control to the target panel or form, dock into that container and then add one or more toolstrips to the toolstrip container. Adding the controls to the toolstrips and writing the event handlers associated with those controls involves the same processes used to perform those tasks for a stationary toolstrip.

Form 1 contains a split panel; on the left hand side are a few buttons included just for eye wash, on the right side panel, a toolstrip container was added and docked to the right hand panel. Two separate toolstrips were added and a few controls were added and given icons and simple event handlers. A picture box control was docked full into the toolstrip menu container and a picture of was centered into the picture box.

At run time, the user may drag the toolstrips to the top, bottom, left, or right sides of the container and release them. Doing so will move the toolstrip to a docked position in the vicinity of the release point. Again, no code was added to make any of that work; the functionality is derived entirely from the controls. 

Code:  Form 2

This form is a little more interesting to look at. The form contains a flow layout panel control which serves as the dockable palette; a standard panel is also contained in the form; this panel is used to contain whatever controls might be added to the form; this panel and its control collection will have to be moved in response to the movement of the flow layout panel control. 

That is, if the flow layout panel gets docked to the top of the container, the standard panel will have to be docked beneath that panel, or if the flow layout panel gets docked to the left hand side of the container, the standard panel will have to fill the region to the right of the flow layout panel. This is easily accomplished by using the dock properties of both controls along with some code to handle a drag and drop. 

The allow drop property on the container has to be set to true to support the drag and drop actions; by default, this property is set to false.

Image8.jpg

Figure 8:  Setting the Form2 Allow Drop Property to True.
 

The code behind this form begins with the standard imports and default constructor:

using System;

using System.Collections.Generic;

using System.ComponentModel;

using System.Data;

using System.Drawing;

using System.Text;

using System.Windows.Forms; 

 

namespace DockToolstrip

{ 

    public partial class Form2 : Form

    { 

        // default constructor

        public Form2()

        {

            InitializeComponent();

        } 

The next bit of code is necessary to support the drag and drop functionality; this code is an event handler for the mouse down event for the flow layout panel control. This code registers the beginning of a drag event whenever the user holds down the mouse button on the flow layout panel control: 

// set up DoDragDrop on panel1 mousedown

private void panel1_MouseDown(object sender, MouseEventArgs e)

{

    this.DoDragDrop(panel1, DragDropEffects.All);

} 

Following this code is the drag drop event handler for Form 2.  When the user drops the dragged object onto the form, this section of code will execute.  The code annotated but the gist of it is that, on drop, the drop point is captured and used to populate a point object that defines that drop location.  Once the point is captured, the flow layout panel and the standard panel are undocked; following that, the drop point is evaluated to determine is proximity to each edge in the container.  If the point is within 100 pixels of an edge, the dock property of the flow layout panel is set to dock to that nearest edge (top, left, bottom, or right).  Once the flow layout panel used as a tool palette is docked to the appropriate edge, the content panel with the form controls is docked to fill the panel.  This will cause the content panel to expand into the remaining area not used by the flow layout panel serving as the tool palette.

The panel itself is also resized as a function of this process, if the panel is docked to the top or bottom of the container, its height property is limited to the height of the tallest control plus a few (10) extra pixels to buffer the controls from the edges.  Alternatively, one could use padding to keep the controls away from the edges.  If the flow layout control is docked to the sides of the container, the width of the widest control should be used to determine the width of the panel.

Lastly, if the panel is not dropped within 100 pixels of an edge, the panel is resized to contain the controls and dropped at the drop location without docking it to an edge. 

private void Form2_DragDrop(object sender, DragEventArgs e)

{

    // get the drop position

    int dropX = e.X;

    int dropY = e.Y;

 

    // create point at drop location

    Point dropLocation = new Point(dropX, dropY);

    Point dropPoint = new Point();

 

    // convert point to client area position

    dropPoint = this.PointToClient(dropLocation);

 

    // prep for the drop

    panel1.Location = dropPoint;

    panel1.Dock = DockStyle.None;

    panel2.Dock = DockStyle.None;

 

    // test the position of the drop to determine

    // whether or not to dock the panel to the top;

    // reset the height

    if (dropPoint.Y < 100)

    {

        panel1.Dock = DockStyle.Top;

        panel1.Height = button1.Height + 10;

        panel2.Dock = DockStyle.Fill;

        return;

    } 

    // test the position of the drop to determine

    // whether or not to dock the panel to the bottom;

    // reset the height

    if (dropPoint.Y > this.Height - 100)

    {

        panel1.Dock = DockStyle.Bottom;

        panel1.Height = button1.Height + 10;

        panel2.Dock = DockStyle.Fill;

        return;

    } 

    // test the position of the drop to determine

    // whether or not to dock the panel to the left side;

    // reset the width of the panel

    if (dropPoint.X < 100)

    {

        panel1.Dock = DockStyle.Left;

        panel1.Width = button1.Width + 10;

        panel2.Dock = DockStyle.Fill;

        return;

    } 

    // test the position of the drop to determine

    // whether or not to dock the panel to the right side;

    // reset the width of the panel

    if (dropPoint.X > this.Width - 100)

    {

        panel1.Dock = DockStyle.Right;

        panel1.Width = button1.Width + 10;

        panel2.Dock = DockStyle.Fill;

        return;

    }

    // if the panel is not near an edge, resize it

    // and drop it at the drop location without

    // specific docking

    panel1.Dock = DockStyle.None;

    panel2.Dock = DockStyle.None;

    panel1.Width = button1.Width + 10;

    panel1.Height = (button1.Height * 6) + 50;

}

After handling the drop event, the next bit of could is also included to support the drag and drop functionality. 

/// <summary>

/// Handle drag enter event when moving dockable

/// control around

/// </summary>

/// <param name="sender"></param>

/// <param name="e"></param>

private void Form2_DragEnter(object sender, DragEventArgs e)

{

    if (e.Data.GetDataPresent(DataFormats.Text))

    {

        e.Effect = DragDropEffects.Copy;

    }

    else

    {

        e.Effect = DragDropEffects.Move;

    }

} 

The rest of the code contained in this demo form is really just there to provide a little eye wash; for example the next code up handles the paint events for the two panels by drawing a gradient in each panel whenever it is moved (and the paint event is fired).

// paint gradients in the panels - just eye wash

private void panel1_Paint(object sender, PaintEventArgs e)

{

    System.Drawing.Drawing2D.LinearGradientBrush gradBrush;

 

    Point start = new Point(0, 0);

    Point end = new Point(panel1.Width, panel1.Height);

 

    gradBrush = new

    System.Drawing.Drawing2D.LinearGradientBrush(start, end, Color.Black, Color.LightSteelBlue);

 

    Graphics g = panel1.CreateGraphics();

    g.FillRectangle(gradBrush, new Rectangle(0, 0, panel1.Width, panel1.Height));

}

 

private void panel2_Paint(object sender, PaintEventArgs e)

{

    System.Drawing.Drawing2D.LinearGradientBrush gradBrush;

 

    Point start = new Point(0, 0);

    Point end = new Point(panel2.Width, panel2.Height);

 

    gradBrush = new System.Drawing.Drawing2D.LinearGradientBrush(start,

    end, Color.Black, Color.LightSlateGray);

 

    Graphics g = panel2.CreateGraphics();

    g.FillRectangle(gradBrush, new Rectangle(0, 0, panel2.Width, panel2.Height));

}

The last bit of code on any consequence in this demo is a click event handler for the flow layout panel used as a custom palette. This click event handler is set to return without doing anything in response to a click event registered on the flow layout control panel. The purpose of this is to prevent the flow layout panel from resizing in response to being clicked without the intent of dragging.

private void panel1_Click(object sender, EventArgs e)

{

    return;

} 

The rest of the code contained in this form related partial class is just there to handle click events from the controls in the dockable palette. The code described previously is all that is needed to support dragging and dropping the custom palette. 

Summary.

This article was intended to demonstrate the use of the toolstrip container and toolstrips with regards to dragging and dropping toolstrips and docking those toolstrips to the edges of the toolstrip container control control. Alternatively, the second form contained in the application is used to demonstrate an approach to building a dockable custom tool palette. The approach given was very simple to implement; there are a variety of other things that could be done to make for a more versatile dockable palette; for example, the control can be made to show scroll bars and allow scrolling.