绑定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>
问题原因分析
- 重复资源定义覆盖:ListViewEx.xaml中重复定义了
ListViewEx.ControlTemplate和ListViewExStyle,后定义的资源会覆盖前一个,导致最终生效的是仅包含GridViewRowPresenter的单条项模板,而非包含ScrollViewer和ItemsPresenter的完整ListView模板。 - 后台类重复定义:C#代码中重复定义了
ListViewEx类,第二个类没有重写GetContainerForItemOverride方法,导致无法自动生成ListViewItemEx容器。 - 样式循环引用:第一个
ListViewExStyle的BasedOn指向自身,会引发资源引用错误。
解决方案
- 修复后台代码:删除重复的
ListViewEx类定义,保留包含GetContainerForItemOverride方法的版本。 - 清理重复资源:删除ListViewEx.xaml中重复的
ControlTemplate和Style,仅保留包含ScrollViewer和ItemsPresenter的完整模板,以及包含所有样式设置的ListViewExStyle。 - 修正样式继承:将
ListViewExStyle的BasedOn指向原生ListView样式({StaticResource {x:Type ListView}}),避免循环引用。
内容的提问来源于stack exchange,提问作者CHKAM
相关产品推荐
相关产品推荐

