创建具有辅助内容

时间:2015-10-08 14:11:12

标签: c# wpf wpf-controls dockpanel

我想创建一个自定义控件,它提供 DockPanel 的所有功能,但它也会公开一个辅助 Overlay ,它位于的“外部” > DockPanel中。将存在一个依赖项属性来控制覆盖面板的可见性,这样当属性设置为真/可见时,面板将显示为覆盖在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。

我该如何解决这个问题?

2 个答案:

答案 0 :(得分:1)

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

DockPanel with semi transparent overlay

我在这里选择了CustomControl,因为我希望继承Panel。 但Panel没有可以更改的模板。 所以我用自定义模板编写了一个继承Control的Custom Control 但是我认为用户控制完全可行(我没有试图说实话)

编辑 UserControl不太好,因为它继承了ContentControl。 所以它只能有一个孩子 DockPanelWithOverlay的目标是拥有许多孩子。 所以我认为UserControl并不是最好的继承。 当你想在xaml中提供一些内容时,UserControl会更好,主要是静态的,不能由控件用户自定义。

编辑结束

为了整理tempalte中的内容,我使用了一个Grid 这两个组件的顺序很重要。 这是绘图顺序。

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

DockPanelWithOverlay
.. |
.. | -ControlTemplate
...... |
...... | -Grid
.......... |
.......... | --DockPanel
.......... | --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. 用于提供一些UIElements的叠加层或用于叠加内容的字符串
  2. 用于隐藏/显示叠加层的OverlayVisibility
  3. 这是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

    我认为这真的是你问题的答案。此致

答案 1 :(得分:0)

我建议我们应该尝试澄清你是如何看待这种方法的。我的猜测是辅助面板也是DockPanel,并且将完全覆盖主面板。也就是说,你看到的是其中之一,但从来没有。您如何设想在两者之间切换?或许ToggleButton?或者只是在一些Trigger的控制之下?

我对实现的第一个想法是你似乎喜欢DockPanel的方式?把事情搞定了,为什么要触摸布局方法呢?一种方法可能是只有一个dockpanel,而是两个子集合,您可以根据要显示的集合进行设置。或者弹出窗口中的辅助面板?

你想写这样的东西:

<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>