Creating controls/components from Toolbox and Initializing

I got this question today and wanted to write on it:

How does creation of controls from toolbox work? A related question is - how do we initialize the control’s properties when it gets created? A more advanced scenario is – how can we create multiple controls/components when double-clicking just a single item in the toolbox (e.g. in a Windows Forms Application double-clicking say the a DataGridView in the ‘Data Sources’ window also creates the DataSet, DataConnector and DataAdapter components along with DataGridView control on the Form. How does that work? The magic lies in interfaces IToolboxUser and ToolboxItem. IToolboxUser is an interface that is implemented by any designer that supports adding controls from the toolbox. DocumentDesigner and ComponentDocumentDesigner implement the IToolboxUser interface. When we double-click an item (control/component) in the toolbox IToolboxUser.ToolPicked() method gets called. If we drag-drop the item then the drag drop handler gets called. In either case when the control/component is about to be created, the flow of execution is as follows:

·          ToolboxItem.CreateComponents() Note: in most cases the toolbox item creates just one component. E.g. double-clicking button in the toolbox only creates a single control Button, but in the advanced scenario mentioned above we can potentially create multiple components and parent them to the DesignerHost’s RootComponent. So, if you create a custom ToolboxItem and associate it with a control (through a ToolboxItem attribute) then you could create multiple components

·          Toolbox item then gets the designer for each of the components it created and checks if they implement IComponentInitialize. If they do then IComponentInitialize.InitializeNewComponent is called. Note: This method can be overridden to initialize the properties on the component being created

As an example consider this code below that creates control on the RootComponent (in this case a Form) by using the IToolboxUser interface of the designer of the RootComponent (FormDocumentDesigner).

DesignSurface ds = new DesignSurface();

ds.BeginLoad(typeof(Form)); // loads the Form

IDesignerHost idh = ds.GetService(typeof(IDesignerHost)) as IDesignerHost;

IToolboxUser tbu = idh.GetDesigner (idh.RootComponent as IComponent) as IToolboxUser;

tbu.ToolPicked(new ToolboxItem(typeof(Button));

// Show the Form

Control view = ds.View as Control;

Form f = new Form();

f.Controls.Add(view);

view.Dock = DockStyle.Fill;

f.Show();

We could instead use a custom toolbox item that would create multiple controls. Below is a snippet of this custom toolbox item that overrides CreateComponentsCore to create 2 controls: TextBox control and MyControl.

protected override IComponent[] CreateComponentsCore(System.ComponentModel.Design.IDesignerHost host)

{

TextBox textbox = (TextBox)host.CreateComponent(typeof(TextBox));

            textbox.Parent = host.RootComponent as Control;

            MyControl myControl = (MyControl)host.CreateComponent(typeof(MyControl));

            return new IComponent[] { textbox, myControl };

}

Please let me know if you need more information of have specific comments.

Comments

  • Anonymous
    January 13, 2005
    Is it works only in VS 2005( .Net Framework 2.0) ? What's about VS 2003( .Net Framework 1.1 ) ?
    Thanks.
  • Anonymous
    January 14, 2005
    IToolboxUser interface is in .Net Framework 1.1 too and is implemented by both DocumentDesigner and ComponentDocumentDesigner. ToolboxItem in .Net Framework 1.1 also has virtual CreateComponentsCore method. IComponentInitialize interface and DesignSurface class is however new in Whidbey providing easy hooks to host/initialize designers.

    Thanks,
    -Dinesh
  • Anonymous
    January 19, 2006
    After reading your article, I thought I'd give IToolboxUser a spin. Howeverr, I'm finding that it's members never get called... Are there any services that conflicts with this?

    public class SampleDesignerHost :
    IContainer
    , IDesignerHost
    , IDesignerLoaderHost
    , IComponentChangeService
    , IExtenderProviderService
    , IToolboxUser
    , IDesignerEventService {

    This function alwasy get called instead.

    public IComponent CreateComponent(Type componentClass) {

    Having ToolPicked() working in the host designer (root) would be most convient and clear to my thinking...


    My mouse down event looks a bit like this:

    if (selectedIndex != 0) {
    if (e.Clicks == 2) {
    IToolboxUser tbu = host.GetDesigner(host.RootComponent) as IToolboxUser;

    if (tbu != null) {
    tbu.ToolPicked(tbi);
    }
    } else if (e.Clicks < 2) {

    try {
    lbSender.DoDragDrop(tbi, DragDropEffects.Copy);
    } catch (Exception ex) {
    MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
    }
    }
    }

    I have drag and drop working ok as it is, however I would like to create a label to go with the control for the screen and then setup soem properties.
  • Anonymous
    March 10, 2006
    Do I have to have to enable drag&drop somewhere for a DesignSurface ?
    I enabled it for the RootComponent (Form in my example), but when I drag a ToolBoxItem from a toolbox, dropping it on the DesignSurface doesn't work (forbidden sign next to the cursor)...

    Please help !
  • Anonymous
    March 31, 2006
    Does MyControl myControl = (MyControl)host.CreateComponent(typeof(MyControl)) always call default constructor? How can we make CreateComponent to call overloaded constructor?
  • Anonymous
    August 15, 2010
    How to add an existing control? When I want to add an already existing control to my design surface, how should I do this? Should I always use CreateComponent and then copy all the relevant properties from my existing control to the new control? Or is there a better and easier way?