WPF如何扩展UserControl资源样式?遇资源字典初始化异常
问题:WPF自定义DataGrid UserControl样式扩展时的ResourceDictionary初始化异常
我基于DataGrid构建了一个带过滤功能的UserControl(过滤逻辑不赘述),控件功能复杂但运行正常(后续计划优化移除大量后台代码)。现在想扩展它的样式——让每个使用该UserControl的地方都能通过自定义转换器实现行分隔等显示效果,但遇到了以下异常:
System.Windows.Markup.XamlParseException: ''设置属性'System.Windows.ResourceDictionary.DeferrableContent'时引发异常。''
InvalidOperationException: 无法重新初始化ResourceDictionary实例。
自定义UserControl代码
<DataGrid x:Class="CustomDataGrid"> <DataGrid.Resources> <!-- Data Grid Row Header --> <Style x:Key="DataGridRowStyle" TargetType="{x:Type DataGridRow}"> <!-- 我希望使用引用该UserControl的控件中定义的特定ControlTemplate(例如添加行分隔符) --> </Style> </DataGrid.Resources> <DataGrid.Style> <!-- DataGrid --> <Style TargetType="{x:Type DataGrid}"> <Setter Property="RowStyle" Value="{StaticResource DataGridRowStyle}" /> </Style> </DataGrid.Style> </DataGrid>
引用该控件的代码
<controls:CustomDataGrid> <controls:CustomDataGrid.Resources> <!-- Data Grid Row Header --> <Style TargetType="{x:Type DataGridRow}"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type DataGridRow}"> <!-- ControlTemplate中包含Border、SelectiveScrollingGrid和DataGridCellsPresenter及特定值转换器等 --> </ControlTemplate> </Setter.Value> </Setter> </Style> </controls:CustomDataGrid.Resources> </controls:CustomDataGrid>
问题原因
自定义UserControl内部已定义DataGrid.Resources,引用控件时又试图重新设置CustomDataGrid.Resources,导致WPF尝试初始化已存在的ResourceDictionary实例,触发异常。
解决方法
方案1:通过依赖属性暴露行模板
在CustomDataGrid后台代码中定义依赖属性,允许传入自定义行模板:
public static readonly DependencyProperty RowTemplateProperty = DependencyProperty.Register("RowTemplate", typeof(ControlTemplate), typeof(CustomDataGrid), new PropertyMetadata(null, OnRowTemplateChanged)); public ControlTemplate RowTemplate { get => (ControlTemplate)GetValue(RowTemplateProperty); set => SetValue(RowTemplateProperty, value); } private static void OnRowTemplateChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var dataGrid = d as CustomDataGrid; if (dataGrid?.RowStyle != null) { dataGrid.RowStyle.Setters.Add(new Setter(DataGridRow.TemplateProperty, e.NewValue)); } }
调整CustomDataGrid的XAML:
<DataGrid x:Class="CustomDataGrid"> <DataGrid.RowStyle> <Style TargetType="{x:Type DataGridRow}"> <!-- 保留默认样式设置,外部模板会覆盖对应属性 --> </Style> </DataGrid.RowStyle> </DataGrid>
引用时直接设置属性:
<controls:CustomDataGrid> <controls:CustomDataGrid.RowTemplate> <ControlTemplate TargetType="{x:Type DataGridRow}"> <!-- 自定义模板内容:Border、SelectiveScrollingGrid、DataGridCellsPresenter及转换器等 --> </ControlTemplate> </controls:CustomDataGrid.RowTemplate> </controls:CustomDataGrid>
方案2:基于样式继承扩展
去掉自定义UserControl内部的DataGrid.Resources,让外部样式继承默认行样式:
修改CustomDataGrid的XAML:
<DataGrid x:Class="CustomDataGrid"> <DataGrid.Style> <Style TargetType="{x:Type DataGrid}"> <Setter Property="RowStyle"> <Setter.Value> <Style TargetType="{x:Type DataGridRow}"> <!-- 定义默认行样式 --> </Style> </Setter.Value> </Setter> </Style> </DataGrid.Style> </DataGrid>
引用时通过BasedOn继承并修改:
<controls:CustomDataGrid> <controls:CustomDataGrid.RowStyle> <Style TargetType="{x:Type DataGridRow}" BasedOn="{Binding RelativeSource={RelativeSource AncestorType=controls:CustomDataGrid}, Path=RowStyle}"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type DataGridRow}"> <!-- 自定义模板内容 --> </ControlTemplate> </Setter.Value> </Setter> </Style> </controls:CustomDataGrid.RowStyle> </controls:CustomDataGrid>
内容的提问来源于stack exchange,提问作者Ludovic Wagner
相关产品推荐
相关产品推荐

