带有附加属性和自定义模板的 ListView GridViewColumnHeader

Posted

技术标签:

【中文标题】带有附加属性和自定义模板的 ListView GridViewColumnHeader【英文标题】:ListView GridViewColumnHeader with attached property and custom template 【发布时间】:2022-01-07 17:13:04 【问题描述】:

我正在尝试逐列自定义ListViewGridViewColumnHeader。我已经能够通过手动将每个列的 GridViewColumnHeader 设置为内联 XAML 来做到这一点(请参阅“Thing 2”列),这没关系,但超级重复,但我真的更希望有一个带有控件的可重用样式带有一些附加属性的模板(尝试在“事物 1”列中)。

基本上,我需要设置度量单位文本块以及页脚标记文本块的样式。

附加属性似乎已设置,但我无法弄清楚如何在模板中提取它们的值。

我意识到GridColumnHeader 不是可视化树的一部分,这可能是这里的主要问题,但它似乎如此接近!

“Thing 1”列的样式设置为内联 XAML,直到我弄清楚如何获取附加的属性值。理想情况下,它将设置在控制模板中,如ExampleListViewHeader

我已经删除了所有不相关的代码:

MainWindow.xaml

<Window.Resources>
        <x:Array x:Key="ExampleItems" Type="x:Type local:ExampleItems">
            <local:ExampleItems Cell1="Item 1-1" Cell2="Item 1-2" Cell3="Item 1-3" />
            <local:ExampleItems Cell1="Item 2-1" Cell2="Item 2-2" Cell3="Item 2-3" />
            <local:ExampleItems Cell1="Item 3-1" Cell2="Item 3-2" Cell3="Item 3-3" />
            <local:ExampleItems Cell1="Item 4-1" Cell2="Item 4-2" Cell3="Item 4-3" />
        </x:Array>

        <Style x:Key="ExampleListView" TargetType="x:Type ListView">
            <Setter Property="VerticalAlignment" Value="Top" />
            <Setter Property="HorizontalContentAlignment" Value="Stretch" />
            <Setter Property="BorderBrush" Value="Transparent" />
            <Setter Property="IsHitTestVisible" Value="False" />
            <Setter Property="Margin" Value="0, 0, 0, 5" />
        </Style>

        <Style x:Key="ExampleListViewHeader" TargetType="x:Type GridViewColumnHeader">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="x:Type GridViewColumnHeader">
                        <TextBlock Text="TemplateBinding Content" Width="TemplateBinding Width" Padding="2, 0" TextWrapping="Wrap" VerticalAlignment="Bottom"/>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
            <Setter Property="FontFamily" Value="DynamicResource PLC_Font" />
            <Setter Property="FontSize" Value="DynamicResource PLC_FontSize_Sub_1" />
            <Setter Property="FontStyle" Value="Italic" />
            <Setter Property="Foreground" Value="Black" />
        </Style>
    </Window.Resources>
    
    <Grid>
        <ListView ItemsSource="StaticResource ExampleItems" Style="StaticResource ExampleListView">
            <ListView.View>
                <GridView>
                    <GridView.Columns>
                        <GridViewColumn Width="100"
                                        local:HeaderAttachedProperties.Marker="1"
                                        local:HeaderAttachedProperties.UofM=" (ft) "
                                        DisplayMemberBinding="Binding Cell1">
                            <GridViewColumnHeader>
                                <Grid>
                                    <Grid.ColumnDefinitions>
                                        <ColumnDefinition Width="Auto" />
                                        <ColumnDefinition Width="Auto" />
                                        <ColumnDefinition Width="Auto" />
                                    </Grid.ColumnDefinitions>

                                    <TextBlock Grid.Column="0" Text="Thing 1" VerticalAlignment="Bottom"/>
                                    <TextBlock Grid.Column="1" Text=" (ft) " FontSize="10" VerticalAlignment="Center"/>

                                    <!--<TextBlock Grid.Column="2" Text="TemplateBinding local:HeaderAttachedProperties.Marker"/>-->

                                    <!--<TextBlock Grid.Column="2" Text="Binding RelativeSource=RelativeSource TemplatedParent, Path=(local:HeaderAttachedProperties.Marker)"/>-->
                                    
                                    <!--<TextBlock Grid.Column="2" Text="Binding RelativeSource=RelativeSource Mode=FindAncestor, AncestorType=x:Type GridViewColumn, Path=(local:HeaderAttachedProperties.Marker)"/>-->
                                    <!--<TextBlock Grid.Column="2" Text="Binding RelativeSource=RelativeSource Mode=FindAncestor, AncestorType=GridViewColumn, Path=(local:HeaderAttachedProperties.Marker)"/>-->
                                    
                                </Grid>
                            </GridViewColumnHeader>
                        </GridViewColumn>

                        <GridViewColumn Width="100"
                                        DisplayMemberBinding="Binding Cell2">
                            <GridViewColumnHeader>
                                <Grid>
                                    <Grid.ColumnDefinitions>
                                        <ColumnDefinition Width="Auto" />
                                        <ColumnDefinition Width="Auto" />
                                        <ColumnDefinition Width="Auto" />
                                    </Grid.ColumnDefinitions>

                                    <TextBlock Grid.Column="0" Text="Thing 2" VerticalAlignment="Bottom"/>
                                    <TextBlock Grid.Column="1" Text=" (ft) " FontSize="10" VerticalAlignment="Center"/>

                                    <TextBlock Grid.Column="2" Text="*" FontSize="10" VerticalAlignment="Center"/>
                                </Grid>
                            </GridViewColumnHeader>
                        </GridViewColumn>
                        
                        <GridViewColumn Header="Thing 3" DisplayMemberBinding="Binding Cell3" HeaderContainerStyle="StaticResource ExampleListViewHeader" />
                    </GridView.Columns>
                </GridView>
            </ListView.View>
        </ListView>
    </Grid>

示例项目

public class ExampleItems

    public string Cell1  get; set; 
    public string Cell2  get; set; 
    public string Cell3  get; set; 

HeaderAttachedProperties

public class HeaderAttachedProperties : DependencyObject

    public static readonly DependencyProperty MarkerProperty = DependencyProperty.RegisterAttached(
        name: "Marker",
        propertyType: typeof(string),
        ownerType: typeof(HeaderAttachedProperties),
        defaultMetadata: new PropertyMetadata(""));

    public static string GetMarker(DependencyObject pDependencyObject)
    
        return (string)pDependencyObject.GetValue(MarkerProperty);
    

    public static void SetMarker(DependencyObject pDependencyObject, string pValue)
    
        pDependencyObject.SetValue(MarkerProperty, pValue);
    

    public static readonly DependencyProperty UofMProperty = DependencyProperty.RegisterAttached(
        name: "UofM",
        propertyType: typeof(string),
        ownerType: typeof(HeaderAttachedProperties),
        defaultMetadata: new PropertyMetadata(""));

    public static string GetUofM(DependencyObject pDependencyObject)
    
        return (string)pDependencyObject.GetValue(UofMProperty);
    

    public static void SetUofM(DependencyObject pDependencyObject, string pValue)
    
        pDependencyObject.SetValue(UofMProperty, pValue);
    

【问题讨论】:

【参考方案1】:

您不一定需要ControlTemplate。您可以改用DataTemplate。为了绑定GridViewColumn上的附加属性,您可以使用GridViewColumnHeaderColumn属性。

<Style x:Key="ListViewHeaderStyle" TargetType="x:Type GridViewColumnHeader">
   <Setter Property="FontFamily" Value="DynamicResource PLC_Font" />
   <Setter Property="FontSize" Value="DynamicResource PLC_FontSize_Sub_1" />
   <Setter Property="FontStyle" Value="Italic" />
   <Setter Property="Foreground" Value="Black" />
</Style>
<GridViewColumn Width="100"
                local:HeaderAttachedProperties.Marker="1"
                local:HeaderAttachedProperties.UofM=" (ft) "
                DisplayMemberBinding="Binding Cell1"
                HeaderContainerStyle="StaticResource ListViewHeaderStyle">
   <GridViewColumn.HeaderTemplate>
      <DataTemplate>
         <Grid>
            <Grid.ColumnDefinitions>
               <ColumnDefinition Width="Auto" />
               <ColumnDefinition Width="Auto" />
               <ColumnDefinition Width="Auto" />
            </Grid.ColumnDefinitions>
            <TextBlock Grid.Column="0" Text="Thing 1" VerticalAlignment="Bottom"/>
            <TextBlock Grid.Column="1" Text="Binding Column.(local:HeaderAttachedProperties.UofM), RelativeSource=RelativeSource AncestorType=x:Type GridViewColumnHeader" FontSize="10" VerticalAlignment="Center"/>
            <TextBlock Grid.Column="2" Text="Binding Column.(local:HeaderAttachedProperties.Marker), RelativeSource=RelativeSource AncestorType=x:Type GridViewColumnHeader"/>
         </Grid>
      </DataTemplate>
   </GridViewColumn.HeaderTemplate>
</GridViewColumn>

如果您也将第一个 TextBlock 参数化,您可以提取并重用它。

<DataTemplate x:Key="ListViewHeaderTemplate">
   <Grid>
      <Grid.ColumnDefinitions>
         <ColumnDefinition Width="Auto" />
         <ColumnDefinition Width="Auto" />
         <ColumnDefinition Width="Auto" />
      </Grid.ColumnDefinitions>
      <TextBlock Grid.Column="0" Text="Binding Column.(local:HeaderAttachedProperties.Anything), RelativeSource=RelativeSource AncestorType=x:Type GridViewColumnHeader" FontSize="10" VerticalAlignment="Center"/>
      <TextBlock Grid.Column="1" Text="Binding Column.(local:HeaderAttachedProperties.UofM), RelativeSource=RelativeSource AncestorType=x:Type GridViewColumnHeader" FontSize="10" VerticalAlignment="Center"/>
      <TextBlock Grid.Column="2" Text="Binding Column.(local:HeaderAttachedProperties.Marker), RelativeSource=RelativeSource AncestorType=x:Type GridViewColumnHeader"/>
   </Grid>
</DataTemplate>
<GridViewColumn Width="100"
                local:HeaderAttachedProperties.Marker="1"
                local:HeaderAttachedProperties.UofM=" (ft) "
                DisplayMemberBinding="Binding Cell1"
                HeaderTemplate="StaticResource ListViewHeaderTemplate"
                HeaderContainerStyle="StaticResource ListViewHeaderStyle">
</GridViewColumn>

您当然可以更改ControlTemplate,但您应该小心,因为它定义了控件的视觉外观和状态。在您当前的模板中,您会丢失大部分状态。

<Style x:Key="ListViewHeader" TargetType="x:Type GridViewColumnHeader">
   <Setter Property="Template">
      <Setter.Value>
         <ControlTemplate TargetType="x:Type GridViewColumnHeader">
            <Grid>
               <Grid.ColumnDefinitions>
                  <ColumnDefinition Width="Auto" />
                  <ColumnDefinition Width="Auto" />
                  <ColumnDefinition Width="Auto" />
               </Grid.ColumnDefinitions>
               <TextBlock Grid.Column="0" Text="Binding Column.(local:HeaderAttachedProperties.Anything), RelativeSource=RelativeSource TemplatedParent" FontSize="10" VerticalAlignment="Center"/>
               <TextBlock Grid.Column="1" Text="Binding Column.(local:HeaderAttachedProperties.UofM), RelativeSource=RelativeSource TemplatedParent" FontSize="10" VerticalAlignment="Center"/>
               <TextBlock Grid.Column="2" Text="Binding Column.(local:HeaderAttachedProperties.Marker), RelativeSource=RelativeSource TemplatedParent"/>
            </Grid>
         </ControlTemplate>
      </Setter.Value>
   </Setter>
   <Setter Property="FontFamily" Value="DynamicResource PLC_Font" />
   <Setter Property="FontSize" Value="DynamicResource PLC_FontSize_Sub_1" />
   <Setter Property="FontStyle" Value="Italic" />
   <Setter Property="Foreground" Value="Black" />
</Style>
<GridViewColumn Width="100"
                local:HeaderAttachedProperties.Marker="1"
                local:HeaderAttachedProperties.UofM=" (ft) "
                DisplayMemberBinding="Binding Cell1">
   <GridViewColumnHeader Style="StaticResource ListViewHeader"/>
</GridViewColumn>

我省略了TextWrappingVerticalAlignment 属性,在需要的地方重新引入它们。

【讨论】:

太棒了!感谢您花时间详细说明所有内容-我非常感谢。 现在我有机会回到这个项目,是的,目标总是包括第一个项目,我只是想如果我不能让它工作对于我尝试的一个字段,没有理由一遍又一遍地更改三个字段的代码。

以上是关于带有附加属性和自定义模板的 ListView GridViewColumnHeader的主要内容,如果未能解决你的问题,请参考以下文章

带有复选框和自定义适配器的 ListView,片段无法正常工作

Android Studio itemsAdapter 和自定义 ListView 不起作用

带有自定义控件和自定义 StringProperty 的 Proguard

ListView 和自定义适配器在 Kotlin 中不起作用

如何使用复选框和自定义适配器从 Listview 中获取选定的列表项?

如何在 ComboBox 中将“附加属性”定义为“SelectedValuePath”?