KB_Soft Group - Software Vendor's Development Partner
Start page
Site map



When developing control elements for .NET our company KBSoft faced a task of providing the design of complex compound control elements in the designtime. Our goal was to provide convenience of designing of a control element, containing plentiful compound parts (signatures, diagrams, images etc), which will be displayed on its surface. And for the user�s comfort these elements should be chosen by a mouse in the designtime. So convenience of such a compound control element adjustment should be the same as of adjustment of control elements on a form, when a user can choose different control elements, move them and resize with the help of a mouse, adjust features of the chosen control element with the help of VisualStudio property grid.

A standard decision comes first to mind for such a problem statement � implement all parts of a custom control element with the help of other controls. But this approach has a lot of difficulties. It is well-known how difficult sometimes it is to get a demanded behavior from a window. For example, if it is needed to make a text mark, which would have oval borders, on a control element, or to obtain non-transparency text display on a semi-transparent background, there will occur difficulties, which we mentioned in the article Non-transparent controls on a semi-transparent window. Besides, each window takes certain resources, receives messages etc, which is often not needed at all.

If you only need to draw on the surface of your control element with the help of GDI+ or OpenGL and forget about additional windows, placed on the control, but at the same time you want to be free in adjusting your control in the designtime, then it is necessary to use VisualStudio opportunities allowing to add certain logic of visual design. Designers and component model for control elements .NET with the base class Component are used for that. The only disadvantage of this approach is a high complexity of implementation of a good design support in the designtime.


This article contains a ready designer SmartControlDesigner, which we created in KBSoft for internal needs. This designer is able to support adjustment of position of some rectangular areas on the surface of your control element. For such areas the frame is drawn in the style of a standard, drawn when you choose a control element on a form in WindowForms application. This frame allows to adjust the position and area sizes. Such an area can be chosen by a mouse and its properties will be displayed in a property grid. Thus, a user, adjusting your control, has an impression that he is working with usual control elements on a form. In fact, this area will never be a window and can be a simple object in the memory, drawn by several GDI-functions. On the image below you will see an appearance of an area designed in IDE VisualStudio.

SmartControlDesigner is totally ready to use, and having implemented several interfaces in your element, you receive a perfectly adjustable in the designtime control element. An example of the current designer implementation can be a base for developing of a more flexible and comfortable system of component adjustment in the designtime. SmartControlDesigner source code contains detailed comments and explanations for that.




To manage control element behavior in the designtime there is a class ControlDesigner in FCL. It contacts the control element class with the help of an attribute DesignerAttribute in the following way:


    public class UserControl1
     Object-Designer (in this case SmartControlDesigner) is created in the moment of opening a form, where a design control UserControl1 is placed. And the control element, to which the designer is tied, will be accessible through the property of this designer. The property ControlDesigner.Control is intended for that purpose.

For the management of element�s composition in the designtime it is necessary to have a set of commands (add element, delete element, adjust element properties etc). The designer allows adding a set of commands, displayed in the designtime for a particular control element. For that, Verbs property should be redefined in the designer. The example is shown below.

public override DesignerVerbCollection 
        return this.verbs;
Each command is defined by its name and function-handler, which will be activated in response of a click on that command. If to fill a returned by the current property collection DesignerVerbCollection with the help of the function verbs.Add(DesignerVerb value) , then the command list of the designtime will look in the following way:

Various facilities of the designer can be used with the help of several interfaces. IDesignerHost refers to them. It allows to control components, designed in the designtime. Interface ISelectionService allowing to react at the user�s change of a chosen component in the designtime. And interface IComponentChangeService, presenting events on deleting or changing of one of the components. References to these interfaces are usually initialized in an overridden function Initialize of Control Designer. This function is called automatically after the designer creation for initialization implementation. GetService(Type serviceType) function, to which the interface type is passed as a parameter is used to get a reference to the interface. Below you will see a fragment of Initialize function from SmartControlDesigner, demonstrating the initialization of necessary interfaces and subscription for mouse events from a control element.
// Get interface IselectionService and save it into the field.
this.selectionService = this.GetService (typeof(ISelectionService)) 
    as ISelectionService;

// Get interface IComponentChangeService and save it into the field.
this.componentChangeService = this.GetService (typeof
    (IComponentChangeService)) as IComponentChangeService;

// Get interface IDesignerHost and save it into the field.
this.designerHost = this.GetService (typeof(IDesignerHost))as IDesignerHost;

if (this.selectionService != null)
    // Subscription to the event of the chosen element change.
    this.selectionService.SelectionChanged += 
    new EventHandler(SelectionServiceSelectionChanged);

if (this.componentChangeService != null)
    // Subscription to the event of component deletion.
    this.componentChangeService.ComponentRemoving += new
        ComponentEventHandler (ComponentChangeServiceComponentRemoving);

    // Subscription to the event of change of any property of the component.
    this.componentChangeService.ComponentChanged +=
        new ComponentChangedEventHandler (componentChangeService_ComponentChanged);

// Subscription to the mouse events from the control element.
this.Control.MouseDown += new MouseEventHandler (Control_MouseDown);
this.Control.MouseUp += new MouseEventHandler(Control_MouseUp);
this.Control.MouseMove += new MouseEventHandler(Control_MouseMove);
    In order to control some compound part of a control element in the designtime with the help of a designer, it is necessary that it would represent itself a class, derived from System.ComponentModel.Component. Then it will be possible to add this component into a host (design area IDE VisualStudio, after which edition of its properties with the help of PropertiGrid will be accessible) with the help of a reference to IDesignerHost received earlier in the following way:

designerHost.CreateComponent(Type type);
, where an object of the Type type, representing itself a reference type, derived from Component will be passed as funcion parameter. After adding a component it is displayed in the lower design part of Visual Studio. Its name is formed on the basis of the component type name.

After receipt of the reference to the component with the help of CreateComponent function this reference is usually passed to the control element, because it �knows� how the created element should be drawn.

After the element creation the user should have a chance to somehow choose the created component for its properites edition. Component adjustment in the designtime presupposes a support of two choice mechanisms.


Firstly, a user can click on the component icon, displayed in the lower part of the design area (shown on a screenshot above). The component�s properties will be displayed in the property grid. In this case, the component (diagram, image, label etc) drawn in the area should be marked as a chosen (for example be in a frame). In order to receive a notification that the user has chosen a component, there is an event SelectionChanged

Secondly, the user should have an opportunity to click a mouse on the area of a component, drawn on the control element and choose it. For that it is necessary to make two actions:

  1. Override the designer function GetHitTest(Point point). This function receives a point, where a click of a mouse was fulfilled. If the function returns true, then it means that the event will be transmitted to the widow of the control element (if the designer is subscribed to the messages from control, as it was shown before, it will be able to process them). If the function returns false, then the click will be processed with VisualStudio environment (which usually leads to the choice of the control element as a chosen component).
  2. In the function of processing a mouse click in the designer, determine a component, on which the click was fulfilled and make this component chosen In IDE VisualStudio environment. The function SetSelectedComponents serves for that purpose.

Description of designer use

  1. Implement the interface IRegionContainer in your designer. This interface allows getting a collection of components, which this control element contains. With the help of this collection the designer gets to know what components are needed to be managed in the the designtime.
  2. Implement the interface IRegion in the components which will be present in your control element. This interface determines a rectangle area of the component.
  3. Associate the designer SmartControlDesigner with the control element with the help of DesignerAttribute attribute.

The interface IRegion contains Bounds property (determines a rectangle area, occupied by the component). And ZOrder (determines an order in which this larea will be drawn, that is its remoteness from the observer).


The interface IRegionContainer contains two properties - Regions and VerbNames and two methods - RegionAdded and RegionRemoved. Regions property should return the collection of components (represented by the references to IRegion) which should be controlled by the designer. The designer will automatically draw the frame around the IRegion area; will provide a support of area sizes changes with the help of a mouse and choice of the component in the designtime when clicking in its area. The IRegion collection of elements returned by this property should also be sorted in correspondence with ZOrder values. The designer will search for an appropriate area from the end to the start of the collection. If you know this fact, you can change the designer behavior when choosing overlapping (placed on one another) areas. For that it is only needed to change the sorting logic.

That is how this feature is implemented in demo-control, which you can find in demo project.
public RegionList Regions
{ get { //Create a backup collection, containing the resultant regions System.Collections.Generic.List<IRegion> regions = new System.Collections.Generic.List<IRegion>(); //Receive an array of elements ImageItem and copy these elements to regions. ImageItem[] images = new ImageItem[imagesList.Count]; this.imagesList.CopyTo(images); regions.AddRange(images); //Receive an array of elements RectItem and copy these elements to regions. RectItem[] rects = new RectItem[rectList.Count]; this.rectList.CopyTo(rects); regions.AddRange(rects); //Sort regions on ZOrder discrease. regions.Sort(delegate(IRegion r1, IRegion r2) { if (r1.ZOrder == r2.ZOrder) return 0; if (r1.ZOrder < r2.ZOrder) return 1; else return -1; } ); return regions; } set { } }
There are two separate collections of components, displayed on the control element � imageList and rectList. The elements of these collections are united into one collection of IRegion links. The components of these two collections can be drawn in the control in any way � it won�t affect their behavior in the designtime.

VerbNames property should return the collection of pairs ( command_name ,  type_of_element ). These commands will be automatically added to the designer with the help of Verbs property. When clicking the command �command_name� a component of the type �type_of_element_created� will be created. After that, the RegionAdded function will be activated, to which the created element will be passed as a parameter. With the help of this function the control gets to know that a new element was added during the process of designing. For example, this function is implemented in the following way in the demo-application:

public void RegionAdded(IRegion region)
    ImageItem ri = region as ImageItem;
    RectItem rect = region as RectItem;
    if (ri != null)

    if (rect != null)
This function determines which component was created in the designer and adds it to a corresponding to it collection.

The remaining function RegionRemoved is called when there happens a deletion of a component in the designtime (the user chose a component and clicked Delete).

In the demo-project this function is defined in the following way:

public void RegionRemoved(IRegion region)
    ImageItem ri = region as ImageItem;
    RectItem rect = region as RectItem;

    if (ri != null)
        if (rect != null)

Here a reverse procedure is produced � a type of the deleted element is defined and it is deleted from the corresponding collection.

Thus, you can create quite a complicated structure of areas, displayed on the surface of your control element. With the help of Regions feature you can control the logic of their placement (what control elements are drawn over the rest elements).

In conclusion, it is necessary to note, that processing of the whole control element deletion should be processed in a special way. If you delete it from the form, its function Dispose will be activated. It is absolutely necessary to activate Dispose functions in all the rest components, added by the designer; otherwise they will remain in the design area, though they won�t have a control on which they could be displayed. Dispose activation for the class, derived from System.ComponentModel.Component leads to its deletion from IDE VisualStudio 2005.