Creating a Dockable Panel-Controlmanager Using C#, Part 1

Creating a Dockable Panel-Controlmanager Using C#, Part 1

Purpose: To make it easy to have panels which can be docked to the left, right, top, and bottom. It should be easy to add this control to the program and easy to understand how it works, so everybody who wants can adapt its features to his needs. The article is addressed to people who have already read and worked with a starter book about C# such as MS Visual C# 2005 Step by Step so maybe ‘C# experts’ will find it too overdone in explaining different parts.

Before you get started, you first have to discuss what you want to get and then, step by step, go down the road to achieve this goal. I did this because I need such a tool for my own programs and looking around I only saw much too simple things, which didn’t fulfil my needs and much too complicated ones, which are a bit overcrowded for my needs and by that too complicated to what I want to get, and too difficult to adapt them to my needs.

The full construction cannot be done in one article, so this will be a serie of articles. It’s adressed to everyone who wants to learn different techniques of programming skills using learning by doing. This first chapter will teach you the following: Attaching three different projects to each other. Creating and using different namespaces, designing an ownerdrawn control, Localisation of usercontrols, including localisation of the controls PropertyBrowser and Errormessages, localiced Discription of Items in the Propertybrowser, Hiding base class Properties in the Propertybrowser, Creating and usage of a TypeConverter, implementing of API calls to a C# program.

But now, start to create all the parts you need to have for an easy docking manager and during learning, you will know what you are doing.

General Design: Decisions

The general design was developed based on the idea to create user-defined panels that are all together connected with a new control named “DockingManager” that is needed to control the docking procedure. All these panels should be User controls, that means inherited from the class Control, by inheriting from the Control class, you have lots of possibilities to use existing other controls like a TabControl or a SplitControl for composing our own ideas. On the other side, for the usage of panels in applications, I would normally prefer to have Forms that I could use to add different controls on it.

But, normal Forms can’t be used in a way that you can dock them wherever you want them to be docked. So, what you will try to achieve is a Form that is dockable, in all the ways you want them, but still useable as a Form. For getting this achieved, you will create a new type of Form that you will name DockableForm. The Class of this Form is derived of a Form.

Here is a picture to what you will achieve after some hours of work.

Figure 1: Your end result

As a first step, you have to create two Projects: one for building all the needed parts of our DockingManager and the other project to test the new elements on their workability. So at first, create an Project named DockingControlTestApp that is a standard windows application and a second project that you open in the same editor together with the Test-application. This is a Windows User control application. This you can nameDockingControls.

Right-click the DockingControlTestApp and select it as the Start project. Use this application now:

At first, you rename Form1 as MDIForm. Now in the Properties of that Form, you set IsMdiContainer to true.

Your Panels should basically have similar features as the IDE of Visual Studio 2005 used in C# has. So basically, there should be two possibilities where you could place a docking control:

  1. Anywhere in the MDI Form
  2. Sharing the same docking position with other panels

The following two figures show both these options:

Figure 2: Anywhere in the MDI Form

Figure 3: Sharing (in this case, the Top Position) Docking with another panel

The screenshot was done just when the mouse was on the Docking Button but not docked, so you can see the green Preview window too. It indicates where the window will be docked.

You also should be able to hide all docked windows and use them only when needed. To get this done, you would need tabs where you could re-open hidden windows again only by hovering over the tabs that contain the Caption of the particular Panel.

Figure 4: Tab registers to show hidden Panels again

It also should be possible to resize the Panels for personal needs and it should be possible to store a chosen combination of Panels and to reload them into the same place at the next program’s start.

What Is not Part of This Project

The Visual studio IDE, and some of the other Docking managers I have seen, allow you to share the space for a window again and again. For example, you are sharing left docking for two windows and want to have a third window there. Three or more windows on one docking position (such as the Left) are only possible when the panels are drawn into the registerbutton. There, you can have as much as you want and the register itself can be docked on different places, as you see in Figure 1.

This was a short description of what we will get in the end.

Designing the Different Parts of the DockingManager

So, where do you begin? You want to have a control that allows you to position your panels and also hide them. So, you will need to have hidden strips where your Tab Buttons are placed to Show panels again, when they had been hidden.

So, do a simple start and construct these strips as parts of your DockingManager. Because you want to use existing controls for this, you will use MenuStrips here.

Figure 5: MDIForm with four empty toolstrips created by DockingManager

In Figure 5, you can see a menustrip, a toolstrip, and a statusstrip, which are part of the Test Application and built at design time, Also, you notice four empty toolstrips (no buttons on them; they are created dynamically by the docking manager at runtime).

This is your first goal to achieve. You need these toolstrips. Each of them should only be shown when you have docked to a side (left, right, top, or bottom) and the docked panel has just been set to hidden. In this case, the panel is visible only when you hover over the button that represents the docked window. If you set the stick (pin) button of the panel to ‘permanent viewable‘ and no other panel is invisible, this toolstrip and the panelbutton are invisible. But that’s later. First, you need to have the docking manager to create these toolstrips.

Keep in mind that there might be existing menus, Toolstrips, and statusstrips already on the screen. To have control over the mechanics of your DockingManager, you now provide your Test application with a menustrip, two Toolstrips, and a statusstrip. The names of them stay as they are: menuStrip1, statusStrip1, toolStrip1, and toolStrip2.

In your DockingControls, you rename the userControl1 to DockingManager.

In this class, which is derived from User Control, you have to add four private fields for the Toolstrips; you will name them Buttonstrips. This should now look like:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;
using System.Diagnostics;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;
using System.Resources;

namespace DockingControls{
   public partial class DockingManager : UserControl {
      private ToolStrip LeftButtonStrip;
      private ToolStrip RightButtonStrip;
      private ToolStrip TopButtonStrip;
      private ToolStrip BottomButtonStrip;

      public DockingManager()  {
         InitializeComponent();
      }
   }
}

Note: I have added some of the necessary ‘using namespace’ declarations. Everybody who wants to follow this project should already know more or less why and when they are needed. I’m adding here all you will need for this control, independent of in which stage of the program you really will need them. For more detailed explanation on namespaces, see MS ‘Help or MSDN.

And now, you can create your ButtonStrips itself. First, create the following function in the Dockingmanager to create the toolstrips:

public void CreateAllElements()
{
   // For testing reasons we set the visibility to true
   // in the DockingManager, it will be false as they are
   // not to be seen when created
   CreateButtonStrip(DockStyle.Left,true  );
   CreateButtonStrip(DockStyle.Right,true );
   CreateButtonStrip(DockStyle.Top,true   );
   CreateButtonStrip(DockStyle.Bottom,true);
   // here other needed elements will be created too
}

Hm… pretty easy. This reduces the problem on how to create a toolstrip and to place it in the correct position. So, you now only need to define this new Method CreateButtonStrip, which is creating one specific toolstrip. The enumeration DockStyle is the known enumeration used to dock controls in C#. This Method is defined like the following:

private void CreateButtonStrip(DockStyle pos, bool visible)
{
   ToolStrip buttonStrip = CreateButtonStrip(pos);
   if (buttonStrip != null)
      {
         buttonStrip.Visible = visible;
      }
}

Sometimes, I need the Method without the possibility to hide them, so I decided to have two different methods with the same name, but different properties I did the original method, CreateButtonStrip(Dockstyle pos) (the following code segment), the easy way; this means that I dropped a control to a form, copied the code, and then adapted it to my needs.

private ToolStrip CreateButtonStrip(DockStyle pos )
{
   ToolStrip buttonStrip = new ToolStrip();
   buttonStrip.AutoSize = false;
   buttonStrip.GripStyle = ToolStripGripStyle.Hidden;
   if (pos == DockStyle.Left || pos == DockStyle.Right)
   {
      // we need a vertical toolstrip
      buttonStrip.LayoutStyle = ToolStripLayoutStyle.
      VerticalStackWithOverflow;
      buttonStrip.TextDirection =
         ToolStripTextDirection.Vertical90;
      buttonStrip.Size = new Size(24, 309);
      buttonStrip.Location = new Point(0, 49);
   }
   else
   {
      // Top or bottom are horizontal Toolstrips
      buttonStrip.LayoutStyle = ToolStripLayoutStyle.
         WHorizontalStackWithOverflow;
      buttonStrip.TextDirection = ToolStripTextDirection.Horizontal;
      buttonStrip.Size = new Size( 309,24);
      buttonStrip.Location = new Point(0, 49);
    }
   buttonStrip.RenderMode = ToolStripRenderMode.ManagerRenderMode;
   // lets sign the controls so we can access them easier
   switch (pos )
   {
      case DockStyle.Left:
         buttonStrip.Text = "Left";
         buttonStrip.Name = "bsLeft";
         break;
      case DockStyle.Right :
         buttonStrip.Text = "Right";
         buttonStrip.Name = "bsRight";
         break;
      case DockStyle.Top :
         buttonStrip.Text = "Top";
         buttonStrip.Name = "bsTop";
         break;
      case DockStyle.Bottom :
         buttonStrip.Text = "Bottom";
         buttonStrip.Name = "bsBottom";
         break;
   }
   if (Parent != null)
   {
      // now we are docking the control;
      buttonStrip.Dock = pos;
      Parent.Controls.Add(buttonStrip);
   }
   return buttonStrip;
}

Now, you will add some buttons to our strips on your testApplication so you will be able to differentiate between the controls added by the user and the toolstrips shown by the DockingManager.

More by Author

Must Read