2012-09-24 47 views
3

我有一個C#WinForms的ListboxPanelControl'用戶控件,它由一個SplitContainer組成,該控件在左邊的'Panel1'中託管一個用戶繪製的Listbox控件。 SplitContainer的右側「Panel2」將託管在項目添加到列表框時添加的面板。因此,如果設計時用戶將第一項添加到列表框中,則將關聯的面板添加到SplitContainer的「Panel2」中。如果將第二項添加到列表框中,則將第二個面板添加到SplitContainer的「Panel2」中。然後,當用戶選擇列表框項目時,關聯的面板顯示在右側。在設計時將控件添加到我的「即時」用戶控件中。

問題出現在設計階段,當用戶從工具箱拖放控件(例如按鈕)到當前活動的右側面板(與當前選定的列表框項目對應的面板)時。我編程式地將它添加到適當的Panel Controls集合中,但是這會導致「'child'不是該父級的子控件」錯誤在Microsoft Visual Studio IDE中。

我研究這個問題,並發現了一些有用的文章顯示「如何讓一個控制,這是另一種控制的孩子接受其控制拖放到它在設計時」,例如:http://www.codeproject.com/Articles/37830/Designing-Nested-Controls

這將「DesignerSerializationVisibility」屬性與ControlDesigner的「EnableDesignMode」方法結合使用,以允許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用戶控件,其中目標託管控件本身是一個子控件,並且當他們的用戶控件本身是動態的,並正在建造中?

感謝您的任何幫助/見解! Tony。

回答

1

只是想我會提供一個更新,以防它對任何未來的讀者有用。我設法解決了「'孩子'不是這個父母的孩子控制權」的錯誤。問題在於我在'當前活動'面板上調用了「EnableDesignMode」,以允許將控件放到它上面,但沒有意識到「EnableDesignMode」方法的要求之一是:「孩子由孩子指定的控制是該控制設計者控制的孩子。「http://msdn.microsoft.com/en-us/library/system.windows.forms.design.controldesigner.enabledesignmode.aspx)。 而且由於當前活動面板是分體容器的右側面板的子面,而不是「此」整體父級ListboxPanel控件,因此它已經投訴!最後,我意識到我們還需要對控件進行序列化,經過很多痛苦和試驗和錯誤之後,我們最終沒有使用「EnableDesignMode」!相反,要走的路似乎是使用Designer的服務:IComponentChangeService,IDesignerHost和ISelectionService。我不使用常規方式(如「Controls.Add()」)添加控件,而是使用IDesignerHost的「CreateComponent」方法,以便設計師'正式'知道它的全部內容。我們將每個操作都包含在一個事務中,以便它可以'撤消/重做'。有點像這樣:

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

希望這是有用的人......

+0

感謝。我正在處理類似的事情,這可能會有所幫助。一個簡單的說明:無論是否存在異常,您的finally塊都會執行。取消之後調用designerTransaction.Commit()是否可以? – TrespassersW

+0

設計師主持人在哪裏發揮作用?你在哪裏執行這個代碼?它在設計師課上嗎? – ernest

相關問題