2015-10-08 67 views
1

我想創建一個自定義控制,提供了DockPanel中的所有功能,一個DockPanel中控制,但同時也暴露了一個二次覆蓋這是DockPanel中的「外部」。將會有一個依賴項屬性來控制覆蓋面板的可見性,這樣當屬性設置爲true/visible時,面板將顯示爲覆蓋DockPanel中的所有內容。創建次要內容

理想的情況下,消費者將能夠控制拖放到同樣的情況作爲一個正常的DockPanel中,並沒有其他的變化將表現就像正常DockPanel中:

<DockPanelWithOverlay LastChildFill="True" > 
    <Button DockPanel.Dock="Bottom".../> 
    <Button DockPanel.Dock="Top".../>   
    <Grid> 
     <Grid controls.../> 
    </Grid> 
</DockPanelWithOverlay> 

然而,將可他們可以在其中放置額外內容並在需要時調用的輔助區域。

<DockPanelWithOverlay LastChildFill="True" > 
    <Button DockPanel.Dock="Bottom".../> 
    <Button DockPanel.Dock="Top".../> 
    <Grid> 
     <Grid controls.../> 
    </Grid> 
    <DockPanel.Overlay> 
     <whatever controls for the overlay> 
    </DockPanel.Overlay>  
</DockPanelWithOverlay> 

但是,由於內容設置了兩次,所以這是無效的?所以應對,利用我猜覆蓋的時候我會明確說明什麼去哪裏?:

<DockPanelWithOverlay LastChildFill="True" > 
    <DockPanel.Children> 
     <Button DockPanel.Dock="Bottom".../> 
     <Button DockPanel.Dock="Top".../> 
     <Grid> 
      <Grid controls.../> 
     </Grid>  
    </DockPanel.Children> 
    <DockPanel.Overlay Visibility="{Binding IsVisible}"> 
     <whatever controls for the overlay> 
    </DockPanel.Overlay> 
</DockPanelWithOverlay> 

我不能完全肯定要解決這個最好的辦法:是否創建一個CustomControl,或用戶控件,直接從DockPanel繼承,並嘗試公開一個單獨的ContentControl,或者從Panel繼承,並將MeasureOverride和ArrangeOverride委託給DockPanel。

我應該如何解決這個問題?

回答

1

有趣的問題。我寫了一個DockPanelWithOverlay組件做的工作:

DockPanel with semi transparent overlay

我這裏選擇的是CustomControl因爲我想有面板的繼承。 但面板沒有可以更改的模板。 所以我寫控制的自定義控件繼承了自定義模板 但用戶控件會很好地工作,我認爲(我沒有嘗試要誠實)

編輯用戶控件是不太好,因爲它繼承了ContentControl中。 所以它只能有一個孩子。
DockPanelWithOverlay的目標是有很多孩子。 所以我認爲UserControl並不是最好的繼承方式。 當你想在xaml中提供一些內容時,用戶控件會更好,主要是靜態的,不能由控件的用戶自定義。

編輯

末來組織內容的tempalte裏面,我用了一個網格。
這兩個組件的順序很重要。 這是繪圖順序。

網格允許將兩個組件放在同一個地方:
裏面會有Overlay控件和一個底層DockPanel。

DockPanelWithOverlay
.. |
.. | - 控制模板
...... |
...... | -Grid
.......... |
.......... | --DockPanel
.......... | --OverlayControl

具有模板的更容易使從DockPanelWithOverlay一些結合模板的控件屬性。 (要生成CustomControl,創建WPFCustom控件庫項目)主題\ generic.xaml的

摘錄庫:

<Style TargetType="{x:Type local:DockPanelWithOverlay}"> 
    <Setter Property="Template"> 
     <Setter.Value> 
      <ControlTemplate TargetType="{x:Type local:DockPanelWithOverlay}"> 
       <!-- the grid allows to put two components at the same place --> 
       <Grid > 
        <DockPanel x:Name="dockPanel" /> 
        <ContentControl x:Name="overlayControl" Visibility="{TemplateBinding OverlayVisibility}" /> 
       </Grid> 
      </ControlTemplate> 
     </Setter.Value> 
    </Setter> 
</Style> 

控制的繼承允許使用模板來創建小的UI元素的層次結構。

一些依賴屬性必須添加用於使結合:

  1. 疊加,用於提供一些UI元素或字符串覆蓋內容
  2. OverlayVisibility用於隱藏/顯示覆蓋

這裏是DockPanelWithOverlay的代碼:
(注意剛剛調用模板組件後調用的OnApplytemplate)

// Children is the property that will be valued with the content inside the tag of the control 
[ContentProperty("Children")] 
public class DockPanelWithOverlay : Control 
{ 
    static DockPanelWithOverlay() 
    { 
     // Associate the control with its template in themes/generic.xaml 
     DefaultStyleKeyProperty.OverrideMetadata(typeof(DockPanelWithOverlay), new FrameworkPropertyMetadata(typeof(DockPanelWithOverlay))); 
    } 
    public DockPanelWithOverlay() 
    { 
     Children = new UIElementCollection(this, this); 
    } 
    public override void OnApplyTemplate() 
    { 
     base.OnApplyTemplate(); 
     // once the template is instanciated, the dockPanel and overlayCOntrol can be found from the template 
     // and the children of DockPanelWithOverlay can be put in the DockPanel 
     var dockPanel = this.GetTemplateChild("dockPanel") as DockPanel; 
     if (dockPanel != null) 
      for (int i = 0; i < Children.Count;) 
      { 
       UIElement elt = Children[0]; 
       Children.RemoveAt(0); 
       dockPanel.Children.Add(elt); 
      } 
    } 
    // Here is the property to show or not the overlay 
    public Visibility OverlayVisibility 
    { 
     get { return (Visibility)GetValue(OverlayVisibilityProperty); } 
     set { SetValue(OverlayVisibilityProperty, value); } 
    } 
    // Here is the overlay. Tipically it could be a Texblock, 
    // or like in our example a Grid holding a TextBlock so that we could put a semi transparent backround 
    public Object Overlay 
    { 
     get { return (Object)GetValue(OverlayProperty); } 
     set { SetValue(OverlayProperty, value); } 
    } 
    // Using a DependencyProperty as the backing store for OverlayProperty. 
    // This enables animation, styling, binding, etc... 
    public static readonly DependencyProperty OverlayProperty = 
     DependencyProperty.Register("Overlay", typeof(Object), typeof(DockPanelWithOverlay), new PropertyMetadata(null)); 
    public static readonly DependencyProperty OverlayVisibilityProperty = 
     DependencyProperty.Register("OverlayVisibility", typeof(Visibility), typeof(DockPanelWithOverlay), new PropertyMetadata(Visibility.Visible)); 
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)] 
    public UIElementCollection Children 
    { 
     get { return (UIElementCollection)GetValue(ChildrenProperty); } 
     set { SetValue(ChildrenProperty, value); } 
    } 
    public static readonly DependencyProperty ChildrenProperty = 
     DependencyProperty.Register("Children", typeof(UIElementCollection), typeof(DockPanelWithOverlay), new PropertyMetadata(null)); 
} 

使用DockPanelWithOverlay:

<lib:DockPanelWithOverlay x:Name="dockPanelWithOverlay1" 
           OverlayVisibility="Visible"    
           HorizontalAlignment="Stretch" VerticalAlignment="Stretch"> 
    <Button Content="Top" Height="50" DockPanel.Dock="Top" Background="Red"/> 
    <Button Content="Bottom" Height="50" DockPanel.Dock="Bottom" Background="Yellow"/> 
    <Button Content="Left" Width="50" DockPanel.Dock="Left" Background="Pink"/> 
    <Button Content="Right" Width="50" DockPanel.Dock="Right" Background="Bisque"/> 
    <Button Content="Center" Background="Azure"/> 
    <lib:DockPanelWithOverlay.Overlay> 
     <Grid Background="#80404080"> 
      <TextBlock Text="Overlay" FontSize="80" Foreground="#FF444444" HorizontalAlignment="Center" VerticalAlignment="Center" RenderTransformOrigin="0.5,0.5"> 
       <TextBlock.RenderTransform> 
        <TransformGroup> 
         <ScaleTransform/> 
         <SkewTransform/> 
         <RotateTransform Angle="-15"/> 
         <TranslateTransform/> 
        </TransformGroup> 
       </TextBlock.RenderTransform> 
      </TextBlock> 
     </Grid> 
    </lib:DockPanelWithOverlay.Overlay> 
</lib:DockPanelWithOverlay> 

該覆蓋可以很容易地開啓或關閉從CheckBox.IsChecked屬性例如結合進行切換。

以下是完整的工作代碼:http://1drv.ms/1NfCl9z

我覺得這是真的回答你的問題。問候

+0

謝謝,這正是我一直在尋找的東西。 – Jason

+0

我喜歡這個,並且會仔細研究它。謝謝Emmanuel。 – PScr

0

我建議我們應該嘗試澄清你如何看待這個工作。我的猜測是,次面板也將是DockPanel,並將完全覆蓋主面板。也就是說,你看到了這個或那個,但從來都沒有。你如何設想在兩者之間切換?也許是?ToggleButton?或者只是在一些Trigger的控制下?

我首先想到的是,你好像喜歡DockPanel?爲什麼觸摸佈局方法?一種方法可能是隻有一個dockset,但是兩個孩子的集合,你根據這個集合來展示。或者在Popup的輔助面板?

你希望能夠寫出這樣的事:

<DockPanelWithAlternative 
    AlternativeVisibility="{Binding somethingHere}" > 
    <TextBlock Dock.Top ... /> 
    <TextBlock Dock.Alternative.Top ... /> 

</DockPanelWithAlternative> 

我在想什麼的是一樣的東西:

<UserControl> 
    <Grid> 
     <DockPanel x:Name="MainPanel" ZIndex="0"/> 
     <DockPanel x:Name="AlternativePanel" Visbility=... ZIndex="1"/> 
    </Grid> 
</UserControl> 
+0

感謝您的回覆@PScr。我期待輔助面板可能承載任何東西,然後消費者可以根據需要插入DockPanel。它通常用於向用戶傳達消息,但非常注重於該控件,而不是使用通用應用程序對話框。它可能會過渡,或者有一些微妙的透明度來允許一些主要的DockPanel顯示。 – Jason

+0

是的,我明白了。也許我們應該考慮一個帶有「DockPanel」和「ContentControl」的'UserControl',它位於頂部。然後你可以爲'ContentControl'設置一個'DataTemplate',用戶可以設置它: PScr

+0

我剛讀到這個:http:// www.codeproject.com/Articles/820324/Implementing-Dialog-Boxes-in-MVVM。也許有一些想法可以適應。 – PScr