Library tutorials & articles

Collection Controls with Rich Design Time Support

Starting Off

I won't put all the code in to this article, because it would just get cluttered. Instead I will paste the important bits, and attempt to describe the rest. I will be developing the control in both VB and C# as I write, and the resultant solution will be available for download at the end.

First things first, we add the new usercontrol to our project. As we don't want our drawing to flicker, we use the protected SetStyle function in the constructor to turn on the DoubleBuffer and AllPaintingInWmPaint styles. These two go hand in hand. We also define the CalculateLayout function, which we will be calling from the collection and when the control is resized.

Next comes defining the subitem class, and the strongly-typed collection class which we'll use to contain the buttons. At this point we add the Buttons property to the main control, which exposes a private instance of this collection, instantiated in the main control's constructor. The ColourButton has an internal Bounds member of type Rectangle, which will hold the position of the button in the control.

For simplicity, our collection will only implement the Add and Remove functions, and the indexer. Normally you would add a few more strongly-typed helper functions to it, such as IndexOf. The constructor of the collection is internal and takes an instance of the main control as a parameter. This is so that this instance can be passed on to buttons as they are added, because when the user changes the colour of a button it needs to signal that a redraw is needed. Here is the code for the ColourButton and ColourButtonCollection classes:

VB.NET

Public Class ColourButton
    Inherits Component
    Private _Colour As Color = Color.White
    Friend Control As CollectionControl = Nothing
    Friend Bounds As Rectangle
    <DefaultValue(GetType(Color), "White")> _
    Public Property Colour() As Color
        Get
            Return _Colour
        End Get
        Set(ByVal Value As Color)
            _Colour = Value
            If Not Control Is Nothing Then Control.Invalidate()
        End Set
    End Property
End Class
Public Class ColourButtonCollection
    Inherits CollectionBase
    Private Control As CollectionControl
    Friend Sub New(ByVal Control As CollectionControl)
        Me.Control = Control
    End Sub
    Default Public ReadOnly Property Item(ByVal Index As Integer) As ColourButton
        Get
            Return DirectCast(List(Index), ColourButton)
        End Get
    End Property
    Public Function Contains(ByVal Button As ColourButton) As Boolean
        Return List.Contains(Button)
    End Function
    Public Function Add(ByVal Button As ColourButton) As Integer
        Dim i As Integer
        i = List.Add(Button)
        Button.Control = Control
        Control.CalculateLayout()
        Return i
    End Function
    Public Sub Remove(ByVal Button As ColourButton)
        List.Remove(Button)
        Button.Control = Nothing
        Control.CalculateLayout()
    End Sub
End Class

C#

public class ColourButton : Component
{
    private Color _Colour = Color.White;
    internal CollectionControl Control = null;
    internal Rectangle Bounds;
    public Color Colour
    {
        get
        {
            return _Colour;
        }
        set
        {
            _Colour = value;
            if (Control != null)
                Control.Invalidate();
        }
    }
}
public class ColourButtonCollection : CollectionBase
{
    private CollectionControl Control;
    internal ColourButtonCollection(CollectionControl Control)
    {
        this.Control = Control;
    }
    public ColourButton this[int Index]
    {
        get
        {
            return (ColourButton) List[Index];
        }
    }
    public bool Contains(ColourButton Button)
    {
        return List.Contains(Button);
    }
    public int Add(ColourButton Button)
    {
        int i;
        i = List.Add(Button);
        Button.Control = Control;
        Control.CalculateLayout();
        return i;
    }
    public void Remove(ColourButton Button)
    {
        List.Remove(Button);
        Button.Control = null;
        Control.CalculateLayout();
    }
}

Comments

  1. 30 Jan 2009 at 17:58
    Nice article, I'm writing a toolstrip type control and this helped me figure out how to remove the child component on deletion. I liked how you selected components in the control on mouse down, however, I had done it differently. Because my control was written in the compact framework, with the designer code being in the full framework. I couldn't select my component in the control code itself by checking for design mode. Therefore, I handled wndproc: protected override void WndProc(ref System.Windows.Forms.Message m) { // left mouse down if (m.Msg == WM_LBUTTONDOWN) { int data = m.LParam.ToInt32(); int y = (int)(data & 0xFFFF0000) >> 16; int x = (int)(data & 0x0000FFFF); Point pt = new Point(x, y); OptionStrip strip = (Control as OptionStrip); clickItem = strip.GetItemAt(pt); if (clickItem != null) return; } else if (m.Msg == WM_LBUTTONUP && clickItem != null) { int data = m.LParam.ToInt32(); int y = (int)(data & 0xFFFF0000) >> 16; int x = (int)(data & 0x0000FFFF); Point pt = new Point(x, y); OptionStrip strip = (Control as OptionStrip); if (strip.GetItemAt(pt).Equals(clickItem)) { ISelectionService ss = (ISelectionService)GetService(typeof(ISelectionService)); ArrayList list = new ArrayList(); list.Add(clickItem); ss.SetSelectedComponents(list, SelectionTypes.Primary); } clickItem = null; return; } I think your solution is simpler, and probably more solid, but this is necessary as the ISelectionService does not exist in the compact framework. However, it does seperate design time code.
  2. 23 May 2007 at 19:35

    GREAT ARTICLE¡¡¡  thank you very, very much, now i can finish my own control. 

    I could not save my own custom class object, NOW I CAN.

  3. 19 Mar 2007 at 09:08
    I've tried to compile the code I've downloaded but at run-time (placed in a form on another project) I don't see the buttons I've created at deign-time..
  4. 17 Jan 2007 at 19:46
    Is it safe to assume value (of complex type) of a property has instance descriptor converter for every component/control you dropped on design surface.

    Regards
    Phani




  5. 01 Jan 1999 at 00:00

    This thread is for discussions of Collection Controls with Rich Design Time Support.

Leave a comment

Sign in or Join us (it's free).

Tim Dawson
AddThis

Related discussion

Related podcasts

  • More jQuery in ASP.NET

    In this episode Chris Brandsma, Rick Strahl, Dave Ward, Bertrand Le Roy, and Scott Koon conclude their discussion of Microsoft's jQuery in ASP.NET announcement1.This episode of the Alt.NET Podcast is brought to you by LLBLGen Pro, the most mature O/R mapper and code generator out there.Are ...

Events coming up

  • Nov 18

    15 Minutes of Fame

    Dresher, United States

    This is a yearly tradition. We select 10 of the favorite speakers from monthly meetings, code camps, and hands on labs. Each one does a 15 minute talk on their favorite .NET technology. This is our 10th anniversary so we plan a gala event with special prizes and refreshments.

Want to stay in touch with what's going on? Follow us on twitter!