吃我的蛋糕也吃它:绑定容器及其内容
Have my cake and eat it too: Binding a Container and It's Contents
我有一个自定义控件 Cake,它包含两个名为 Slice 和 Filling 的 DependencyProperties。 如何创建既能让我访问 Slice,又能让我设计 slice 的样式?
<Style TargetType={x:Type local:Cake}>
//I don't like setting DataContext Here
<Setter Property="DataContext" Value="{Binding RelativeSource={RelativeSource Self}}/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType={x:Type local:Cake}>
<Grid DataContext="{Binding RelativeSource={RelativeSource TemplatedParent}}">
//This is how I display a slice
<ContentPresenter Content={Binding Slice}/>
//This is how cake decorations are displayed
<ItemsPresenter/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="Slice">
<Setter.Value>
//Design Slice Here - it's easy to override when I want
<Slice Filling={Binding Filling}> // it's just in a setter.
</Setter.Value>
</Setter>
<Setter Property="DataContext" Value="{Binding RelativeSource={RelativeSource Self}}/>
</Style>
我试过的选项:
我不能使用用户控件,因为我想允许命名内容,而 apparently 不适用于用户控件。参见 Here.
我不喜欢上面的示例,因为我必须将 Cake 容器的 DataContext 设置为 self,这意味着用户不能将 DataContext 用于 他们的
绑定。
我无法使用 RelativeSource 绑定填充 属性,因为有几个
蛋糕,Style 不知道哪个是正确的 parent。看
Here.
我可以直接用 Slice 元素替换 Content Presenter,
但是因为它在模板中,所以我失去了对 Slice Anywhere 的访问权限
在模板之外。虽然我可能可以沿着 visualTree 向下转换到切片,但这感觉是一场维护噩梦。
我基本上希望每个蛋糕都有一片,并且能够使用
进行设置
<Cake.Slice>
<DockPanel>
<Rectangle Background= “Blue”/>
<Rectangle Background= “Blue”/>
<Rectangle Background=“{Binding Filling}”/>
</DockPanel>
</Cake.Slice>
同时还赋予其默认外观。
编辑:
Apparent 只有我的风格行得通,前提是我引用了 Cake.dll 而不是 Cake 项目。为什么会这样?
这可能不是您所需要的,但我希望它能为您提供实现该目标的指导。
首先,您不需要将 DataContext
设置为控件本身,您可以使用 {TemplateBinding Slice}
从 Cake 的 ControlTemplate 绑定到 Cake 控件的属性(填充和切片),即只是 {Binding Slice, RelativeSource={RelativeSource TemplatedParent}}
的快捷方式(因此您可以使用其中之一)。
这将是您的控件的简化版本,因为我不知道您的 ControlTemplate 中的 ItemPresenter 应该显示哪些项目,或者您的 Slice 和 Filling 属性的类型是什么。在这个例子中 Filling 是 SolidColorBrush
,Slice 是 Style
。该样式应用于 Cake 的 ControlTemplate
中的 ContentControl
,因此您可以为切片预定义样式,并应用您选择的填充(如果您的切片 属性 有不同的用途,您可以例如介绍另一个 属性 称为 SliceStyle)。
蛋糕控:
public class Cake : Control
{
static Cake()
{
DefaultStyleKeyProperty.OverrideMetadata(
typeof(Cake),
new FrameworkPropertyMetadata(typeof(Cake)));
}
public SolidColorBrush Filling
{
get { return (SolidColorBrush)GetValue(FillingProperty); }
set { SetValue(FillingProperty, value); }
}
public static readonly DependencyProperty FillingProperty =
DependencyProperty.Register(
"Filling",
typeof(SolidColorBrush),
typeof(Cake),
new PropertyMetadata(Brushes.Transparent));
public Style Slice
{
get { return (Style)GetValue(SliceProperty); }
set { SetValue(SliceProperty, value); }
}
public static readonly DependencyProperty SliceProperty =
DependencyProperty.Register(
"Slice",
typeof(Style),
typeof(Cake),
new PropertyMetadata(null));
}
默认样式(Generic.xaml):
<Style TargetType="{x:Type local:Cake}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:Cake}">
<ContentControl Width="{TemplateBinding Width}"
Height="{TemplateBinding Height}"
Style="{TemplateBinding Slice}"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
不同的切片样式(这些是为 ContentControl 设置 ControlTemplate,因此您不会在问题中遇到第 3 个问题):
<Window.Resources>
<Style x:Key="TwoLayeredSlice" TargetType="{x:Type ContentControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Rectangle Fill="{Binding Filling,
RelativeSource={RelativeSource AncestorType={x:Type local:Cake}}}"/>
<Rectangle Fill="Brown"
Grid.Row="1"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="FourLayeredSlice" TargetType="{x:Type ContentControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Rectangle Fill="{Binding Filling,
RelativeSource={RelativeSource AncestorType={x:Type local:Cake}}}"/>
<Rectangle Fill="Brown"
Grid.Row="1"/>
<Rectangle Fill="{Binding Filling,
RelativeSource={RelativeSource AncestorType={x:Type local:Cake}}}"
Grid.Row="2"/>
<Rectangle Fill="Brown"
Grid.Row="3"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
以及正在使用的控件:
<Grid Background="Gray">
<local:Cake Width="200"
Height="100"
HorizontalAlignment="Left"
Filling="Gold"
Slice="{StaticResource TwoLayeredSlice}"/>
<local:Cake Width="200"
Height="100"
HorizontalAlignment="Center"
Filling="Pink"
Slice="{StaticResource FourLayeredSlice}"/>
<local:Cake Width="200"
Height="100"
HorizontalAlignment="Right"
Filling="Blue"
Slice="{StaticResource FourLayeredSlice}"/>
</Grid>
祝你胃口大开!
我有一个自定义控件 Cake,它包含两个名为 Slice 和 Filling 的 DependencyProperties。 如何创建既能让我访问 Slice,又能让我设计 slice 的样式?
<Style TargetType={x:Type local:Cake}>
//I don't like setting DataContext Here
<Setter Property="DataContext" Value="{Binding RelativeSource={RelativeSource Self}}/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType={x:Type local:Cake}>
<Grid DataContext="{Binding RelativeSource={RelativeSource TemplatedParent}}">
//This is how I display a slice
<ContentPresenter Content={Binding Slice}/>
//This is how cake decorations are displayed
<ItemsPresenter/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="Slice">
<Setter.Value>
//Design Slice Here - it's easy to override when I want
<Slice Filling={Binding Filling}> // it's just in a setter.
</Setter.Value>
</Setter>
<Setter Property="DataContext" Value="{Binding RelativeSource={RelativeSource Self}}/>
</Style>
我试过的选项:
我不能使用用户控件,因为我想允许命名内容,而 apparently 不适用于用户控件。参见 Here.
我不喜欢上面的示例,因为我必须将 Cake 容器的 DataContext 设置为 self,这意味着用户不能将 DataContext 用于 他们的 绑定。
我无法使用 RelativeSource 绑定填充 属性,因为有几个 蛋糕,Style 不知道哪个是正确的 parent。看 Here.
我可以直接用 Slice 元素替换 Content Presenter, 但是因为它在模板中,所以我失去了对 Slice Anywhere 的访问权限 在模板之外。虽然我可能可以沿着 visualTree 向下转换到切片,但这感觉是一场维护噩梦。
我基本上希望每个蛋糕都有一片,并且能够使用
进行设置<Cake.Slice>
<DockPanel>
<Rectangle Background= “Blue”/>
<Rectangle Background= “Blue”/>
<Rectangle Background=“{Binding Filling}”/>
</DockPanel>
</Cake.Slice>
同时还赋予其默认外观。
编辑: Apparent 只有我的风格行得通,前提是我引用了 Cake.dll 而不是 Cake 项目。为什么会这样?
这可能不是您所需要的,但我希望它能为您提供实现该目标的指导。
首先,您不需要将 DataContext
设置为控件本身,您可以使用 {TemplateBinding Slice}
从 Cake 的 ControlTemplate 绑定到 Cake 控件的属性(填充和切片),即只是 {Binding Slice, RelativeSource={RelativeSource TemplatedParent}}
的快捷方式(因此您可以使用其中之一)。
这将是您的控件的简化版本,因为我不知道您的 ControlTemplate 中的 ItemPresenter 应该显示哪些项目,或者您的 Slice 和 Filling 属性的类型是什么。在这个例子中 Filling 是 SolidColorBrush
,Slice 是 Style
。该样式应用于 Cake 的 ControlTemplate
中的 ContentControl
,因此您可以为切片预定义样式,并应用您选择的填充(如果您的切片 属性 有不同的用途,您可以例如介绍另一个 属性 称为 SliceStyle)。
蛋糕控:
public class Cake : Control
{
static Cake()
{
DefaultStyleKeyProperty.OverrideMetadata(
typeof(Cake),
new FrameworkPropertyMetadata(typeof(Cake)));
}
public SolidColorBrush Filling
{
get { return (SolidColorBrush)GetValue(FillingProperty); }
set { SetValue(FillingProperty, value); }
}
public static readonly DependencyProperty FillingProperty =
DependencyProperty.Register(
"Filling",
typeof(SolidColorBrush),
typeof(Cake),
new PropertyMetadata(Brushes.Transparent));
public Style Slice
{
get { return (Style)GetValue(SliceProperty); }
set { SetValue(SliceProperty, value); }
}
public static readonly DependencyProperty SliceProperty =
DependencyProperty.Register(
"Slice",
typeof(Style),
typeof(Cake),
new PropertyMetadata(null));
}
默认样式(Generic.xaml):
<Style TargetType="{x:Type local:Cake}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:Cake}">
<ContentControl Width="{TemplateBinding Width}"
Height="{TemplateBinding Height}"
Style="{TemplateBinding Slice}"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
不同的切片样式(这些是为 ContentControl 设置 ControlTemplate,因此您不会在问题中遇到第 3 个问题):
<Window.Resources>
<Style x:Key="TwoLayeredSlice" TargetType="{x:Type ContentControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Rectangle Fill="{Binding Filling,
RelativeSource={RelativeSource AncestorType={x:Type local:Cake}}}"/>
<Rectangle Fill="Brown"
Grid.Row="1"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="FourLayeredSlice" TargetType="{x:Type ContentControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Rectangle Fill="{Binding Filling,
RelativeSource={RelativeSource AncestorType={x:Type local:Cake}}}"/>
<Rectangle Fill="Brown"
Grid.Row="1"/>
<Rectangle Fill="{Binding Filling,
RelativeSource={RelativeSource AncestorType={x:Type local:Cake}}}"
Grid.Row="2"/>
<Rectangle Fill="Brown"
Grid.Row="3"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
以及正在使用的控件:
<Grid Background="Gray">
<local:Cake Width="200"
Height="100"
HorizontalAlignment="Left"
Filling="Gold"
Slice="{StaticResource TwoLayeredSlice}"/>
<local:Cake Width="200"
Height="100"
HorizontalAlignment="Center"
Filling="Pink"
Slice="{StaticResource FourLayeredSlice}"/>
<local:Cake Width="200"
Height="100"
HorizontalAlignment="Right"
Filling="Blue"
Slice="{StaticResource FourLayeredSlice}"/>
</Grid>
祝你胃口大开!