WinUI 3自定义按钮主题咨询:适配明暗主题与多样式区分
WinUI 3 自定义主题按钮实现方案
你不需要创建全新的自定义Button控件,通过主题资源配置+修改控件模板,再配合轻量的附加属性,就能实现你要的效果,同时支持明暗主题自动切换。以下是具体步骤:
1. 定义明暗主题对应的颜色资源
在App.xaml或单独的资源字典文件中,为Primary、Secondary按钮的各个状态(正常、悬停、点击等)定义不同主题下的颜色:
<ResourceDictionary> <ResourceDictionary.ThemeDictionaries> <!-- 浅色主题 --> <ResourceDictionary x:Key="Light"> <!-- Primary按钮颜色 --> <SolidColorBrush x:Key="PrimaryButtonBackground" Color="#0078D4"/> <SolidColorBrush x:Key="PrimaryButtonForeground" Color="White"/> <SolidColorBrush x:Key="PrimaryButtonHoverBackground" Color="#106EBE"/> <SolidColorBrush x:Key="PrimaryButtonPressedBackground" Color="#005A9E"/> <SolidColorBrush x:Key="PrimaryButtonBorderBrush" Color="#0078D4"/> <!-- Secondary按钮颜色 --> <SolidColorBrush x:Key="SecondaryButtonBackground" Color="#EDEDED"/> <SolidColorBrush x:Key="SecondaryButtonForeground" Color="#1F1F1F"/> <SolidColorBrush x:Key="SecondaryButtonHoverBackground" Color="#DCDCDC"/> <SolidColorBrush x:Key="SecondaryButtonPressedBackground" Color="#C5C5C5"/> <SolidColorBrush x:Key="SecondaryButtonBorderBrush" Color="#EDEDED"/> </ResourceDictionary> <!-- 深色主题 --> <ResourceDictionary x:Key="Dark"> <!-- Primary按钮颜色 --> <SolidColorBrush x:Key="PrimaryButtonBackground" Color="#0078D4"/> <SolidColorBrush x:Key="PrimaryButtonForeground" Color="White"/> <SolidColorBrush x:Key="PrimaryButtonHoverBackground" Color="#106EBE"/> <SolidColorBrush x:Key="PrimaryButtonPressedBackground" Color="#005A9E"/> <SolidColorBrush x:Key="PrimaryButtonBorderBrush" Color="#0078D4"/> <!-- Secondary按钮颜色 --> <SolidColorBrush x:Key="SecondaryButtonBackground" Color="#323232"/> <SolidColorBrush x:Key="SecondaryButtonForeground" Color="#EDEDED"/> <SolidColorBrush x:Key="SecondaryButtonHoverBackground" Color="#404040"/> <SolidColorBrush x:Key="SecondaryButtonPressedBackground" Color="#4A4A4A"/> <SolidColorBrush x:Key="SecondaryButtonBorderBrush" Color="#323232"/> </ResourceDictionary> </ResourceDictionary.ThemeDictionaries> </ResourceDictionary>
2. 自定义Button样式(控件模板)
复制WinUI 3默认的Button模板,替换其中的颜色引用为上面定义的主题资源,分别创建Primary和Secondary按钮的样式:
Primary按钮样式
<Style x:Key="PrimaryButtonStyle" TargetType="Button"> <Setter Property="Background" Value="{ThemeResource PrimaryButtonBackground}"/> <Setter Property="Foreground" Value="{ThemeResource PrimaryButtonForeground}"/> <Setter Property="BorderBrush" Value="{ThemeResource PrimaryButtonBorderBrush}"/> <Setter Property="BorderThickness" Value="1"/> <Setter Property="Padding" Value="8,4"/> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="Button"> <Grid x:Name="RootGrid" Background="{TemplateBinding Background}"> <VisualStateManager.VisualStateGroups> <VisualStateGroup x:Name="CommonStates"> <VisualState x:Name="Normal"/> <VisualState x:Name="PointerOver"> <Storyboard> <ObjectAnimationUsingKeyFrames Storyboard.TargetName="RootGrid" Storyboard.TargetProperty="Background"> <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource PrimaryButtonHoverBackground}"/> </ObjectAnimationUsingKeyFrames> </Storyboard> </VisualState> <VisualState x:Name="Pressed"> <Storyboard> <ObjectAnimationUsingKeyFrames Storyboard.TargetName="RootGrid" Storyboard.TargetProperty="Background"> <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource PrimaryButtonPressedBackground}"/> </ObjectAnimationUsingKeyFrames> </Storyboard> </VisualState> <!-- 可补充Disabled等其他状态的样式 --> </VisualStateGroup> </VisualStateManager.VisualStateGroups> <ContentPresenter x:Name="ContentPresenter" Content="{TemplateBinding Content}" ContentTemplate="{TemplateBinding ContentTemplate}" Foreground="{TemplateBinding Foreground}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" Padding="{TemplateBinding Padding}"/> </Grid> </ControlTemplate> </Setter.Value> </Setter> </Style>
Secondary按钮样式
类似Primary样式,只需将所有资源引用替换为Secondary对应的键即可。
3. 关联type属性与样式
原生Button没有type属性,我们可以通过附加属性实现这个语法:
编写附加属性代码
using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; namespace YourAppNamespace { public static class ButtonExtensions { public static readonly DependencyProperty TypeProperty = DependencyProperty.RegisterAttached( "Type", typeof(string), typeof(ButtonExtensions), new PropertyMetadata(null, OnTypeChanged)); public static string GetType(Button button) { return (string)button.GetValue(TypeProperty); } public static void SetType(Button button, string value) { button.SetValue(TypeProperty, value); } private static void OnTypeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { if (d is Button button) { button.Style = e.NewValue switch { "Primary" => (Style)button.Resources["PrimaryButtonStyle"], "Secondary" => (Style)button.Resources["SecondaryButtonStyle"], _ => null // 恢复默认样式 }; } } } }
在XAML中使用
添加命名空间引用后,即可用type语法设置按钮:
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:YourAppNamespace"> <StackPanel> <Button local:ButtonExtensions.Type="Primary">Action1</Button> <Button local:ButtonExtensions.Type="Secondary">Action2</Button> </StackPanel> </Page>
核心原理说明
- 用
ThemeResource引用颜色资源,系统会自动根据当前应用的明暗主题切换对应的颜色值; - 自定义样式基于原生Button模板修改,保留了Button的所有原生行为,无需从零创建控件;
- 附加属性实现了
type语法的快捷设置,避免了每次手动指定Style的繁琐。
内容的提问来源于stack exchange,提问作者holycreeper
相关产品推荐
相关产品推荐

