如何在自定义控件中通过ComponentResourceKey修改内部自定义控件样式
你遇到的核心问题是:用ComponentResourceKey定义的ButtonBrushKey,资源查找逻辑会优先从应用程序级、主题级资源字典中查找。你之前在MyCustomControl的Style根节点定义资源,这个层级不在MyToggleButton的资源查找路径内,所以不生效;而Window的Resources是全局层级,能被所有子元素找到,因此可以生效。
下面提供三种可行的解决办法:
方法一:在ControlTemplate内部定义资源
将修改ButtonBrush的资源放在MyCustomControl的ControlTemplate的Resources中,作为子元素的MyToggleButton就能正确识别到该资源:
<Style TargetType="{x:Type ctrl:MyCustomControl}"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type ctrl:MyCustomControl}"> <!-- 把资源放在ControlTemplate的Resources中 --> <ControlTemplate.Resources> <SolidColorBrush x:Key="{x:Static toggle:MyToggleButton.ButtonBrushKey}" Color="Green" /> </ControlTemplate.Resources> <Border BorderBrush="SteelBlue" BorderThickness="2" Background="LightGray"> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto"/> <ColumnDefinition Width="Auto"/> </Grid.ColumnDefinitions> <toggle:MyToggleButton Grid.Column="0" VerticalAlignment="Center" Height="45" Width="100" Margin="5,0,0,0"/> <TextBlock Grid.Column="1" VerticalAlignment="Center" Text="Some Text" FontWeight="Bold" Margin="5,0,0,0"/> </Grid> </Border> </ControlTemplate> </Setter.Value> </Setter> </Style>
这种方式会让MyCustomControl内部所有的MyToggleButton都使用该颜色的ButtonBrush。
方法二:单独给MyToggleButton实例设置资源
如果只想修改当前这个特定的MyToggleButton实例,直接在它自身的Resources节点中定义即可:
<toggle:MyToggleButton Grid.Column="0" VerticalAlignment="Center" Height="45" Width="100" Margin="5,0,0,0"> <toggle:MyToggleButton.Resources> <SolidColorBrush x:Key="{x:Static toggle:MyToggleButton.ButtonBrushKey}" Color="Green" /> </toggle:MyToggleButton.Resources> </toggle:MyToggleButton>
方法三:改用依赖属性(推荐)
使用ComponentResourceKey的方式灵活性不足,将ButtonBrush改为MyToggleButton的依赖属性是WPF自定义控件的最佳实践,这样可以像普通控件属性一样直接设置,还支持绑定、样式Setter等操作。
1. 修改MyToggleButton后台代码
public class MyToggleButton : ToggleButton { // 定义ButtonBrush依赖属性 public static readonly DependencyProperty ButtonBrushProperty = DependencyProperty.Register( nameof(ButtonBrush), typeof(Brush), typeof(MyToggleButton), new PropertyMetadata(Brushes.Red)); // 默认红色 public Brush ButtonBrush { get => (Brush)GetValue(ButtonBrushProperty); set => SetValue(ButtonBrushProperty, value); } static MyToggleButton() { DefaultStyleKeyProperty.OverrideMetadata(typeof(MyToggleButton), new FrameworkPropertyMetadata(typeof(MyToggleButton))); } }
2. 更新Generic.xaml中的模板
把原来的DynamicResource绑定替换为TemplateBinding,关联到新的依赖属性:
<Style TargetType="{x:Type ctrl:MyToggleButton}"> <Setter Property="Background" Value="SteelBlue"/> <Setter Property="BorderBrush" Value="Green"/> <Setter Property="BorderThickness" Value="2"/> <Setter Property="Height" Value="28"/> <Setter Property="Width" Value="65"/> <Setter Property="ButtonBrush" Value="Red"/> <!-- 设置默认值 --> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type ctrl:MyToggleButton}"> <Border x:Name="back" BorderBrush="{Binding BorderBrush, RelativeSource={RelativeSource TemplatedParent}}" Background="{Binding Background, RelativeSource={RelativeSource TemplatedParent}}" BorderThickness="{Binding BorderThickness, RelativeSource={RelativeSource TemplatedParent}}" CornerRadius="5"> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition/> <ColumnDefinition/> </Grid.ColumnDefinitions> <!-- 修改Background为TemplateBinding ButtonBrush --> <Border x:Name="button" Grid.Column="0" Grid.ColumnSpan="2" Background="{TemplateBinding ButtonBrush}" Width="16" Height="16" Margin="2" CornerRadius="3" HorizontalAlignment="Left"> <!-- 修改Fill为TemplateBinding ButtonBrush --> <Path x:Name="icon" Fill="{TemplateBinding ButtonBrush}" Stretch="Uniform" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="2" Data="{Binding OnPathData, RelativeSource={RelativeSource TemplatedParent}}"/> </Border> <TextBlock x:Name="text" Grid.Column="1" Text="On" FontSize="12" Foreground="White" HorizontalAlignment="Center" VerticalAlignment="Center"/> </Grid> </Border> </ControlTemplate> </Setter.Value> </Setter> </Style>
3. 在MyCustomControl中直接设置属性
现在可以像设置Width、Height一样,直接给MyToggleButton设置ButtonBrush属性:
<toggle:MyToggleButton Grid.Column="0" VerticalAlignment="Center" Height="45" Width="100" Margin="5,0,0,0" ButtonBrush="Green"/>
这种方式还支持样式Setter、数据绑定等高级操作,比如可以在MyCustomControl的Style中统一设置内部所有MyToggleButton的ButtonBrush。
内容的提问来源于stack exchange,提问作者CoderForHire

