WPF - 设置外观控件的样式:如何从 ControlTemplates 的第二级访问控件的依赖属性?

发布于 2024-08-08 18:30:37 字数 5154 浏览 12 评论 0原文

我正在扩展 ItemsControlclass 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}}" 

但是就像我在 ItemsControlControlTemplate 内部 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

荒路情人 2024-08-15 18:30:37

哎呀,我的错,

Content="{Binding AlternativeContent, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type WPFControls:EnhancedItemsControl}}}" 

实际上有效(但我的标记比示例中的标记更复杂,并且由于其他错误而没有显示内容......)

Oops, my bad,

Content="{Binding AlternativeContent, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type WPFControls:EnhancedItemsControl}}}" 

actually works (but my markup was more complicated than the one in example, and the content was not shown because of other bug...)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文