Добавление элементов управления в мой пользовательский элемент управления "на лету" во время разработки..?

У меня есть пользовательский элемент управления C# WinForms ListboxPanelControl, который состоит из SplitContainer, который содержит нарисованный пользователем элемент управления Listbox в Panel1 слева. Панель Panel2, расположенная справа от SplitContainer, будет содержать панели, которые добавляются по мере добавления элементов в список. Таким образом, если пользователь времени разработки добавляет первый элемент в список, соответствующая панель добавляется в "панель2" SplitContainer. Если второй элемент добавляется в список, вторая панель добавляется в SplitContainer Panel2. Затем, когда пользователь выбирает элементы списка, соответствующая панель отображается справа.

Проблема возникает во время разработки, когда пользователь перетаскивает элемент управления (например, кнопку) с панели инструментов на текущую активную правую панель (панель, соответствующую выбранному в данный момент элементу списка). Я программно добавляю его в соответствующую коллекцию элементов управления Panel, но это приводит к ошибке "дочерний элемент не является дочерним элементом управления этого родительского элемента" в Microsoft Visual Studio IDE.

Я исследовал проблему и обнаружил несколько полезных статей, показывающих "как разрешить элемент управления, который является дочерним элементом другого элемента управления, принимать допущенные элементы управления во время разработки", например: http://www.codeproject.com/Articles/37830/Designing-Nested-Controls

При этом используется атрибут "DesignerSerializationVisibility" в сочетании с методом "EnableDesignMode" ControlDesigner, чтобы позволить VS IDE обрабатывать перетаскивание элементов управления на другой элемент управления. Проблема заключается в том, что в этом сценарии целевой хост-контроль UserControl эффективно зафиксирован и известен заранее. Под моим контролем целевая хостинговая панель, на которую могут быть добавлены другие элементы управления во время разработки, не известна заранее, поскольку сам мой UserControl может быть построен "на лету" (поскольку пользователи добавляют больше элементов списка, в результате чего в более потенциальных панелях хостинга).

Я попытался использовать методы "DesignerSerializationVisibility" и "EnableDesignMode" более гибким способом, но все же, похоже, натолкнулся на вышеупомянутое "дочерний элемент не является дочерним элементом управления этой родительской ошибки".

Я надеюсь, что все это имеет смысл!? Код в настоящее время выглядит примерно так:

Это внутри класса ListboxPanelControl: ...

    [
    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;
        }
    }

Затем в событии "OnControlAdded" в ListBoxPanelControl я вызываю "EnableDesignMode" (через простую оболочку "SetDesignMode") для активной в данный момент панели, а затем добавляю элемент управления:

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();
...
}

Я надеюсь, что объяснил проблему достаточно ясно! По сути, кому-нибудь удалось добавить элемент управления в свой пользовательский элемент управления WinForms, где целевой хост-элемент управления сам является дочерним элементом управления, и когда их пользовательский элемент управления сам по себе является динамическим и создается на лету?

Спасибо за любую помощь / понимание! Тони.

1 ответ

Решение

Просто подумал, что предоставлю обновление на случай, если оно пригодится будущим читателям. Мне удалось решить ошибку "ребенок не является дочерним элементом управления этого родителя". Проблема заключалась в том, что я вызывал "EnableDesignMode" на своей "активной в данный момент" панели, чтобы разрешить перетаскивание на него элементов управления, но не понимал, что одно из требований метода "EnableDesignMode" заключается в следующем: "Дочерний элемент управления указанный child является дочерним для этого элемента управления конструктора элемента управления ". ( http://msdn.microsoft.com/en-us/library/system.windows.forms.design.controldesigner.enabledesignmode.aspx). И поскольку активная в данный момент панель была дочерней по отношению к правой панели панели split-контейнера, а не "this" общего родительского элемента управления ListboxPanel, она жаловалась!

В конце концов, я понял, что нам также нужно будет сериализовать элемент управления, и после большой боли, проб и ошибок мы в конечном итоге не использовали "EnableDesignMode"! Вместо этого казалось, что нужно использовать сервисы дизайнера: IComponentChangeService, IDesignerHost и ISelectionService. Вместо простого добавления элемента управления обычным способом (как в "Controls.Add()"), я использую метод "CreateComponent" IDesignerHost, чтобы дизайнер "официально" знал все об этом. Мы заключаем каждую операцию в транзакцию, чтобы ее можно было "отменить / повторить". Немного так:

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();
}

Надеюсь, это кому-нибудь пригодится...

Другие вопросы по тегам