创建具有辅助内容的 DockPanel 控件
Create a DockPanel control with secondary content
我想创建一个自定义控件来提供 DockPanel 的所有功能,但它也公开了一个辅助 Overlay是 DockPanel 的 "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 层次结构。
必须添加一些依赖属性以允许绑定:
- 用于提供一些 UIElement 的 Overlay,或用于覆盖内容的字符串
- 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
我认为这确实是您问题的答案。问候
我想创建一个自定义控件来提供 DockPanel 的所有功能,但它也公开了一个辅助 Overlay是 DockPanel 的 "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 层次结构。
必须添加一些依赖属性以允许绑定:
- 用于提供一些 UIElement 的 Overlay,或用于覆盖内容的字符串
- 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
我认为这确实是您问题的答案。问候