在上一篇中,完成了一个自定义样式的按钮,下面在这个 基础上再继续编写更多样式~。

本篇实现三种按钮,带图片的按钮,文本按钮(类似超链接),和等待按钮。

效果如下图:

WPF 自定义Button样式(续)_c#

git地址:https://github.com/gxygit/WpfTemplate

有兴趣的小伙伴可以下载查看哦

图片按钮样式如下 :



 <Style x:Key="ImageButton" TargetType="{x:Type Button}">        <Setter Property="Background" Value="Transparent"/>        <Setter Property="Padding" Value="10 2"/>        <Setter Property="BorderThickness" Value="0"/>        <Setter Property="Margin" Value="10"/>        <Setter Property="FontSize" Value="{StaticResource FontSizeRegular}"/>        <Setter Property="Template" >            <Setter.Value>                <ControlTemplate TargetType="{x:Type ButtonBase}">                    <Border x:Name="back"                            Background="{TemplateBinding Background}"                            BorderBrush="Black"                            BorderThickness="0.8"                            CornerRadius="3"                            Padding="{TemplateBinding Padding}">                        <StackPanel Orientation="Horizontal"                                Height="{TemplateBinding Height}"                                HorizontalAlignment="Center"                                    VerticalAlignment="Center">                            <Image Source="/Images/head.jpg" Height="{Binding ActualHeight,ElementName=text}"                               Margin="0 0 5 0"                               Stretch="UniformToFill" />                            <TextBlock Text="{TemplateBinding Content}"                                   FontSize="{TemplateBinding FontSize}"                                   FontFamily="{TemplateBinding FontFamily}"                                   Foreground="#000000"                                   VerticalAlignment="Center"                                   x:Name="text"                                   />                        </StackPanel>                    </Border>                    <ControlTemplate.Triggers>                        <EventTrigger RoutedEvent="MouseEnter">                            <BeginStoryboard>                                <Storyboard >                                    <ColorAnimation To="#57A64A" Duration="0:0:0.3" Storyboard.TargetName="back" Storyboard.TargetProperty="Background.Color"/>                                    <ColorAnimation To="#FFFFFF" Duration="0:0:0.3" Storyboard.TargetName="text" Storyboard.TargetProperty="Foreground.Color"/>                                </Storyboard>                            </BeginStoryboard>                        </EventTrigger>                        <EventTrigger RoutedEvent="MouseLeave">                            <BeginStoryboard>                                <Storyboard >                                    <ColorAnimation To="#0057A64A" Duration="0:0:0.3" Storyboard.TargetName="back" Storyboard.TargetProperty="Background.Color"/>                                    <ColorAnimation To="#000000" Duration="0:0:0.3" Storyboard.TargetName="text" Storyboard.TargetProperty="Foreground.Color"/>                                </Storyboard>                            </BeginStoryboard>                        </EventTrigger>                    </ControlTemplate.Triggers>                </ControlTemplate>            </Setter.Value>        </Setter></Style>

文本按钮样式如下 :





 <Style x:Key="TextButton" TargetType="{x:Type Button}" >        <Setter Property="Background" Value="Transparent"/>        <Setter Property="Foreground" Value="Black"/>        <Setter Property="BorderThickness" Value="0"/>        <Setter Property="FontSize" Value="{StaticResource FontSizeRegular}"/>        <Setter Property="Padding" Value="10 2"/>        <Setter Property="Margin" Value="0 10"/>        <Setter Property="Template">            <Setter.Value>                <ControlTemplate TargetType="{x:Type ButtonBase}">                    <Border x:Name="border"                            CornerRadius="10"                            BorderBrush="{TemplateBinding BorderBrush}"                            BorderThickness="{TemplateBinding BorderThickness}"                            Background="{TemplateBinding Background}"                             SnapsToDevicePixels="True">                        <TextBlock Text="{TemplateBinding Content}"                                    Focusable="False"                                    FontFamily="{TemplateBinding FontFamily}"                                   FontSize="{TemplateBinding FontSize}"                                   HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"                                    Margin="{TemplateBinding Padding}"                                    SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"                                    VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>                    </Border>                    <ControlTemplate.Triggers>                        <Trigger Property="IsMouseOver" Value="True">                            <Setter Property="Foreground" Value="Blue"/>                        </Trigger>                        <Trigger Property="IsEnabled" Value="False">                            <Setter Property="Foreground" Value="Gray"/>                        </Trigger>                    </ControlTemplate.Triggers>                </ControlTemplate>            </Setter.Value>        </Setter></Style>

样式都统一写在了/Styles/Button.xaml里面,并在App.xaml中合并引用。

最后,等待图标,我使用了FontAwesome字体,下载链接为:

http://fontawesome.dashgame.com/

WPF 自定义Button样式(续)_ico_02

点击下载即可。下载后,找到fonts/fontawesome-webfont.ttf ,双击打开后即可看到

WPF 自定义Button样式(续)_microsoft_03

,字体名字为FontAwesome。把它复制到项目/fonts/ 目录。

添加/Styles/Fonts.xaml。在其中添加字体的引用。代码如下 






<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"                    xmlns:local="clr-namespace:WpfTemplate"                    xmlns:system="clr-namespace:System;assembly=mscorlib">    <FontFamily x:Key="WebDings" >pack://application;,,,/Fonts/#WEBDINGS</FontFamily>    <FontFamily x:Key="FontAwesome" >pack://application;,,,/Fonts/#FontAwesome</FontFamily>    <system:Double x:Key="FontSizeSmaller">10</system:Double>    <system:Double x:Key="FontSizeSmall">12</system:Double>    <system:Double x:Key="FontSizeRegular">14</system:Double>    <system:Double x:Key="FontSizeLarge">16</system:Double>    <system:Double x:Key="FontSizeXLarge">24</system:Double>    <system:Double x:Key="FontSizeXXLarge">30</system:Double>
<system:String x:Key="FontAwesomeWaitIcon">&#xf110;</system:String></ResourceDictionary>

FontAwesomeWaitIcon 既是旋转图标的代码。代码可在fontswesome官网首页查看。

使用开发者工具查看网页原代码

WPF 自定义Button样式(续)_ico_04

发现\f110 ,在xaml中引用就是 &#xf110; 

顺便写了几个字体大小的定义

准备工作完成了,下面就是等待按钮的样式了






 <Style x:Key="WaitingButton" TargetType="{x:Type Button}">        <Setter Property="Background" Value="Transparent"/>        <Setter Property="Padding" Value="10 2"/>        <Setter Property="BorderThickness" Value="0"/>        <Setter Property="Margin" Value="10"/>        <Setter Property="FontSize" Value="{StaticResource FontSizeRegular}"/>        <!--<Setter Property="local:IsBusyProperty.Value" Value="False"/>-->        <Setter Property="Template">            <Setter.Value>                <ControlTemplate TargetType="{x:Type ButtonBase}">                    <Border Width="{TemplateBinding Width}"                             Height="{TemplateBinding Height}"                             Background="{TemplateBinding Background}"                            BorderBrush="Black"                            BorderThickness="0.8"                            CornerRadius="3"                            Padding="{TemplateBinding Padding}"                            x:Name="border"                            >                        <Grid>                            <TextBlock Text="{TemplateBinding Content}"                                    VerticalAlignment="Center" HorizontalAlignment="Center"                                   Foreground="{TemplateBinding Foreground}"                                   FontSize="{TemplateBinding FontSize}"                                   Visibility="{TemplateBinding local:IsBusyProperty.Value,Converter={local:BooleanToVisiblityConverter}}"                                   FontFamily="{TemplateBinding FontFamily}"                                   x:Name="text"                                   />                            <TextBlock Style="{StaticResource SpinningText}"                                       Visibility="{TemplateBinding local:IsBusyProperty.Value,Converter={local:BooleanToVisiblityConverter},ConverterParameter=True}"                                       FontSize="{TemplateBinding FontSize}"                                       HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"                                        VerticalAlignment="{TemplateBinding VerticalContentAlignment}"                                      />                        </Grid>                    </Border>                    <ControlTemplate.Triggers>                        <EventTrigger RoutedEvent="MouseEnter">                            <BeginStoryboard>                                <Storyboard >                                    <ColorAnimation To="#57A64A" Duration="0:0:0.3" Storyboard.TargetName="border" Storyboard.TargetProperty="Background.Color"/>                                    <ColorAnimation To="#FFFFFF" Duration="0:0:0.3" Storyboard.TargetName="text" Storyboard.TargetProperty="Foreground.Color"/>                                </Storyboard>                            </BeginStoryboard>                        </EventTrigger>                        <EventTrigger RoutedEvent="MouseLeave">                            <BeginStoryboard>                                <Storyboard >                                    <ColorAnimation To="#0057A64A" Duration="0:0:0.3" Storyboard.TargetName="border" Storyboard.TargetProperty="Background.Color"/>                                    <ColorAnimation To="#000000" Duration="0:0:0.3" Storyboard.TargetName="text" Storyboard.TargetProperty="Foreground.Color"/>                                </Storyboard>                            </BeginStoryboard>                        </EventTrigger>                        <Trigger Property="IsEnabled" Value="False">                            <Setter Property="Background" TargetName="border" Value="LightGray"/>                            <Setter Property="Foreground" TargetName="text" Value="White"/>                        </Trigger>                    </ControlTemplate.Triggers>                </ControlTemplate>            </Setter.Value>        </Setter></Style>

样式中引用了一个textblock的样式 SpinningText ,样式代码写在/Styles/Texts.xaml中。代码如下 





<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"                    xmlns:local="clr-namespace:WpfTemplate"                    xmlns:system="clr-namespace:System;assembly=mscorlib">    <Style TargetType="{x:Type TextBlock}" x:Key="TextBlockBaseStyle">        <Setter Property="FontFamily" Value="{StaticResource FontAwesome}"/>        <Setter Property="FontSize" Value="{StaticResource FontSizeRegular}"/></Style>
<Style TargetType="{x:Type TextBlock}" x:Key="SpinningText" BasedOn="{StaticResource TextBlockBaseStyle}"> <Setter Property="FontFamily" Value="{StaticResource FontAwesome}"/> <Setter Property="Text" Value="{StaticResource FontAwesomeWaitIcon}"/> <Setter Property="RenderTransformOrigin" Value="0.5,0.5"/> <Setter Property="RenderTransform" > <Setter.Value> <RotateTransform/> </Setter.Value> </Setter>
<Style.Resources> <Storyboard x:Key="Spin"> <DoubleAnimation Storyboard.TargetProperty="(UIElement.RenderTransform).(RotateTransform.Angle)" From="0" To="360" Duration="0:0:2" RepeatBehavior="Forever"/> </Storyboard> </Style.Resources> <Style.Triggers> <DataTrigger Binding="{Binding RelativeSource={x:Static RelativeSource.Self},Path=IsVisible}" Value="True"> <DataTrigger.EnterActions> <BeginStoryboard Name="SpinStoryboard" Storyboard="{StaticResource Spin}"/> </DataTrigger.EnterActions> <DataTrigger.ExitActions> <RemoveStoryboard BeginStoryboardName="SpinStoryboard"/> </DataTrigger.ExitActions> </DataTrigger> </Style.Triggers></Style></ResourceDictionary>

在 WaitingButton 样式中还用到了 一个附加属性 IsBusyProperty。

为了以后定义附加属性的方便,把它封闭一下,






using System;using System.Windows;
namespace WpfTemplate{ /// <summary> /// a base attached property to replace the vanilla wpf attached property /// </summary> /// <typeparam name="Parent">the parent class to be the attached property</typeparam> /// <typeparam name="Property">the type of this attached property</typeparam> public abstract class BaseAttachedProperty<Parent, Property> where Parent : new() { #region public properties
public static Parent Instance { get; private set; } = new Parent();
#endregion
#region attached property definitions
public static readonly DependencyProperty ValueProperty = DependencyProperty.RegisterAttached("Value", typeof(Property), typeof(BaseAttachedProperty<Parent, Property>), new UIPropertyMetadata( default(Property), new PropertyChangedCallback(OnValuePropertyChanged), new CoerceValueCallback(OnValuePropertyUpdated) ));
/// <summary> /// the callback event when the <see cref="ValueProperty"/> is changed /// 值改变了才会调用 /// </summary> /// <param name="d">the ui element that had it's property changed</param> /// <param name="e">the arguments for the event</param> private static void OnValuePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e){ //call the parent function (Instance as BaseAttachedProperty<Parent, Property>)?.OnValueChanged(d, e); //call event listeners (Instance as BaseAttachedProperty<Parent, Property>)?.ValueChanged(d, e); } /// <summary> /// the callback event when the <see cref="ValueProperty"/> is changed /// 每次设置值 即使值和本次设置之前的相同也会调用 /// </summary> /// <param name="d">the ui element that had it's property changed</param> /// <param name="e">the arguments for the event</param> private static object OnValuePropertyUpdated(DependencyObject d, object value){ //call the parent function (Instance as BaseAttachedProperty<Parent, Property>)?.OnValueUpdated(d, value); //call event listeners (Instance as BaseAttachedProperty<Parent, Property>)?.ValueUpdated(d, value);
return value; } /// <summary> /// get the attached property /// </summary> /// <param name="d">the element to get the property from</param> /// <returns></returns> public static Property GetValue(DependencyObject d) => (Property)d.GetValue(ValueProperty);
/// <summary> /// set the attached property /// </summary> /// <param name="d">the element to set the property</param> /// <param name="value">the value to set the property</param> public static void SetValue(DependencyObject d, Property value) => d.SetValue(ValueProperty, value); #endregion
#region public events /// <summary> /// 当值改变时会引发事件 /// </summary> public event Action<DependencyObject, DependencyPropertyChangedEventArgs> ValueChanged = (sender, e) => { }; /// <summary> /// 当值改变时会引发事件,即使新值和旧值相同 /// </summary> public event Action<DependencyObject, object> ValueUpdated = (sender, value) => { }; #endregion
#region event methods /// <summary> /// 值改变事件 /// </summary> /// <param name="sender">the ui element</param> /// <param name="e">the argument of this event</param> public virtual void OnValueChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) { } /// <summary> /// 值更新事件,(同改变事件,并且再次值相同时) /// </summary> /// <param name="sender">the ui element</param> /// <param name="e">the argument of this event</param> public virtual void OnValueUpdated(DependencyObject sender, object value) { } #endregion }}

定义好之后 ,再定义附加属性只需要像下面这样添加上IsBusyProperty:









namespace WpfTemplate{    public class IsBusyProperty : BaseAttachedProperty<IsBusyProperty, bool>    {    }}

其次,还用到了一个ValueConverter.同样,先封闭一下喽,










using System;using System.Globalization;using System.Windows.Data;using System.Windows.Markup;
namespace WpfTemplate{ /// <summary> /// a base value converter that allows direct xaml usage /// </summary> /// <typeparam name="T">the type of this value converter</typeparam> public abstract class BaseValueConverter<T> : MarkupExtension, IValueConverter where T:class, new() { /// <summary> /// a single static instance of this value converter /// </summary> private static T Converter = null;
#region markup extension methods
/// <summary> /// provides a static instance of the value converter /// </summary> /// <param name="serviceProvider">the service provider</param> /// <returns></returns> public override object ProvideValue(IServiceProvider serviceProvider) { return Converter ?? (Converter = new T()); } #endregion
#region value converter methods
/// <summary> /// the method to convert one type to another /// </summary> /// <param name="value"></param> /// <param name="targetType"></param> /// <param name="parameter"></param> /// <param name="culture"></param> /// <returns></returns> public abstract object Convert(object value, Type targetType, object parameter, CultureInfo culture); /// <summary> /// converter a value back to it's source type /// </summary> /// <param name="value"></param> /// <param name="targetType"></param> /// <param name="parameter"></param> /// <param name="culture"></param> /// <returns></returns> public abstract object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture); #endregion }}

,然后,像下面这样添加一个bool->Visibilty的转换器




using System;using System.Globalization;using System.Windows;
namespace WpfTemplate{ /// <summary> /// a converter that takes in a boolean and return a <see cref="Visibility"/> /// </summary> public class BooleanToVisiblityConverter : BaseValueConverter<BooleanToVisiblityConverter> { public override object Convert(object value, Type targetType, object parameter, CultureInfo culture) { if(parameter==null) return (bool)value ? Visibility.Hidden : Visibility.Visible; else return (bool)value ? Visibility.Visible : Visibility.Hidden; }
public override object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { throw new NotImplementedException(); } }}

在MainWindowViewModel中添加一个BusyCommand, 用于改变按钮的IsBusyProperty。



using System.Windows;using System.Windows.Input;
namespace WpfTemplate{ /// <summary> /// /// </summary> public class MainWindowViewModel:BaseViewModel { private Window mWindow;//window 对象
public bool ButtonIsBusy { get; set; } public ICommand MinimizeCommand { get; set; } public ICommand MaximizeCommand { get; set; } public ICommand CloseCommand { get; set; } public ICommand BusyCommand { get; set; }


#region constructor public MainWindowViewModel(Window window) { mWindow = window; MinimizeCommand = new RelayCommand(() => mWindow.WindowState = WindowState.Minimized); MaximizeCommand = new RelayCommand(() => mWindow.WindowState ^= WindowState.Maximized); CloseCommand = new RelayCommand(() => mWindow.Close()); BusyCommand = new RelayCommand(() => ButtonIsBusy = !ButtonIsBusy); } #endregion }}

。这样,MainWindow.xaml里面就可以添加按钮了







  <Button Content="测试BusyProperty"                    Style="{StaticResource WaitingButton}"                    Command="{Binding BusyCommand}"                    local:IsBusyProperty.Value="{Binding ButtonIsBusy}"/>

按钮command绑定到BusyCommand,同时设置IsBusyProperty.Value绑定到ButtonIsBusy属性上。

好了,大功造成啦~

WPF 自定义Button样式(续)_c#_05

​​

WPF 自定义Button样式(续)_xml_06