如何为通用基础创建默认样式 class
How to create a default style for a common base class
Objective
我有一个针对 ToggleButton
的自定义控件,它也适用于 Button
。所以我想对这两种类型使用通用的默认值 ControlTemplate
。
我尝试的策略是在模板中设置 TargetType="{x:Type ButtonBase}"
,如果在 Button
或 ToggleButton
.
上明确设置,则效果很好
隐式
自定义控件库
在项目根目录的 Themes 文件夹中名为 Generic.xaml 的资源字典中...
<Style TargetType="{x:Type ButtonBase}">
<Setter Property="Template">
<Setter.Value>
<!--Modified Control Template-->
<ControlTemplate TargetType="{x:Type ButtonBase}">
在控件的 class 中,我使用 FrameworkElement.DefaultStyleKey...
在其静态构造函数中设置了用户控件类型的元数据
static ContentToggle()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(ContentToggle),
new FrameworkPropertyMetadata(typeof(ButtonBase)));
}
正在使用 WPF 应用程序项目
App.xaml...
<Application x:Class="Spec.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="MainWindow.xaml">
<Application.Resources>
<ResourceDictionary>
<!--Get a reference to the window to establish View Context-->
<RelativeSource x:Key="View" Mode="FindAncestor"
AncestorType="{x:Type Window}" />
<ResourceDictionary.MergedDictionaries>
<!--Local Style-->
<ResourceDictionary Source="pack://application:,,,/ButtonStyle.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>
MainWindow.xaml...
<Window x:Class="Spec.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:b="clr-namespace:ContentToggleButton;assembly=ContentToggleButton"
Title="MainWindow" Height="350" Width="525">
<Grid>
<b:ContentToggle Name="Toggle" Height="30"
Content="{Binding options, RelativeSource={StaticResource View}}"
/>
</Grid>
</Window>
MainWindow.xaml.cs
public partial class MainWindow : Window
{
public List<string> options { get; set; }
public bool initialState { get; set; }
public MainWindow ()
{
options = new List<string> { "Checked", "UnChecked" };
initialState = false;
InitializeComponent();
}
}
还有一个名为 ButtonStyle.xaml 的文件,它定义了自定义控件要使用的画笔。它通过 App.xaml.
中的合并字典暴露在应用程序的根目录中
结果
ContentToggle instance
的模板为 null,样式化控件没有视觉效果(当我窥探控件时,它没有子元素)。
我的理解是自动ButtonBase
style/template 将用于我的控制。我错过了什么?
显式
如果在控件上显式声明 style/template,自定义控件将按预期工作。以下适用于样式目标设置为 ButtonBase
...
正在使用 WPF 应用程序项目
在App.xaml...
<Application x:Class="Spec.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="MainWindow.xaml">
<Application.Resources>
<ResourceDictionary>
<!--Get a reference to the window to establish View Context-->
<RelativeSource x:Key="View" Mode="FindAncestor"
AncestorType="{x:Type Window}" />
<ResourceDictionary.MergedDictionaries>
<!--custom control-->
<ResourceDictionary Source="pack://application:,,,/ContentToggleButton;component/Themes/Generic.xaml" />
<!--Local Style-->
<ResourceDictionary Source="pack://application:,,,/ButtonStyle.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>
在MainWindow.xaml...
<Window x:Class="Spec.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:b="clr-namespace:ContentToggleButton;assembly=ContentToggleButton"
Title="MainWindow" Height="350" Width="525">
<Grid>
<b:ContentToggle Name="Toggle" Height="30"
Content="{Binding options, RelativeSource={StaticResource View}}"
Style="{DynamicResource LocalButtonStyle}"
/>
</Grid>
</Window>
在ButtonStyle.xaml...
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<!--Custom Button backgrounds-->
<LinearGradientBrush x:Key="Button.Static.Background"
EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#FF2C0606" Offset="1"/>
<GradientStop Color="#E6ADAD"/>
</LinearGradientBrush>
<LinearGradientBrush x:Key="Button.MouseOver.Background" EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#FF2C0606" Offset="1"/>
<GradientStop Color="#FFF2F2"/>
</LinearGradientBrush>
<LinearGradientBrush x:Key="Button.MouseOver.Checked.Background" EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#FF2C0606" Offset="1"/>
<GradientStop Color="#F2FFF3"/>
</LinearGradientBrush>
<LinearGradientBrush x:Key="Button.Checked.Background" EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#FF2C0606" Offset="1"/>
<GradientStop x:Name="GradientStop" Color="#ADE6B1"/>
</LinearGradientBrush>
<!--Establish the style colours-->
<SolidColorBrush x:Key="Button.Static.Border" Color="#FF707070" />
<SolidColorBrush x:Key="Button.MouseOver.Border" Color="#FF3C7FB1" />
<SolidColorBrush x:Key="Button.Pressed.Background" Color="#C4F6CE" />
<SolidColorBrush x:Key="Button.Pressed.Border" Color="#FF2C628B" />
<SolidColorBrush x:Key="Button.Disabled.Background" Color="#FFF4F4F4" />
<SolidColorBrush x:Key="Button.Disabled.Border" Color="#FFADB2B5" />
<SolidColorBrush x:Key="Button.Disabled.Foreground" Color="#FF838383" />
<!--Custom Style-->
<Style x:Key="LocalButtonStyle" TargetType="{x:Type ButtonBase}" BasedOn="{StaticResource ButtonStyle}">
<Setter Property="HorizontalAlignment" Value="Stretch" />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="HorizontalContentAlignment" Value="Center" />
<Setter Property="VerticalContentAlignment" Value="Center" />
<Setter Property="Background"
Value="{StaticResource Button.Static.Background}"/>
<Setter Property="BorderBrush"
Value="{StaticResource Button.Static.Border}"/>
</Style>
</ResourceDictionary>
解决方案结构(两种情况通用)
我不完全知道为什么,但是对于 ButtonBase 主题搜索根本不起作用(可能与它是抽象的有关)。即使您尝试使用例如 FindResource
或类似方法为 ButtonBase
找到样式 - 您也找不到任何东西,即使您在 Generic.xaml 中自己定义它(对于其他控件,例如按钮你会发现一种风格)。
现在,要解决您的特定问题,我认为最简单的方法是为 ContentTogger 定义样式,但为 ButtonBase 定义模板(您可以将其移出单独的资源并重新使用):
<Style TargetType="{x:Type c:ContentToggle}">
<Setter Property="Template">
<Setter.Value>
<!-- move this template out to separate resource -->
<ControlTemplate TargetType="{x:Type ButtonBase}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
看了开头对你的问题的介绍,我建议采用以下方法。这只是一个给出基本概念的示例。
Generic.xaml
<Style x:Key="CommonStyle" TargetType="{x:Type Button}">
<Setter Property="Background" Value="Yellow"/>
<Setter Property="FontSize" Value="25"/>
</Style>
<Style TargetType="{x:Type local:CustomButton}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:CustomButton}">
<Border Background="{TemplateBinding Background}" CornerRadius="10"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<Button Style="{StaticResource CommonStyle}" Content="{TemplateBinding Content}" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
CustomButton.cs
public class CustomButton : ToggleButton
{
static CustomButton()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(CustomButton), new FrameworkPropertyMetadata(typeof(CustomButton)));
}
}
看看这是否能解决您的问题,否则我会尝试改进这个答案。
Objective
我有一个针对 ToggleButton
的自定义控件,它也适用于 Button
。所以我想对这两种类型使用通用的默认值 ControlTemplate
。
我尝试的策略是在模板中设置 TargetType="{x:Type ButtonBase}"
,如果在 Button
或 ToggleButton
.
隐式
自定义控件库
在项目根目录的 Themes 文件夹中名为 Generic.xaml 的资源字典中...
<Style TargetType="{x:Type ButtonBase}">
<Setter Property="Template">
<Setter.Value>
<!--Modified Control Template-->
<ControlTemplate TargetType="{x:Type ButtonBase}">
在控件的 class 中,我使用 FrameworkElement.DefaultStyleKey...
在其静态构造函数中设置了用户控件类型的元数据static ContentToggle()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(ContentToggle),
new FrameworkPropertyMetadata(typeof(ButtonBase)));
}
正在使用 WPF 应用程序项目
App.xaml...
<Application x:Class="Spec.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="MainWindow.xaml">
<Application.Resources>
<ResourceDictionary>
<!--Get a reference to the window to establish View Context-->
<RelativeSource x:Key="View" Mode="FindAncestor"
AncestorType="{x:Type Window}" />
<ResourceDictionary.MergedDictionaries>
<!--Local Style-->
<ResourceDictionary Source="pack://application:,,,/ButtonStyle.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>
MainWindow.xaml...
<Window x:Class="Spec.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:b="clr-namespace:ContentToggleButton;assembly=ContentToggleButton"
Title="MainWindow" Height="350" Width="525">
<Grid>
<b:ContentToggle Name="Toggle" Height="30"
Content="{Binding options, RelativeSource={StaticResource View}}"
/>
</Grid>
</Window>
MainWindow.xaml.cs
public partial class MainWindow : Window
{
public List<string> options { get; set; }
public bool initialState { get; set; }
public MainWindow ()
{
options = new List<string> { "Checked", "UnChecked" };
initialState = false;
InitializeComponent();
}
}
还有一个名为 ButtonStyle.xaml 的文件,它定义了自定义控件要使用的画笔。它通过 App.xaml.
中的合并字典暴露在应用程序的根目录中结果
ContentToggle instance
的模板为 null,样式化控件没有视觉效果(当我窥探控件时,它没有子元素)。
我的理解是自动ButtonBase
style/template 将用于我的控制。我错过了什么?
显式
如果在控件上显式声明 style/template,自定义控件将按预期工作。以下适用于样式目标设置为 ButtonBase
...
正在使用 WPF 应用程序项目
在App.xaml...
<Application x:Class="Spec.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="MainWindow.xaml">
<Application.Resources>
<ResourceDictionary>
<!--Get a reference to the window to establish View Context-->
<RelativeSource x:Key="View" Mode="FindAncestor"
AncestorType="{x:Type Window}" />
<ResourceDictionary.MergedDictionaries>
<!--custom control-->
<ResourceDictionary Source="pack://application:,,,/ContentToggleButton;component/Themes/Generic.xaml" />
<!--Local Style-->
<ResourceDictionary Source="pack://application:,,,/ButtonStyle.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>
在MainWindow.xaml...
<Window x:Class="Spec.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:b="clr-namespace:ContentToggleButton;assembly=ContentToggleButton"
Title="MainWindow" Height="350" Width="525">
<Grid>
<b:ContentToggle Name="Toggle" Height="30"
Content="{Binding options, RelativeSource={StaticResource View}}"
Style="{DynamicResource LocalButtonStyle}"
/>
</Grid>
</Window>
在ButtonStyle.xaml...
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<!--Custom Button backgrounds-->
<LinearGradientBrush x:Key="Button.Static.Background"
EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#FF2C0606" Offset="1"/>
<GradientStop Color="#E6ADAD"/>
</LinearGradientBrush>
<LinearGradientBrush x:Key="Button.MouseOver.Background" EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#FF2C0606" Offset="1"/>
<GradientStop Color="#FFF2F2"/>
</LinearGradientBrush>
<LinearGradientBrush x:Key="Button.MouseOver.Checked.Background" EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#FF2C0606" Offset="1"/>
<GradientStop Color="#F2FFF3"/>
</LinearGradientBrush>
<LinearGradientBrush x:Key="Button.Checked.Background" EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#FF2C0606" Offset="1"/>
<GradientStop x:Name="GradientStop" Color="#ADE6B1"/>
</LinearGradientBrush>
<!--Establish the style colours-->
<SolidColorBrush x:Key="Button.Static.Border" Color="#FF707070" />
<SolidColorBrush x:Key="Button.MouseOver.Border" Color="#FF3C7FB1" />
<SolidColorBrush x:Key="Button.Pressed.Background" Color="#C4F6CE" />
<SolidColorBrush x:Key="Button.Pressed.Border" Color="#FF2C628B" />
<SolidColorBrush x:Key="Button.Disabled.Background" Color="#FFF4F4F4" />
<SolidColorBrush x:Key="Button.Disabled.Border" Color="#FFADB2B5" />
<SolidColorBrush x:Key="Button.Disabled.Foreground" Color="#FF838383" />
<!--Custom Style-->
<Style x:Key="LocalButtonStyle" TargetType="{x:Type ButtonBase}" BasedOn="{StaticResource ButtonStyle}">
<Setter Property="HorizontalAlignment" Value="Stretch" />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="HorizontalContentAlignment" Value="Center" />
<Setter Property="VerticalContentAlignment" Value="Center" />
<Setter Property="Background"
Value="{StaticResource Button.Static.Background}"/>
<Setter Property="BorderBrush"
Value="{StaticResource Button.Static.Border}"/>
</Style>
</ResourceDictionary>
解决方案结构(两种情况通用)
我不完全知道为什么,但是对于 ButtonBase 主题搜索根本不起作用(可能与它是抽象的有关)。即使您尝试使用例如 FindResource
或类似方法为 ButtonBase
找到样式 - 您也找不到任何东西,即使您在 Generic.xaml 中自己定义它(对于其他控件,例如按钮你会发现一种风格)。
现在,要解决您的特定问题,我认为最简单的方法是为 ContentTogger 定义样式,但为 ButtonBase 定义模板(您可以将其移出单独的资源并重新使用):
<Style TargetType="{x:Type c:ContentToggle}">
<Setter Property="Template">
<Setter.Value>
<!-- move this template out to separate resource -->
<ControlTemplate TargetType="{x:Type ButtonBase}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
看了开头对你的问题的介绍,我建议采用以下方法。这只是一个给出基本概念的示例。
Generic.xaml
<Style x:Key="CommonStyle" TargetType="{x:Type Button}">
<Setter Property="Background" Value="Yellow"/>
<Setter Property="FontSize" Value="25"/>
</Style>
<Style TargetType="{x:Type local:CustomButton}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:CustomButton}">
<Border Background="{TemplateBinding Background}" CornerRadius="10"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<Button Style="{StaticResource CommonStyle}" Content="{TemplateBinding Content}" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
CustomButton.cs
public class CustomButton : ToggleButton
{
static CustomButton()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(CustomButton), new FrameworkPropertyMetadata(typeof(CustomButton)));
}
}
看看这是否能解决您的问题,否则我会尝试改进这个答案。