[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 的枚举里使用,谢谢观看。

 

posted @ 2020-08-18 13:22  好想写代码啊  阅读(785)  评论(2编辑  收藏  举报