سؤال

I have a C# WinForms 'ListboxPanelControl' UserControl that is composed of a SplitContainer that hosts a user-drawn Listbox control in 'Panel1' on the left. The SplitContainer's right-hand 'Panel2' will host Panels that are added as items are added to the Listbox. So, if the design-time user adds the first item to the Listbox, an associated Panel is added to the SplitContainer's 'Panel2'. If a 2nd item is added to the Listbox, a 2nd Panel is added to the SplitContainer's 'Panel2'. Then, as the user selects Listbox items, the associated Panel is displayed on the right.

The problem comes at design-time, when the user drags-and-drops a control (e.g. a Button) from the toolbox onto the currently active right-hand Panel (the Panel corresponding to the currently selected Listbox item). I programatically add it to the appropriate Panel's Controls collection, but this results in a "'child' is not a child control of this parent" error within the Microsoft Visual Studio IDE.

I've researched the problem and discovered some useful articles showing "how to allow a Control, which is a child of another Control to accept having controls dropped onto it at design time", e.g.: http://www.codeproject.com/Articles/37830/Designing-Nested-Controls

This uses the "DesignerSerializationVisibility" attribute in combination with ControlDesigner's "EnableDesignMode" method, to allow the VS IDE to handle the dropping of controls onto another control. The problem is that in this scenario, the UserControl's target hosting Control is effectively fixed and known up-front. With my control, the target hosting Panel which can have other controls dropped onto it at design-time, is not known up-front, since my UserControl itself can be built 'on-the-fly' (as users add more Listbox items resulting in more potential hosting Panels).

I've tried using the "DesignerSerializationVisibility" and "EnableDesignMode" method in a more flexible way, but still seem to run into the aforementioned 'child is not a child control of this parent' error.

I hope this all makes sense!? The code currently looks something like this:

This is within the ListboxPanelControl Class: ...

    [
    Category("Appearance"),
    DesignerSerializationVisibility(DesignerSerializationVisibility.Content)
    ]
    public Panel MainPanel
    {
        get
        {
            //Instead of returning a 'fixed' Panel we return whatever the currently
            //active Panel is according to the currently selected Listbox item
            if( mnCurrentlyActiveListBoxIndex >= 0 )
            {
                ListBoxEntry lEntry = (ListBoxEntry)listBox.Items[mnCurrentlyActiveListBoxIndex];
                return lEntry.RelatedPanel;
            }

            return null;
        }
    }

Then, in the "OnControlAdded" event in the ListBoxPanelControl, I call "EnableDesignMode" (via a simple "SetDesignMode" wrapper) for the currently-active Panel, and then add the control:

protected override void OnControlAdded(ControlEventArgs e)
{
...
mDesigner.SetDesignMode(MainPanel, "MainPanel" + mnCurrentlyActiveListBoxIndex.ToString());
                    ListBoxEntry lEntry = (ListBoxEntry)listBox.Items[mnCurrentlyActiveListBoxIndex];
                    lEntry.RelatedPanel.Controls.Add(e.Control);
                    e.Control.BringToFront();
                    e.Control.Refresh();
...
}

I hope I've explained the issue clearly enough! Basically, has anyone managed to add a Control to their WinForms UserControl, where the target hosting Control is itself a child Control, and when their UserControl is itself dynamic and being built on-the-fly?

Thanks for any help/insight! Tony.

هل كانت مفيدة؟

المحلول

Just thought I'd provide an update in case it's useful for any future readers. I managed to solve the "'child' is not a child control of this parent" error. The problem was that I was calling "EnableDesignMode" on my 'currently active' panel, to allow controls to be dropped onto it, but didn't realise that one of the requirements of the "EnableDesignMode" method is that: "The child control specified by child is a child of this control designer's control." (http://msdn.microsoft.com/en-us/library/system.windows.forms.design.controldesigner.enabledesignmode.aspx). And because the currently active panel was a child of the split-container's right-hand Panel, and not 'this' overall parent ListboxPanel control, it complained!

In the end, I realised that we would also need to serialise the control and after much pain and trial and error, we ended up not using "EnableDesignMode" after all! Instead, the way to go seemed to be to use the Designer's services: IComponentChangeService, IDesignerHost, and ISelectionService. Instead of just adding a control the conventional way (as in "Controls.Add()" ), I use the IDesignerHost's "CreateComponent" method so that the designer 'officially' knows all about it. We wrap each operation in a transaction, so that it can be 'undone/redone'. A bit like this:

DesignerTransaction designerTransaction = m_designHost.CreateTransaction("Add Page");
try
{
    Panel p = (Panel)m_designHost.CreateComponent(typeof(Panel));

    m_changeService.OnComponentChanging(mControl.MainPanel, TypeDescriptor.GetProperties(mControl.MainPanel)["Controls"]);

    ...
    ...
    //Add our Panel to our splitContainer Panel2's controls collection, etc

    ...
    m_changeService.OnComponentChanged(mControl.MainPanel, TypeDescriptor.GetProperties(mControl.MainPanel)["Controls"], null, null);

    //Use the selection service to select the newly added Panel
    m_selectionService.SetSelectedComponents(new object[] { p });

}
catch( Exception ex )
{
     designerTransaction.Cancel();
}
finally
{
     designerTransaction.Commit();
}

Hope that's useful to someone...

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top