You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

绑定GridView的ListViewEx自定义默认样式加载失败问题

WPF扩展控件ListViewEx使用GridView时默认样式加载失败问题

我正在开发一个包含扩展控件的类库,基于ListView和ListViewItem实现了ListViewEx与ListViewItemEx控件。遇到一个问题:当为ListViewEx设置GridView时,ResourceDictionary中定义的默认Style和ControlTemplate无法自动加载。手动在Window中给ListViewEx指定Style={StaticResource ListViewExStyle}、给ListViewItemEx指定Style={StaticResource ListViewItemExStyle}时,样式能正常生效。


后台代码(存在重复定义问题)

public class ListViewEx : ListView
{
    /* 外观扩展相关的依赖属性,比如BackgroundInactive、BackgroundMouseOver、CornerRadius等... */
    
    static ListViewEx()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(ListViewEx),
            new FrameworkPropertyMetadata(typeof(ListViewEx)));
    }
    
    protected override DependencyObject GetContainerForItemOverride()
    {
        return new ListViewItemEx();
    }
}

// 重复定义的ListViewEx类,无GetContainerForItemOverride方法
public class ListViewEx : ListView
{
    /* 外观扩展相关的依赖属性,比如BackgroundInactive、BackgroundMouseOver、CornerRadius等... */

    static ListViewEx()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(ListViewEx),
            new FrameworkPropertyMetadata(typeof(ListViewEx)));
    }
}

样式文件内容

Themes/ListViewEx.xaml(存在重复资源定义问题)

<ResourceDictionary
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:local="clr-namespace:chkam05.Tools.ControlsEx"
  xmlns:theme="clr-namespace:chkam05.Tools.ControlsEx.Data.Theme"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    
  <x:Static x:Key="ThemeMgr" Member="theme:ThemeManager.Instance"/>
    
  <ControlTemplate x:Key="ListViewEx.ScrollViewerEx.ControlTemplate" TargetType="{x:Type local:ScrollViewer}">
    <Border
      x:Name="border"
      Background="{TemplateBinding Background}"
      BorderBrush="{TemplateBinding BorderBrush}"
      BorderThickness="{TemplateBinding BorderThickness}"
      CornerRadius="{TemplateBinding CornerBackground}">
            
      <Grid>
        <Grid.ColumnDefinitions>
          <ColumnDefinition Width="*"/>
          <ColumnDefinition Width="Auto"/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
          <RowDefinition Height="*"/>
          <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
              
        <DockPanel
          Grid.Column="0"
          Grid.Row="0"
          Margin="{TemplateBinding Padding}">
                
          <ScrollViewer
            DockPanel.Dock="Top"
            Focusable="False"
            HorizontalScrollBarVisibility="Hidden"
            VerticalScrollBarVisibility="Hidden">
                  
            <GridViewHeaderRowPresenter
              AllowsColumnReorder="{Binding Path=TemplatedParent.View.AllowsColumnReorder, RelativeSource={RelativeSource TemplatedParent}}"
              Columns="{Binding Path=TemplatedParent.View.Columns, RelativeSource={RelativeSource TemplatedParent}}"
              ColumnHeaderContainerStyle="{Binding Path=TemplatedParent.View.ColumnHeaderContainerStyle, RelativeSource={RelativeSource TemplatedParent}}"
              ColumnHeaderContextMenu="{Binding Path=TemplatedParent.View.ColumnHeaderContextMenu, RelativeSource={RelativeSource TemplatedParent}}"
              ColumnHeaderTemplate="{Binding Path=TemplatedParent.View.ColumnHeaderTemplate, RelativeSource={RelativeSource TemplatedParent}}"
              ColumnHeaderTemplateSelector="{Binding Path=TemplatedParent.View.ColumnHeaderTemplateSelector, RelativeSource={RelativeSource TemplatedParent}}"
              ColumnHeaderToolTip="{Binding Path=TemplatedParent.View.ColumnHeaderToolTip, RelativeSource={RelativeSource TemplatedParent}}"
              Margin="0"
              SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
          </ScrollViewer>
          
          <ScrollContentPresenter
            Name="PART_ScrollContentPresenter"
            KeyboardNavigation.DirectionalNavigation="Local"
            CanContentScroll="True"
            CanHorizontallyScroll="False"
            CanVerticallyScroll="False"/>
        </DockPanel>
        
        <local:ScrollBarEx
          x:Name="PART_VerticalScrollBar"
          AutomationProperties.AutomationId="VerticalScrollBar"
          Cursor="Arrow"
          Grid.Column="1"
          Grid.Row="0"
          Maximum="{TemplateBinding ScrollableHeight}"
          Minimum="0"
          MaxWidth="{TemplateBinding VerticalScrollBarWidth}"
          Style="{TemplateBinding VerticalScrollBarStyle}"
          Value="{Binding VerticalOffset, Mode=OneWay, RelativeSource={RelativeSource Mode=TemplatedParent}}"
          ViewportSize="{TemplateBinding ViewportHeight}"
          Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}"
          Width="{TemplateBinding VerticalScrollBarWidth}"/>

        <local:ScrollBarEx
          x:Name="PART_HorizontalScrollBar"
          AutomationProperties.AutomationId="HorizontalScrollBar"
          Cursor="Arrow"
          Grid.Column="0"
          Grid.Row="1"
          Height="{TemplateBinding HorizontalScrollBarHeight}"
          MaxHeight="{TemplateBinding HorizontalScrollBarHeight}"
          Maximum="{TemplateBinding ScrollableWidth}"
          Minimum="0"
          Orientation="Horizontal"
          Style="{TemplateBinding HorizontalScrollBarStyle}"
          Value="{Binding HorizontalOffset, Mode=OneWay, RelativeSource={RelativeSource Mode=TemplatedParent}}"
          ViewportSize="{TemplateBinding ViewportWidth}"
          Visibility="{TemplateBinding ComputedHorizontalScrollBarVisibility}"/>
      </Grid>
    </Border>
  </ControlTemplate>
  
  <Style x:Key="ListViewEx.ScrollViewerExStyle" BasedOn="{StaticResource ScrollViewerExStyle}" TargetType="{x:Type local:ScrollViewerEx}">
    <Setter Property="Template" Value="{StaticResource ListViewEx.ScrollViewerEx.ControlTemplate}"/>
  </Style>
  
  <!-- 第一个ListViewEx模板(完整模板) -->
  <ControlTemplate x:Key="ListViewEx.ControlTemplate" TargetType="{x:Type local:ListViewEx}">
    <Border
      x:Name="Bd"
      Background="{TemplateBinding Background}"
      BorderBrush="{TemplateBinding BorderBrush}"
      BorderThickness="{TemplateBinding BorderThickness}"
      CornerRadius="{TemplateBinding CornerRadius}"
      SnapsToDevicePixels="true">
      
      <local:ScrollViewerEx Style="{StaticResource ListViewEx.ScrollViewerExStyle}">
        <ItemsPresenter x:Name="itemsPresenter"/>
      </local:ScrollViewerEx>
    </Border>
  </ControlTemplate>
  
  <!-- 第一个ListViewEx样式(存在循环引用) -->
  <Style x:Key="ListViewExStyle" BasedOn="{StaticResource ListViewExStyle}" TargetType="{x:Type local:ListViewEx}">
    <Setter Property="GridView.ColumnHeaderContainerStyle" Value="{StaticResource ListViewEx.ColumnHeaderStyle}"/>
    <Setter Property="Template" Value="{StaticResource ListViewEx.ControlTemplate}"/>
  </Style>
  
  <!-- 第二个ListViewEx模板(仅单条项模板,覆盖了前面的完整模板) -->
  <ControlTemplate x:Key="ListViewEx.ControlTemplate" TargetType="{x:Type local:ListViewEx}">
    <Border
      x:Name="border"
      Background="{TemplateBinding Background}"
      BorderBrush="{TemplateBinding BorderBrush}"
      BorderThickness="{TemplateBinding BorderThickness}"
      CornerRadius="{TemplateBinding CornerRadius}"
      Padding="{TemplateBinding Padding}"
      SnapsToDevicePixels="true">

      <GridViewRowPresenter
        x:Name="contentPresenter"
        HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
        SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
        TextBlock.Foreground="{TemplateBinding Foreground}"
        VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
    </Border>
  </ControlTemplate>

  <!-- 第二个ListViewEx样式(覆盖了前面的样式) -->
  <Style x:Key="ListViewExStyle" TargetType="{x:Type local:ListViewEx}">
    <Setter Property="Background" Value="{Binding DataContext.ThemeBackground, Mode=OneWay, Source={StaticResource ThemeMgr}}"/>
    <Setter Property="BackgroundInactive" Value="{Binding DataContext.BackgroundInactive, Mode=OneWay, Source={StaticResource ThemeMgr}}"/>
    <Setter Property="BorderBrush" Value="{Binding DataContext.AccentBorderBrush, Mode=OneWay, Source={StaticResource ThemeMgr}}"/>
    <Setter Property="BorderBrushInactive" Value="{Binding DataContext.BorderBrushInactive, Mode=OneWay, Source={StaticResource ThemeMgr}}"/>
    <Setter Property="BorderThickness" Value="1"/>
    <Setter Property="Foreground" Value="{Binding DataContext.ThemeForeground, Mode=OneWay, Source={StaticResource ThemeMgr}}"/>
    <Setter Property="ForegroundInactive" Value="{Binding DataContext.ForegroundInactive, Mode=OneWay, Source={StaticResource ThemeMgr}}"/>
    <Setter Property="ScrollViewer.CanContentScroll" Value="true"/>
    <Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Auto"/>
    <Setter Property="ScrollViewer.PanningMode" Value="Both"/>
    <Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto"/>
    <Setter Property="Stylus.IsFlicksEnabled" Value="False"/>
    <Setter Property="Template" Value="{StaticResource ListViewEx.ControlTemplate}"/>
    <Setter Property="VerticalContentAlignment" Value="Center"/>
  </Style>
</ResourceDictionary>

Themes/Generic.xaml

<ResourceDictionary
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:local="clr-namespace:chkam05.Tools.ControlsEx"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
  
  <ResourceDictionary.MergedDictionaries>
    <!-- 其他控件样式 -->
    <ResourceDictionary Source="pack://application:,,,/chkam05.Tools.ControlsEx;component/Themes/ListViewEx.xaml"/>
    <ResourceDictionary Source="pack://application:,,,/chkam05.Tools.ControlsEx;component/Themes/ListViewItemEx.xaml"/>
    <!-- 其他控件样式 -->
  </ResourceDictionary.MergedDictionaries>
  
  <Style BasedOn="{StaticResource ListViewExStyle}" TargetType="{x:Type local:ListViewEx}"/>
  <Style BasedOn="{StaticResource ListViewItemExStyle}" TargetType="{x:Type local:ListViewItemEx}"/>
  
</ResourceDictionary>

问题原因分析

  1. 重复资源定义覆盖:ListViewEx.xaml中重复定义了ListViewEx.ControlTemplate和ListViewExStyle,后定义的资源会覆盖前一个,导致最终生效的是仅包含GridViewRowPresenter的单条项模板,而非包含ScrollViewer和ItemsPresenter的完整ListView模板。
  2. 后台类重复定义:C#代码中重复定义了ListViewEx类,第二个类没有重写GetContainerForItemOverride方法,导致无法自动生成ListViewItemEx容器。
  3. 样式循环引用:第一个ListViewExStyle的BasedOn指向自身,会引发资源引用错误。

解决方案

  1. 修复后台代码:删除重复的ListViewEx类定义,保留包含GetContainerForItemOverride方法的版本。
  2. 清理重复资源:删除ListViewEx.xaml中重复的ControlTemplate和Style,仅保留包含ScrollViewer和ItemsPresenter的完整模板,以及包含所有样式设置的ListViewExStyle。
  3. 修正样式继承:将ListViewExStyle的BasedOn指向原生ListView样式({StaticResource {x:Type ListView}}),避免循环引用。

内容的提问来源于stack exchange,提问作者CHKAM

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 20:39:59