创建具有辅助内容的 DockPanel 控件

Create a DockPanel control with secondary content

我想创建一个自定义控件来提供 DockPanel 的所有功能,但它也公开了一个辅助 OverlayDockPanel 的 "outside"。将有一个依赖项 属性 将控制覆盖面板的可见性,这样当 属性 设置为 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 还是 UserControl,直接从 DockPanel 继承并尝试公开一个单独的 ContentControl,或者可能从 Panel 继承并委托 MeasureOverride和 ArrangeOverride 到 DockPanel。

我该如何解决这个问题?

我建议我们应该尝试阐明您如何看待这项工作。我的猜测是辅助面板也将是 DockPanel,并且将完全覆盖主面板。也就是说,你要么看到一个,要么看到另一个,但永远不会看到两者。您如何设想在两者之间切换?也许 ToggleButton?还是仅在某些 Trigger 的控制之下?

关于实施,我的第一个想法是您似乎喜欢 DockPanel?布局,那么为什么要触及布局方法呢?一种方法可能是只有一个停靠面板,但有两个子项集合,您可以根据要显示的内容进行设置。还是 `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>

有趣的问题。我写了一个 DockPanelWithOverlay 组件来完成这项工作:

这里选择CustomControl是因为我想继承Panel。 但是 Panel 没有可以更改的模板。 所以我写了一个自定义控件,继承了带有自定义模板的控件 但是我认为 Usercontrol 会完美地工作(我没有尝试说实话)

Edit UserControl不太好,因为它继承了ContentControl。 所以它只能有一个children.
DockPanelWithOverlay 的目标是有很多 children。 所以我认为 UserControl 通常不是最好的继承。 当您想在 xaml 中提供一些内容时,UserControl 更好,主要是静态的,不能由控件的用户自定义。

编辑结束

为了在模板中组织内容,我使用了网格。
两个组件的顺序很重要。 这是绘图顺序。

网格允许将两个组件放在同一个地方:
里面会有 Overlay 控件和一个底层 DockPanel。

DockPanelWithOverlay
..|
..|-控制模板
......|
......|-网格
..........|
..........|--Dock面板
..........|--OverlayControl

拥有模板可以更轻松地从 DockPanelWithOverlay 到模板的控件属性进行一些绑定。 (要生成 CustomControl,请创建 WPFCustom 控件库项目)

图书馆中 themes\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>

继承控制允许使用模板创建小的 UIElements 层次结构。

必须添加一些依赖属性以允许绑定:

  1. 用于提供一些 UIElement 的 Overlay,或用于覆盖内容的字符串
  2. hiding/showing 叠加层的 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

我认为这确实是您问题的答案。问候