[WPF][Rubyer] 写一个简单的 UI 库 (一) - Button
前言
写一套属于自己的简单 WPF UI 库
主要参考了下面的开源项目:
MaterialDesignInXamlToolkit:https://github.com/MaterialDesignInXAML/MaterialDesignInXamlToolkit
PanuonUI:https://github.com/Panuon/PanuonUI
1.创建项目
Visual Studio 2019 创建基于 WPF App(.NET Core) 的应用程序 和 WPF Custom Control Library(.NET Core) 的项目;
删除掉 控件库自动生成的 CustomControl1.cs ,去掉 Generic.xaml 里自动生成的内容;
Generic.xaml 里引用 Button.xaml
<ResourceDictionary.MergedDictionaries> <ResourceDictionary Source="pack://application:,,,/Rubyer;component/Themes/Button.xaml" /> </ResourceDictionary.MergedDictionaries>
2.添加文件
控件库 Themes 文件夹里添加 资源词典 文件 StyleBase.xaml 和 Button.xaml
StyleBase.xaml:用于定义全局的颜色画刷和常量等
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:system="clr-namespace:System;assembly=mscorlib"> <!--Brush--> <SolidColorBrush x:Key="Light" Color="#6EC6FF"/> <SolidColorBrush x:Key="LightForeground" Color="#000000"/> <SolidColorBrush x:Key="Primary" Color="#2196F3"/> <SolidColorBrush x:Key="PrimaryForeground" Color="#FFFFFF"/> <SolidColorBrush x:Key="Dark" Color="#0069C0"/> <SolidColorBrush x:Key="DarkForeground" Color="#FFFFFF"/> <SolidColorBrush x:Key="Accent" Color="#F50057"/> <SolidColorBrush x:Key="AccentForeground" Color="#FFFFFF"/> <SolidColorBrush x:Key="BackgroundGray" Color="#BDBDBD" Opacity="0.3"/> <!--Opacity--> <system:Double x:Key="UnenabledOpacity">0.25</system:Double> <system:Double x:Key="MouseOverOpacity">0.7</system:Double> <system:Double x:Key="PressedOpacity">1.0</system:Double> </ResourceDictionary>
回到 RubyerDemo 程序 这边,引用 Rubyer 主题项目,App.xaml 里添加 对 Rubyer 主题 Generic.xam 的引用
<Application.Resources> <ResourceDictionary> <ResourceDictionary.MergedDictionaries> <ResourceDictionary Source="pack://application:,,,/Rubyer;component/Themes/Generic.xaml" /> </ResourceDictionary.MergedDictionaries> </ResourceDictionary> </Application.Resources>
3.修改模板
MainWindow.xaml 添加一个 Buttton ,选中 Button 右键 => 编辑模板 => 编辑副本,定义位置选=> 资源词典 => Rubyer 主题里的 Button.xaml
Button.xaml 的模板可根据个人定义修改,我修改成以下
<ResourceDictionary.MergedDictionaries> <ResourceDictionary Source="pack://application:,,,/Rubyer;component/Themes/BaseStyle.xaml" /> </ResourceDictionary.MergedDictionaries> <converter:HalfTheValueConverter x:Key="halfTheValue"/> <Style x:Key="FocusVisual"> <Setter Property="Control.Template"> <Setter.Value> <ControlTemplate> <Rectangle Margin="2" SnapsToDevicePixels="true" Stroke="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" StrokeThickness="1" StrokeDashArray="1 2"/> </ControlTemplate> </Setter.Value> </Setter> </Style> <!--按钮基础--> <Style x:Key="RubyerButton" TargetType="{x:Type Button}"> <Setter Property="FocusVisualStyle" Value="{StaticResource FocusVisual}"/> <Setter Property="BorderBrush" Value="Transparent"/> <Setter Property="BorderThickness" Value="1"/> <Setter Property="HorizontalContentAlignment" Value="Center"/> <Setter Property="VerticalContentAlignment" Value="Center"/> <Setter Property="Padding" Value="1"/> <Setter Property="Height" Value="30"/> <Setter Property="Cursor" Value="Hand"/> <Setter Property="rubyer:ButtonHelper.CornerRadius" Value="3"/> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type Button}"> <Border x:Name="border" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" SnapsToDevicePixels="true" CornerRadius="{Binding Path=(rubyer:ButtonHelper.CornerRadius),RelativeSource={RelativeSource Mode=TemplatedParent}}" > <ContentPresenter x:Name="contentPresenter" Focusable="False" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/> </Border> <ControlTemplate.Triggers> <Trigger Property="IsDefaulted" Value="true"> <Setter Property="BorderBrush" TargetName="border" Value="{DynamicResource Accent}"/> </Trigger> <Trigger Property="IsMouseOver" Value="true"> <Setter Property="Opacity" TargetName="border" Value="{DynamicResource MouseOverOpacity}"/> </Trigger> <Trigger Property="IsPressed" Value="true"> <Setter Property="Opacity" TargetName="border" Value="{DynamicResource PressedOpacity}"/> </Trigger> <Trigger Property="IsEnabled" Value="false"> <Setter Property="Opacity" TargetName="border" Value="{DynamicResource UnenabledOpacity}"/> </Trigger> </ControlTemplate.Triggers> </ControlTemplate> </Setter.Value> </Setter> </Style> <!--默认按钮--> <Style TargetType="{x:Type Button}" BasedOn="{StaticResource RubyerButton}"> <Setter Property="Background" Value="{DynamicResource Primary}"/> <Setter Property="Foreground" Value="{DynamicResource PrimaryForeground}"/> </Style> <!--亮色按钮--> <Style x:Key="LightButton" TargetType="{x:Type Button}" BasedOn="{StaticResource RubyerButton}"> <Setter Property="Background" Value="{DynamicResource Light}"/> <Setter Property="Foreground" Value="{DynamicResource LightForeground}"/> </Style> <!--暗色按钮--> <Style x:Key="DarkButton" TargetType="{x:Type Button}" BasedOn="{StaticResource RubyerButton}"> <Setter Property="Background" Value="{DynamicResource Dark}"/> <Setter Property="Foreground" Value="{DynamicResource DarkForeground}"/> </Style> <!--强调色按钮--> <Style x:Key="AccentButton" TargetType="{x:Type Button}" BasedOn="{StaticResource RubyerButton}"> <Setter Property="Background" Value="{DynamicResource Accent}"/> <Setter Property="Foreground" Value="{DynamicResource AccentForeground}"/> </Style> <!--文字按钮--> <Style x:Key="TextMidButton" TargetType="{x:Type Button}" BasedOn="{StaticResource RubyerButton}"> <Setter Property="Background" Value="Transparent"/> <Setter Property="Foreground" Value="{DynamicResource Primary}"/> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type Button}"> <Border x:Name="border" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" SnapsToDevicePixels="true" CornerRadius="{Binding Path=(rubyer:ButtonHelper.CornerRadius),RelativeSource={RelativeSource Mode=TemplatedParent}}" > <ContentPresenter x:Name="contentPresenter" Focusable="False" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/> </Border> <ControlTemplate.Triggers> <Trigger Property="IsDefaulted" Value="true"> <Setter Property="BorderBrush" TargetName="border" Value="{DynamicResource Accent}"/> </Trigger> <Trigger Property="IsMouseOver" Value="true"> <Setter Property="Opacity" TargetName="border" Value="{DynamicResource MouseOverOpacity}"/> <Setter Property="Background" TargetName="border" Value="{StaticResource BackgroundGray}"/> </Trigger> <Trigger Property="IsPressed" Value="true"> <Setter Property="Opacity" TargetName="border" Value="{DynamicResource PressedOpacity}"/> <Setter Property="Background" TargetName="border" Value="{StaticResource BackgroundGray}"/> </Trigger> <Trigger Property="IsEnabled" Value="false"> <Setter Property="Opacity" TargetName="border" Value="{DynamicResource UnenabledOpacity}"/> </Trigger> </ControlTemplate.Triggers> </ControlTemplate> </Setter.Value> </Setter> </Style> <!--文字亮色按钮--> <Style x:Key="TextLightButton" TargetType="{x:Type Button}" BasedOn="{StaticResource TextMidButton}"> <Setter Property="Background" Value="Transparent"/> <Setter Property="Foreground" Value="{DynamicResource Light}"/> </Style> <!--文字暗色按钮--> <Style x:Key="TextDarkButton" TargetType="{x:Type Button}" BasedOn="{StaticResource TextMidButton}"> <Setter Property="Background" Value="Transparent"/> <Setter Property="Foreground" Value="{DynamicResource Dark}"/> </Style> <!--文字强调色按钮--> <Style x:Key="TextAccentButton" TargetType="{x:Type Button}" BasedOn="{StaticResource TextMidButton}"> <Setter Property="Background" Value="Transparent"/> <Setter Property="Foreground" Value="{DynamicResource Accent}"/> </Style> <!--圆形按钮--> <Style x:Key="CircleMidButton" TargetType="{x:Type Button}" BasedOn="{StaticResource RubyerButton}"> <Setter Property="Background" Value="{DynamicResource Primary}"/> <Setter Property="Foreground" Value="{DynamicResource PrimaryForeground}"/> <Setter Property="rubyer:ButtonHelper.CircleDima" Value="30"/> <Setter Property="Height" Value="{Binding Path=(rubyer:ButtonHelper.CircleDima),RelativeSource={RelativeSource Mode=Self}}"/> <Setter Property="Width" Value="{Binding Path=(rubyer:ButtonHelper.CircleDima),RelativeSource={RelativeSource Mode=Self}}"/> <Setter Property="rubyer:ButtonHelper.CornerRadius" Value="{Binding Path=(rubyer:ButtonHelper.CircleDima),RelativeSource={RelativeSource Mode=Self},Converter={StaticResource halfTheValue}}"/> </Style> <!--圆形亮色按钮--> <Style x:Key="CircleLightButton" TargetType="{x:Type Button}" BasedOn="{StaticResource CircleMidButton}"> <Setter Property="Background" Value="{DynamicResource Light}"/> <Setter Property="Foreground" Value="{DynamicResource LightForeground}"/> </Style> <!--圆形暗色按钮--> <Style x:Key="CircleDarkButton" TargetType="{x:Type Button}" BasedOn="{StaticResource CircleMidButton}"> <Setter Property="Background" Value="{DynamicResource Dark}"/> <Setter Property="Foreground" Value="{DynamicResource DarkForeground}"/> </Style> <!--圆形强调色按钮--> <Style x:Key="CircleAccentButton" TargetType="{x:Type Button}" BasedOn="{StaticResource CircleMidButton}"> <Setter Property="Background" Value="{DynamicResource Accent}"/> <Setter Property="Foreground" Value="{DynamicResource AccentForeground}"/> </Style>
HalfTheValueConverter.cs: 为取值一半的转换器,主要用于制作圆型按钮
public class HalfTheValueConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { return ((double)value) / 2; } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { return default; } }
ButtonHelper.cs: 为 Button 提供圆角 和 圆直径 的附加属性
public static class ButtonHelper { // 圆角半径 public static readonly DependencyProperty CornerRadiusProperty = DependencyProperty.RegisterAttached( "CornerRadius", typeof(CornerRadius), typeof(ButtonHelper)); public static void SetCornerRadius(DependencyObject element, CornerRadius value) { element.SetValue(CornerRadiusProperty, value); } public static CornerRadius GetCornerRadius(DependencyObject element) { return (CornerRadius)element.GetValue(CornerRadiusProperty); } // 圆型按钮直径 public static readonly DependencyProperty CircleDimaProperty = DependencyProperty.RegisterAttached( "CircleDima", typeof(double), typeof(ButtonHelper)); public static void SetCircleDima(DependencyObject element, double value) { element.SetValue(CircleDimaProperty, value); } public static double GetCircleDima(DependencyObject element) { return (double)element.GetValue(CircleDimaProperty); } }
最后主题项目文件包含如下:
4.Demo 项目里添加 各种按钮的 Style 的 Button
<Grid> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/> </Grid.RowDefinitions> <GroupBox Header="普通按钮"> <StackPanel Orientation="Horizontal" Margin="10"> <Button Width="100" IsDefault="True">普通按钮</Button> <Button Width="100" Style="{DynamicResource LightButton}">亮色按钮</Button> <Button Width="100" Style="{DynamicResource DarkButton}">暗色按钮</Button> <Button Width="100" Style="{DynamicResource AccentButton}">强调按钮</Button> </StackPanel> </GroupBox> <GroupBox Header="圆角按钮" Grid.Row="1"> <StackPanel Orientation="Horizontal" Margin="10"> <Button Width="100" rubyer:ButtonHelper.CornerRadius="15">普通按钮</Button> <Button Width="100" Style="{DynamicResource LightButton}" rubyer:ButtonHelper.CornerRadius="15">亮色按钮</Button> <Button Width="100" Style="{DynamicResource DarkButton}" rubyer:ButtonHelper.CornerRadius="15">暗色按钮</Button> <Button Width="100" Style="{DynamicResource AccentButton}" rubyer:ButtonHelper.CornerRadius="15">强调按钮</Button> </StackPanel> </GroupBox> <GroupBox Header="文字按钮" Grid.Row="2"> <StackPanel Orientation="Horizontal" Margin="10"> <Button Width="100" Style="{DynamicResource TextMidButton}">文字按钮</Button> <Button Width="100" Style="{DynamicResource TextLightButton}">文字按钮</Button> <Button Width="100" Style="{DynamicResource TextDarkButton}">文字按钮</Button> <Button Width="100" Style="{DynamicResource TextAccentButton}">文字按钮</Button> </StackPanel> </GroupBox> <GroupBox Header="圆型按钮" Grid.Row="3"> <StackPanel Orientation="Horizontal" Margin="10"> <Button Style="{DynamicResource CircleMidButton}"> <rubyer:Icon Type="Cookie"/> </Button> <Button Style="{DynamicResource CircleLightButton}" rubyer:ButtonHelper.CircleDima="40"> <rubyer:Icon Type="Wechat" Height="30" Width="30"/> </Button> <Button Style="{DynamicResource CircleDarkButton}" rubyer:ButtonHelper.CircleDima="50"> <rubyer:Icon Type="GithubCircle" Height="40" Width="40"/> </Button> <Button Style="{DynamicResource CircleAccentButton}" rubyer:ButtonHelper.CircleDima="60"> <rubyer:Icon Type="Cookie" Height="50" Width="50"/> </Button> </StackPanel> </GroupBox> <GroupBox Header="图标按钮" Grid.Row="4"> <StackPanel Orientation="Horizontal" Margin="10"> <Button Style="{DynamicResource TextMidButton}"> <rubyer:Icon Type="WindowMinimize"/> </Button> <Button Style="{DynamicResource TextMidButton}"> <rubyer:Icon Type="WindowMaximize"/> </Button> <Button Style="{DynamicResource TextMidButton}"> <rubyer:Icon Type="WindowRestore"/> </Button> <Button Style="{DynamicResource TextMidButton}"> <rubyer:Icon Type="WindowClose"/> </Button> </StackPanel> </GroupBox> </Grid>
5.附上运行效果图:
6.最后,下期介绍怎么将 iconfont 的图标添加到 WPF 的枚举里使用,谢谢观看。