WPF中override ResourceDictionary中的设置的方法

本文详细阐述了在WPF应用中,当资源文件内修改控件样式后,在使用时如何正确地引入新的样式以覆盖资源文件内的设置,确保预期功能实现。以DataGrid为例,说明了在简单引用资源文件而不做处理时,即使在DataGridTextColumn中设置IsReadOnly为false,仍会导致列不能编辑的情况。通过引入新的Style并在Style的Setter里设置,可以有效解决此问题。

当资源文件里改变了控件的样式时,在使用的地方如果想改变资源文件里修改的内容,会造成无法达到预期目的的结果。

以DataGrid为例,我在资源文件里,改变了默认的DataGrid的样式,其中我设置了IsReadOnly为True。在使用时,这将导致DataGrid的列不能编辑;可实际情况要求编辑功能,我们就会想到针对列设置IsReadOnly为false。

如代码里的注释掉的内容所示,在简单地引用资源文件,而不做处理时,是无法达到预期的。

正确的做法是:

引入通过引入新的Style,在Style的Setter里设置,来override资源文件里的设置;这样再针对每个列单独设置IsReadOnly就会起作用。

其中的原理还没有太明白,仅仅记录下处理方法。

 

界面

 1 <Window x:Class="learnwpf.MainWindow"
 2         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 3         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 4         Title="MainWindow" Height="350" Width="525" WindowStartupLocation="CenterScreen">
 5     <Grid>
 6         <DataGrid x:Name="dg" AutoGenerateColumns="False"
 7                   CanUserAddRows="False" CanUserDeleteRows="False" CanUserReorderColumns="False"
 8                   CanUserResizeColumns="False" CanUserResizeRows="False" CanUserSortColumns="False">
 9             <!--以下这样的字典引用方式,将导致即使在DataGridTextColumn里设置IsReadOnly为false仍旧不能编辑-->
10             <!--<DataGrid.Resources>
11                 <ResourceDictionary Source="newlook/DataGrid.xaml" />
12             </DataGrid.Resources>-->
13             <!--解决办法如下,注意x:key的使用-->
14             <DataGrid.Resources>
15                 <ResourceDictionary>
16                     <ResourceDictionary.MergedDictionaries>
17                         <ResourceDictionary Source="newlook/DataGrid.xaml" />
18                         <ResourceDictionary>
19                             <Style TargetType="DataGrid">
20                                 <Setter Property="IsReadOnly" Value="False" />
21                             </Style>
22                         </ResourceDictionary>
23                     </ResourceDictionary.MergedDictionaries>
24                 </ResourceDictionary>
25             </DataGrid.Resources>
26             <DataGrid.Columns>
27                 <DataGridTextColumn x:Name="text" Header="文本" Binding="{Binding Name,UpdateSourceTrigger=PropertyChanged,Mode=TwoWay}" IsReadOnly="True" />
28                 <DataGridTextColumn x:Name="combo" Header="选择" Binding="{Binding Class,UpdateSourceTrigger=PropertyChanged,Mode=TwoWay}" />
29             </DataGrid.Columns>
30         </DataGrid>
31     </Grid>
32 </Window>

资源文件

  1 <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  2                     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
  3     
  4     <SolidColorBrush x:Key="GridHeaderForeground" Color="#333"/>
  5     <SolidColorBrush x:Key="GridHeaderLineBrush" Color="#FFCCCEDB"/>
  6     <SolidColorBrush x:Key="GridHeaderBackgroudBrush" Color="#FFE3E3E3"/>
  7     <SolidColorBrush x:Key="GridLineBrush" Color="#FFEFEFF2"/>
  8     <SolidColorBrush x:Key="GridDrakRow" Color="#FFF9F9F9"/>
  9     <SolidColorBrush x:Key="GridBorderBrush" Color="#FFCCCEDB"/>
 10 
 11     <Style x:Key="ColumnHeaderGripperStyle" TargetType="{x:Type Thumb}">
 12         <Setter Property="Width" Value="3" />
 13         <Setter Property="Background" Value="Transparent" />
 14         <Setter Property="Cursor" Value="SizeWE" />
 15         <Setter Property="Opacity" Value="0" />
 16     </Style>
 17 
 18     <Style TargetType="DataGrid">
 19         <Setter Property="Margin" Value="0"/>
 20         <Setter Property="CanUserResizeColumns" Value="True"/>
 21         <Setter Property="CanUserSortColumns" Value="False"/>
 22         <Setter Property="AutoGenerateColumns" Value="False"/>
 23         <Setter Property="CanUserResizeRows" Value="False"/>
 24         <Setter Property="SelectionMode" Value="Single"/>
 25         <Setter Property="RowHeaderWidth" Value="0"/>
 26         <Setter Property="CanUserAddRows" Value="False"/>
 27         <Setter Property="CanUserDeleteRows" Value="False"/>
 28         <Setter Property="IsReadOnly" Value="True"/>
 29         <Setter Property="Background" Value="AliceBlue" />
 30         <Setter Property="AlternationCount" Value="2" />
 31         <Setter Property="ClipToBounds" Value="True" />
 32         <Setter Property="BorderBrush" Value="{DynamicResource GridBorderBrush}" />
 33         <Setter Property="HorizontalGridLinesBrush" Value="{DynamicResource GridLineBrush}"/>
 34         <Setter Property="VerticalGridLinesBrush" Value="{DynamicResource GridLineBrush}"/>
 35     </Style>
 36 
 37     <Style TargetType="DataGridColumnHeader">
 38         <Setter Property="SnapsToDevicePixels" Value="True" />
 39         <Setter Property="Foreground" Value="{DynamicResource GridHeaderForeground}" />
 40         <Setter Property="Template">
 41             <Setter.Value>
 42                 <ControlTemplate TargetType="DataGridColumnHeader">
 43                     <Border x:Name="BackgroundBorder" BorderThickness="1,0,1,1" 
 44                             Margin="-1,0,0,0"
 45                             Background="{DynamicResource GridHeaderBackgroudBrush}"
 46                             BorderBrush="{DynamicResource GridHeaderLineBrush}" Width="Auto">
 47                         <Grid>
 48                             <Grid.ColumnDefinitions>
 49                                 <ColumnDefinition Width="*" />
 50                             </Grid.ColumnDefinitions>
 51                             <ContentPresenter  Margin="0,0,0,0" VerticalAlignment="Center" HorizontalAlignment="Center"/>
 52                             <Thumb x:Name="PART_LeftHeaderGripper" HorizontalAlignment="Left" Style="{StaticResource ColumnHeaderGripperStyle}" />
 53                             <Thumb x:Name="PART_RightHeaderGripper" HorizontalAlignment="Right" Style="{StaticResource ColumnHeaderGripperStyle}" />
 54                         </Grid>
 55                     </Border>
 56                 </ControlTemplate>
 57             </Setter.Value>
 58         </Setter>
 59         <Setter Property="Height" Value="25"/>
 60     </Style>
 61 
 62     <Style TargetType="{x:Type DataGridRow}">
 63         <Setter Property="Background" Value="Beige" />
 64         <Setter Property="SnapsToDevicePixels" Value="true" />
 65         <Setter Property="Height" Value="24" />
 66         <Setter Property="Template">
 67             <Setter.Value>
 68                 <ControlTemplate TargetType="{x:Type DataGridRow}">
 69                     <Border x:Name="DGR_Border" SnapsToDevicePixels="True">
 70                         <VisualStateManager.VisualStateGroups>
 71                             <VisualStateGroup x:Name="CommonStates">
 72                                 <VisualState x:Name="Normal" />
 73                                 <VisualState x:Name="Unfocused_Selected">
 74                                     <Storyboard>
 75                                         <ObjectAnimationUsingKeyFrames Storyboard.TargetName="DGR_Border" Storyboard.TargetProperty="Background">
 76                                             <DiscreteObjectKeyFrame KeyTime="00:00:00" Value="{DynamicResource CtrlSelectedUnFocusBG}" />
 77                                         </ObjectAnimationUsingKeyFrames>
 78                                     </Storyboard>
 79                                 </VisualState>
 80                                 <VisualState x:Name="Normal_AlternatingRow">
 81                                     <Storyboard>
 82                                         <ObjectAnimationUsingKeyFrames Storyboard.TargetName="DGR_Border" Storyboard.TargetProperty="Background">
 83                                             <DiscreteObjectKeyFrame KeyTime="00:00:00" Value="{StaticResource GridDrakRow}" />
 84                                         </ObjectAnimationUsingKeyFrames>
 85                                     </Storyboard>
 86                                 </VisualState>
 87                                 <VisualState x:Name="MouseOver">
 88                                     <Storyboard>
 89                                         <ObjectAnimationUsingKeyFrames Storyboard.TargetName="DGR_Border" Storyboard.TargetProperty="Background">
 90                                             <DiscreteObjectKeyFrame KeyTime="00:00:00" Value="{DynamicResource CtrlMouseOverBG}" />
 91                                         </ObjectAnimationUsingKeyFrames>
 92                                     </Storyboard>
 93                                 </VisualState>
 94                                 <VisualState x:Name="Normal_Selected">
 95                                     <Storyboard>
 96                                         <ObjectAnimationUsingKeyFrames Storyboard.TargetName="DGR_Border" Storyboard.TargetProperty="Background">
 97                                             <DiscreteObjectKeyFrame KeyTime="00:00:00" Value="{DynamicResource CtrlSelectedBG}" />
 98                                         </ObjectAnimationUsingKeyFrames>
 99                                     </Storyboard>
100                                 </VisualState>
101                             </VisualStateGroup>
102                         </VisualStateManager.VisualStateGroups>
103                         <SelectiveScrollingGrid>
104                             <SelectiveScrollingGrid.ColumnDefinitions>
105                                 <ColumnDefinition Width="Auto" />
106                                 <ColumnDefinition Width="*" />
107                             </SelectiveScrollingGrid.ColumnDefinitions>
108                             <SelectiveScrollingGrid.RowDefinitions>
109                                 <RowDefinition Height="*" />
110                                 <RowDefinition Height="Auto" />
111                             </SelectiveScrollingGrid.RowDefinitions>
112                             <DataGridCellsPresenter Grid.Column="1"
113                                       ItemsPanel="{TemplateBinding ItemsPanel}"
114                                       SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
115                             <DataGridDetailsPresenter Grid.Column="1"
116                                         Grid.Row="1"
117                                         Visibility="{TemplateBinding DetailsVisibility}"
118                                         SelectiveScrollingGrid.SelectiveScrollingOrientation=
119                   "{Binding AreRowDetailsFrozen, 
120                   ConverterParameter={x:Static SelectiveScrollingOrientation.Vertical},
121                   Converter={x:Static DataGrid.RowDetailsScrollingConverter}, 
122                   RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}"/>
123                             <DataGridRowHeader Grid.RowSpan="2"
124                                  SelectiveScrollingGrid.SelectiveScrollingOrientation="Vertical"
125                                  Visibility="{Binding HeadersVisibility, 
126                   ConverterParameter={x:Static DataGridHeadersVisibility.Row}, 
127                   Converter={x:Static DataGrid.HeadersVisibilityConverter}, 
128                   RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}" />
129                         </SelectiveScrollingGrid>
130                     </Border>
131                     <ControlTemplate.Triggers>
132                         <Trigger Property="IsSelected" Value="True">
133                             <Setter Property="Foreground" Value="Black" />
134                         </Trigger>
135                     </ControlTemplate.Triggers>
136                 </ControlTemplate>
137             </Setter.Value>
138         </Setter>
139     </Style>
140 
141 </ResourceDictionary>

逻辑

 1 using System.Collections.Generic;
 2 using System.Windows;
 3 
 4 namespace learnwpf
 5 {
 6     /// <summary>
 7     /// Interaction logic for MainWindow.xaml
 8     /// </summary>
 9     public partial class MainWindow : Window
10     {
11         public MainWindow()
12         {
13             InitializeComponent();
14             init();
15         }
16 
17         private class TestData
18         {
19             public string Name { get; set; }
20             public string Class { get; set; }
21         }
22 
23         private void init()
24         {
25             List<TestData> data = new List<TestData>()
26             {
27                 new TestData(){Name="first",Class="10"},
28                 new TestData(){Name="secon",Class="15"},
29             };
30             dg.ItemsSource = data;
31         }
32     }
33 }

 

转载于:https://www.cnblogs.com/warnet/p/4898570.html

<think> 我们正在讨论WPF中如何延续使用自定义用户控件模板。根据引用[1],CustomControl通过统一的模板和样式定义提供了模块化的UI,便于样式和自定义,并提升代码的可读性和可维护性。英雄联盟项目在WPF中采用这一方法,并在迁移到OpenSilver时应用相同模式,以减少代码修改。 引用[3]提到了在自定义控件中获取模板元素的方法:在OnApplyTemplate方法中,应使用GetTemplateChild而不是VisualTreeHelper来获取ControlTemplate中的元素,因为Visual Tree可能尚未完全构建。 因此,延续使用自定义用户控件模板的关键在于正确使用CustomControl和ControlTemplate,并遵循一定的生命周期规则。 方法如下: 1. **创建自定义控件**:从Control类继承,并重写OnApplyTemplate方法。 2. **定义默认样式**:在generic.xaml中为自定义控件定义默认的ControlTemplate。 3. **获取模板中的元素**:在OnApplyTemplate中,使用GetTemplateChild方法按名称获取模板中的元素。 4. **处理模板应用后的逻辑**:在OnApplyTemplate中,可以添加事件处理等逻辑,但要注意模板可能被动态更换,因此要处理旧模板元素的清理。 具体步骤: **步骤1:创建自定义控件类** ```csharp public class CustomButton : Control { static CustomButton() { // 重写默认样式 DefaultStyleKeyProperty.OverrideMetadata(typeof(CustomButton), new FrameworkPropertyMetadata(typeof(CustomButton))); } // 定义依赖属性等 public override void OnApplyTemplate() { base.OnApplyTemplate(); // 获取模板中的元素 var border = GetTemplateChild("PART_Border") as Border; if (border != null) { // 可以在这里为border添加事件处理等 } // 注意:如果有之前模板的元素事件绑定,需要先移除,避免内存泄漏 } } ``` **步骤2:在generic.xaml中定义默认模板** 在项目的Themes文件夹下创建generic.xaml,并添加类似以下的样式: ```xml <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:YourNamespace"> <Style TargetType="{x:Type local:CustomButton}"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type local:CustomButton}"> <Border x:Name="PART_Border" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}"> <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/> </Border> </ControlTemplate> </Setter.Value> </Setter> </Style> </ResourceDictionary> ``` **步骤3:使用自定义控件** 在XAML中,可以像使用内置控件一样使用自定义控件,并可以通过重新设置Template属性来替换模板。 ```xml <Window ... xmlns:local="clr-namespace:YourNamespace"> <Grid> <local:CustomButton Content="Click Me" Background="LightBlue" /> </Grid> </Window> ``` **步骤4:重新定义模板(延续使用)** 若要为自定义控件应用不同的模板,只需在资源中重新定义样式即可: ```xml <Window.Resources> <Style TargetType="{x:Type local:CustomButton}"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type local:CustomButton}"> <!-- 自定义的新模板 --> <Grid x:Name="PART_Root"> <Ellipse Fill="{TemplateBinding Background}" /> <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/> </Grid> </ControlTemplate> </Setter.Value> </Setter> </Style> </Window.Resources> ``` 这样,自定义控件的模板就可以被延续使用或替换,而不需要修改控件本身的代码。 **注意事项:** - 在OnApplyTemplate中,一定要检查获取的元素是否为null,因为模板可能被替换,而新模板可能没有该命名的部分。 - 如果需要处理事件,记得在模板更换时移除旧模板元素的事件处理程序,避免内存泄漏。 - 使用命名约定(如以"PART_"开头)来标识模板中需要被控件类访问的元素,这是一种常见的做法。 通过以上方法,可以确保自定义用户控件模板的延续使用,同时保持代码的清晰和可维护性[^1][^3]。 </think> ### WPF中延续使用自定义用户控件模板的方法WPF中延续使用自定义用户控件模板的核心是通过 **控件模板(ControlTemplate)** 和 **样式(Style)** 实现UI与逻辑的分离。以下是具体实现方法: #### 1. 创建自定义控件(CustomControl) 这是最佳实践,引用[1]提到这种方法提供**模块化UI**并提升**可维护性**: ```csharp public class CustomButton : Button { static CustomButton() { // 关键:关联默认样式 DefaultStyleKeyProperty.OverrideMetadata( typeof(CustomButton), new FrameworkPropertyMetadata(typeof(CustomButton)) ); } public override void OnApplyTemplate() { base.OnApplyTemplate(); // 使用GetTemplateChild安全获取模板元素(引用[3]推荐) var border = GetTemplateChild("PART_Border") as Border; // 初始化逻辑... } } ``` #### 2. 定义默认模板(Generic.xaml) 在`Themes/Generic.xaml`中定义默认模板: ```xml <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:local="clr-namespace:YourApp"> <Style TargetType="{x:Type local:CustomButton}"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type local:CustomButton}"> <!-- 基础模板 --> <Border x:Name="PART_Border" Background="{TemplateBinding Background}"> <ContentPresenter /> </Border> <ControlTemplate.Triggers> <!-- 交互状态 --> </ControlTemplate.Triggers> </ControlTemplate> </Setter.Value> </Setter> </Style> </ResourceDictionary> ``` #### 3. 模板复用机制 通过以下方式实现模板延续: - **样式继承**: ```xml <Style TargetType="local:CustomButton" BasedOn="{StaticResource {x:Type local:CustomButton}}"> <!-- 扩展模板属性 --> </Style> ``` - **动态模板替换**: ```csharp var newTemplate = (ControlTemplate)FindResource("NewButtonTemplate"); customButton.Template = newTemplate; ``` #### 4. 迁移兼容性设计(引用[1]实践) ```xml <!-- 共享模板库 --> <ResourceDictionary Source="/SharedControls;component/Themes/ButtonTemplates.xaml"/> ``` - 保持**相同的模板部件名称**(如`PART_Border`) - 使用**TemplateBinding**而非直接绑定 - 避免在代码中硬写视觉树操作(推荐使用`GetTemplateChild`) #### 5. 模板部件契约 通过`TemplatePart`特性声明依赖部件: ```csharp [TemplatePart(Name = "PART_Border", Type = typeof(Border))] public class CustomButton : Control { ... } ``` > **关键实践**: > 1. 模板修改只需更新XAML资源,无需修改控件逻辑[^1] > 2. 通过`OnApplyTemplate`安全访问模板元素(避免使用`VisualTreeHelper`在初始化阶段访问)[^3] > 3. 保持**部件命名一致性**确保跨项目迁移时模板兼容[^1] --- ### 相关问题 1. WPF中如何实现控件模板的动态切换? 2. 自定义控件与用户控件(UserControl)在模板应用上有何本质区别? 3. 当迁移WPF控件到其他框架(如OpenSilver)时,模板需要哪些适配调整?[^1]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值