今天我们开始制作我们的按钮,主要的效果就是一个按钮正常状态、鼠标滑过、按下三态显示不同的图片。

首先我们需要给扩展按钮添加三个属性,分别是正常状态图片,鼠标滑过图片,按钮按下图片。

 先贴出ButtonEx类

    public class ButtonEx : Button
    {
        
        static ButtonEx()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(ButtonEx), new FrameworkPropertyMetadata(typeof(ButtonEx)));
        }


        [CategoryAttribute("自定义属性"),DescriptionAttribute("获取或设置默认的背景图片")]
        public ImageSource NormalImage
        {
            get { return (ImageSource)GetValue(NormalImageProperty); }
            set { SetValue(NormalImageProperty, value); }
        }

        // Using a DependencyProperty as the backing store for NormalImage.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty NormalImageProperty =
            DependencyProperty.Register("NormalImage", typeof(ImageSource), typeof(ButtonEx), null);


        [CategoryAttribute("自定义属性"),DescriptionAttribute("获取或设置鼠标滑过时的背景图片")]
        public ImageSource MouseOverImage
        {
            get { return (ImageSource)GetValue(MouseOverImageProperty); }
            set { SetValue(MouseOverImageProperty, value); }
        }

        // Using a DependencyProperty as the backing store for MouseOverImage.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty MouseOverImageProperty =
            DependencyProperty.Register("MouseOverImage", typeof(ImageSource), typeof(ButtonEx), null);


        [CategoryAttribute("自定义属性"),DescriptionAttribute("获取或设置鼠按钮按下时的背景图片")]
        public ImageSource PressImage
        {
            get { return (ImageSource)GetValue(PressImageProperty); }
            set { SetValue(PressImageProperty, value); }
        }

        // Using a DependencyProperty as the backing store for PressImage.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty PressImageProperty =
            DependencyProperty.Register("PressImage", typeof(ImageSource), typeof(ButtonEx), null);

        
        

        [Browsable(false)]
        public new Brush Background
        {
            get { return base.Background; }
            set { base.Background = value; }
        }

        [Browsable(false)]
        public new Brush BorderBrush
        {
            get { return base.BorderBrush; }
            set { base.BorderBrush = value; }
        }
    }

我们这里定义了主要的三个属性

下面通过xaml来定义样式

<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:WpfCustomControlLibrary1">
    <Style TargetType="{x:Type local:ButtonEx}">
        <!--Setter是一个设置器,用来设置该Style要对TargetType的哪些属性或对象进行设置,所以这里设置了ButtonEx属性NormalImage属性设置了一张图片-->
        <Setter Property="NormalImage" Value="/WpfCustomControlLibrary1;component/Images/button_normal.png"></Setter>
        <Setter Property="MouseOverImage" Value="/WpfCustomControlLibrary1;component/Images/button_hover.png"></Setter>
        <Setter Property="PressImage" Value="/WpfCustomControlLibrary1;component/Images/button_pushed.png"></Setter>
        <Setter Property="Width" Value="72"/>
        <Setter Property="Height"  Value="28"/>
        <!--Template属性决定了将来ButtonEx的样式,该属性是ControlTemplate类型的-->
        <Setter Property="Template">
            <Setter.Value>
                <!--实例化一个ControlTemplate实体,赋值给Template属性-->
                <ControlTemplate TargetType="{x:Type local:ButtonEx}">
                    <Grid>
                        <!--用于指定控件处于特定状态时如何更改控件的外观,这是一组外观变化-->
                        <VisualStateManager.VisualStateGroups>
                            <VisualStateGroup x:Name="CommonStates">
                                <!--设置状态变化所需要的时间-->
                                <VisualStateGroup.Transitions>
                                    <!--需要声明从什么状态变为另一种状态及状态变化所需要的时间-->
                                    <VisualTransition From="Noraml" To="MouseOver" GeneratedDuration="00:00:0.15" />
                                    <VisualTransition From="MouseOver" To=" Normal" GeneratedDuration="00:00:0.15"/>
                                    <VisualTransition From="Pressed" To="MouseOver" GeneratedDuration="00:00:0.15"/>
                                </VisualStateGroup.Transitions>
                                <!--定义状态,自然状态,一个状态下需要几个特性,需要使用动画板来控制-->
                                <VisualState x:Name="Normal">
                                    <!--故事板能够用TargetProperty和TargetName属性指向一个特定的属性和特定的元素,
                                        故事版在动画和希望应用动画的属性之间架起了一座桥梁,同一个故事版中放置几个动画,
                                        并且每个动画可以用于不同的元素和属性-->
                                    <!--一个控制板将属性和动画联系在一起,当为自然状态下,将自然状态下的图片Opacity属性设置为1,然后将鼠标滑过和按下状态Opacity设置成0,
                                         注意:一定要将后两种状态Opacity设置成0,不然有可能是1,这样可能得到我们不想要的效果-->
                                    <Storyboard>
                                        <!--动画有两种,一种是在一个开始值和结束值之间以逐步增加的方式(被称为线性插值过程)改变属性的动画,
                                            以及从一个值突然变成另一个值的动画,第一种很好理解,第二种都是使用关键帧动画的技术,多数以
                                            "类型名+AnimationUsingKeyFrames"的形式命名的,下面使用的就是这种动画类型-->
                                        <DoubleAnimationUsingKeyFrames Storyboard.TargetName="PART_Normal" Storyboard.TargetProperty="(Opacity)">
                                            <!--获取或设置应到达关键帧的目标 System.Windows.Media.Animation.DoubleKeyFrame.Value 的时间。获取或设置关键帧的目标值。-->
                                            <EasingDoubleKeyFrame KeyTime="0" Value="1"></EasingDoubleKeyFrame>
                                        </DoubleAnimationUsingKeyFrames>
                                        <DoubleAnimationUsingKeyFrames Storyboard.TargetName="PART_MouseOver" Storyboard.TargetProperty="Opacity">
                                            <EasingDoubleKeyFrame KeyTime="0" Value="0"></EasingDoubleKeyFrame>
                                        </DoubleAnimationUsingKeyFrames>
                                        <DoubleAnimationUsingKeyFrames Storyboard.TargetName="PART_Pressed" Storyboard.TargetProperty="Opacity">
                                            <EasingDoubleKeyFrame KeyTime="0" Value="0"></EasingDoubleKeyFrame>
                                        </DoubleAnimationUsingKeyFrames>
                                    </Storyboard>
                                </VisualState>
                                <!--鼠标按下状态-->
                                <VisualState x:Name="Pressed">
                                    <Storyboard>
                                        <DoubleAnimationUsingKeyFrames Storyboard.TargetName="PART_Pressed" Storyboard.TargetProperty="Opacity">
                                            <EasingDoubleKeyFrame KeyTime="0" Value="1">
                                            </EasingDoubleKeyFrame>
                                        </DoubleAnimationUsingKeyFrames>
                                        <DoubleAnimationUsingKeyFrames Storyboard.TargetName="PART_Normal" Storyboard.TargetProperty="Opacity">
                                            <EasingDoubleKeyFrame KeyTime="0" Value="0">
                                            </EasingDoubleKeyFrame>
                                        </DoubleAnimationUsingKeyFrames>
                                        <DoubleAnimationUsingKeyFrames Storyboard.TargetName="PART_MouseOver" Storyboard.TargetProperty="Opacity">
                                            <EasingDoubleKeyFrame KeyTime="0" Value="0"></EasingDoubleKeyFrame>
                                        </DoubleAnimationUsingKeyFrames>
                                    </Storyboard>
                                </VisualState>
                                <VisualState x:Name="MouseOver">
                                    <Storyboard>
                                        <DoubleAnimationUsingKeyFrames Storyboard.TargetName="PART_MouseOver" Storyboard.TargetProperty="Opacity">
                                            <EasingDoubleKeyFrame KeyTime="0" Value="1"></EasingDoubleKeyFrame>
                                        </DoubleAnimationUsingKeyFrames>
                                        <DoubleAnimationUsingKeyFrames Storyboard.TargetName="PART_Normal" Storyboard.TargetProperty="Opacity">
                                            <EasingDoubleKeyFrame KeyTime="0" Value="0"/>
                                        </DoubleAnimationUsingKeyFrames>
                                        <DoubleAnimationUsingKeyFrames Storyboard.TargetName="PART_Pressed" Storyboard.TargetProperty="Opacity">
                                            <EasingDoubleKeyFrame KeyTime="0" Value="0"/>
                                        </DoubleAnimationUsingKeyFrames>
                                    </Storyboard>
                                </VisualState>
                            </VisualStateGroup>
                        </VisualStateManager.VisualStateGroups>
                        <!--这里放了一张图片元素,所以正常情况下,NormalImage属性图片会显示到按钮上-->
                        <Image Source="{TemplateBinding NormalImage}" Name="PART_Normal"></Image>
                        <Image Source="{TemplateBinding PressImage}" Name="PART_Pressed"></Image>
                        <Image Source="{TemplateBinding MouseOverImage}" Name="PART_MouseOver"></Image>
                        <Border Background="{TemplateBinding Background}"
                            BorderBrush="{TemplateBinding BorderBrush}"
                            BorderThickness="{TemplateBinding BorderThickness}">
                        </Border>
                        <!--绑定了ButtonEx的Content属性-->
                        <ContentPresenter Content="{TemplateBinding Content}"></ContentPresenter>
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

所有的难以理解的都加上注释了