WPF - 设置外观控件的样式:如何从 ControlTemplates 的第二级访问控件的依赖属性?
我正在扩展 ItemsControl
(class EnhancedItemsControl : ItemsControl
),因为我想向其添加几个依赖属性 - 例如将显示的 AlternativeContent
当集合中没有项目时(想想搜索结果的项目控件中的“输入搜索词并点击搜索”标签)。
我对 ItemsControl
进行了子类化并添加了 AlternativeContent
dep。其类型为 FrameworkElement
的属性。现在我想在 Themes/Generic.xaml 中提供默认样式(我已将 ThemeInfoAttribute
添加到 AsseblyInfo,并在静态构造函数中提供元数据,如 优秀教程)。
该样式包含一个 ControlTemplate
,我需要在 ItemsControl 模板内使用第二个 ControlTemplate
,我在其中添加一个 ContentPresenter
来显示 >替代内容
。
现在,我的问题是如何告诉 ContentPresenter
它应该从顶级 EnhancedItemsControl
获取其内容?如果我在 style 的 ControlTemplate
内部,我会使用:
Content="{Binding AlternativeContent, RelativeSource={RelativeSource TemplatedParent}}"
但是就像我在 ItemsControl
的 ControlTemplate
内部 style 的 ControlTemplate
一样>,这显然不起作用,我需要引用的不是父模板,而是祖父母模板,但是, TemplateBinding
没有 AncestorLevel
参数。
我也尝试过:
Content="{Binding AlternativeContent, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type WPFControls:EnhancedItemsControl}}}"
但这也会导致空的 ContentPresenter
。我无法命名 TemplatedParent
(因为它位于 ControlTemplate
之外),因此我无法按名称引用它。我无法使用 TemplatedParent
RelativeBinding
,因为它没有达到两个级别的控件模板。奇怪的是,RelativeSource
FindAncestor
不起作用。
知道如何解决这个问题吗?谢谢你!
Generic.xaml(摘录):
<Style TargetType="{x:Type WPFControls:EnhancedItemsControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type WPFControls:EnhancedItemsControl}">
<ItemsControl
x:Name="RootItemsControl"
ItemsSource="{Binding}"
ItemTemplate="{TemplateBinding ItemTemplate}"
Background="{TemplateBinding Background}"
HorizontalContentAlignment="Stretch"
>
<ItemsControl.Template>
<ControlTemplate>
<ScrollViewer
x:Name="ContentScrollViewer"
CanContentScroll="False"
>
<StackPanel
x:Name="InnerPanel"
>
<ItemsPresenter
Width="{Binding ActualWidth, ElementName=InnerPanel}"
MaxWidth="{Binding ActualWidth, ElementName=InnerPanel}"
HorizontalAlignment="Stretch"
/>
<ContentPresenter
Content="{Binding AlternativeContent, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type WPFControls:EnhancedItemsControl}}}"
>
<ContentPresenter.Style>
<Style>
<Setter Property="ContentPresenter.Visibility" Value="Collapsed" />
<Style.Triggers>
<DataTrigger
Binding="{Binding Items.Count, ElementName=RootItemsControl}"
Value="0">
<Setter Property="ContentPresenter.Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</ContentPresenter.Style>
</ContentPresenter>
</StackPanel>
</ScrollViewer>
</ControlTemplate>
</ItemsControl.Template>
</ItemsControl>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
控制代码:
public class EnhancedItemsControl : ItemsControl
{
static EnhancedItemsControl()
{
DefaultStyleKeyProperty.OverrideMetadata(
typeof(EnhancedItemsControl),
new FrameworkPropertyMetadata(typeof(EnhancedItemsControl)));
}
public FrameworkElement AlternativeContent
{
get { return (FrameworkElement)GetValue(AlternativeContentProperty); }
set { SetValue(AlternativeContentProperty, value); }
}
// Using a DependencyProperty as the backing store for AlternativeContent. This enables animation, styling, binding, etc...
public static readonly DependencyProperty AlternativeContentProperty =
DependencyProperty.Register("AlternativeContent", typeof(FrameworkElement), typeof(EnhancedItemsControl), new UIPropertyMetadata(null));
}
用法(List
作为 DataContext
提供):
<WPFControls:EnhancedItemsControl Height="120" x:Name="EnhancedCollection"
>
<WPFControls:EnhancedItemsControl.AlternativeContent>
<WPFControls:CenteredLabel>
Alternative content
</WPFControls:CenteredLabel>
</WPFControls:EnhancedItemsControl.AlternativeContent>
<WPFControls:EnhancedItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}" />
</DataTemplate>
</WPFControls:EnhancedItemsControl.ItemTemplate>
</WPFControls:EnhancedItemsControl>
I am extending the ItemsControl
(class EnhancedItemsControl : ItemsControl
), because I want to add several dependecy properties to it - like AlternativeContent
which will be displayed when there are no items in collection (think of 'enter a search terms and hit search' label in a itemscontrol for results of search).
I have subclassed ItemsControl
and added an AlternativeContent
dep. property of type FrameworkElement
to it. Now I want to provide default style in Themes/Generic.xaml (I have added ThemeInfoAttribute
to AsseblyInfo, and provided metadata in static costructor as said in this excellent tutorial).
The style contains a ControlTemplate
, and I need to use second ControlTemplate
inside of ItemsControl template, where I add a ContentPresenter
that should show the AlternativeContent
.
Now, my problem is how do I tell the ContentPresenter
that it should take its content from the top-level EnhancedItemsControl
? If I were inside style's ControlTemplate
, I would use:
Content="{Binding AlternativeContent, RelativeSource={RelativeSource TemplatedParent}}"
but as I am in ItemsControl
's ControlTemplate
inside style's ControlTemplate
, this obviously doesn't work, I'd need to refer not to parent template, but to grandparent template, however, TemplateBinding
doesn't have the AncestorLevel
parameter.
I also tried:
Content="{Binding AlternativeContent, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type WPFControls:EnhancedItemsControl}}}"
but that results in empty ContentPresenter
as well. I cannot name the TemplatedParent
(because it is outside the ControlTemplate
), so I cannot refer to it by name. I cannot use TemplatedParent
RelativeBinding
, because that doesn't reach over two levels of controltemplates. And RelativeSource
FindAncestor
strangely doesn't work.
Any idea how to solve this? Thank you!
Generic.xaml (excerpt):
<Style TargetType="{x:Type WPFControls:EnhancedItemsControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type WPFControls:EnhancedItemsControl}">
<ItemsControl
x:Name="RootItemsControl"
ItemsSource="{Binding}"
ItemTemplate="{TemplateBinding ItemTemplate}"
Background="{TemplateBinding Background}"
HorizontalContentAlignment="Stretch"
>
<ItemsControl.Template>
<ControlTemplate>
<ScrollViewer
x:Name="ContentScrollViewer"
CanContentScroll="False"
>
<StackPanel
x:Name="InnerPanel"
>
<ItemsPresenter
Width="{Binding ActualWidth, ElementName=InnerPanel}"
MaxWidth="{Binding ActualWidth, ElementName=InnerPanel}"
HorizontalAlignment="Stretch"
/>
<ContentPresenter
Content="{Binding AlternativeContent, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type WPFControls:EnhancedItemsControl}}}"
>
<ContentPresenter.Style>
<Style>
<Setter Property="ContentPresenter.Visibility" Value="Collapsed" />
<Style.Triggers>
<DataTrigger
Binding="{Binding Items.Count, ElementName=RootItemsControl}"
Value="0">
<Setter Property="ContentPresenter.Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</ContentPresenter.Style>
</ContentPresenter>
</StackPanel>
</ScrollViewer>
</ControlTemplate>
</ItemsControl.Template>
</ItemsControl>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Control code:
public class EnhancedItemsControl : ItemsControl
{
static EnhancedItemsControl()
{
DefaultStyleKeyProperty.OverrideMetadata(
typeof(EnhancedItemsControl),
new FrameworkPropertyMetadata(typeof(EnhancedItemsControl)));
}
public FrameworkElement AlternativeContent
{
get { return (FrameworkElement)GetValue(AlternativeContentProperty); }
set { SetValue(AlternativeContentProperty, value); }
}
// Using a DependencyProperty as the backing store for AlternativeContent. This enables animation, styling, binding, etc...
public static readonly DependencyProperty AlternativeContentProperty =
DependencyProperty.Register("AlternativeContent", typeof(FrameworkElement), typeof(EnhancedItemsControl), new UIPropertyMetadata(null));
}
Usage (a List<string>
is provided as DataContext
):
<WPFControls:EnhancedItemsControl Height="120" x:Name="EnhancedCollection"
>
<WPFControls:EnhancedItemsControl.AlternativeContent>
<WPFControls:CenteredLabel>
Alternative content
</WPFControls:CenteredLabel>
</WPFControls:EnhancedItemsControl.AlternativeContent>
<WPFControls:EnhancedItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}" />
</DataTemplate>
</WPFControls:EnhancedItemsControl.ItemTemplate>
</WPFControls:EnhancedItemsControl>
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
哎呀,我的错,
实际上有效(但我的标记比示例中的标记更复杂,并且由于其他错误而没有显示内容......)
Oops, my bad,
actually works (but my markup was more complicated than the one in example, and the content was not shown because of other bug...)