天天看點

WPF自定義控件-按鈕-DoubleAnimation動畫

WPF自定義控件-按鈕-DoubleAnimation動畫

    • 效果展示
    • 原理
    • 下面為其中一個樣式的代碼
    • 到此代碼都結束了
    • 補充上述3點我在做樣式的注意點

效果展示

WPF自定義控件-按鈕-DoubleAnimation動畫

最近在看Wpf的書,初了解Wpf,也沒做過C#和Wpf的項目。

如果下述有不妥的地方請多多包含,有錯誤,還請指教。

原理

自定義控件繼承Button,模闆為兩個重疊的Border

下面為其中一個樣式的代碼

  1. 自定義控件檔案(MyButton.cs),自定生成的代碼先暫時什麼都不用改
using System.Windows;
using System.Windows.Controls;

namespace CSDN01
{
    public class MyButton : Button
    {
        static MyButton()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(MyButton), new FrameworkPropertyMetadata(typeof(MyButton)));
        }
    }
}
                
  1. 項目2,資源字典檔案(TemplateMyButton.xaml),我暫時先把屬性設成固定值,後期再改成依賴屬性

    屬性UseLayoutRounding=“False”

    的設定是一個注意點(後續♦1處說明)

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:local="clr-namespace:CSDN01">
    <ControlTemplate x:Key="TemplateMyButton" TargetType="{x:Type local:MyButton}">
        <Grid UseLayoutRounding="False">
            <Border x:Name="bd1" Background="LightCyan">
                <TextBlock Text="按鈕" FontSize="12" Foreground="DarkCyan"
                               HorizontalAlignment="Center" VerticalAlignment="Center"/>
            </Border>
            <Border x:Name="bd2" Background="DarkCyan" Height="0">
                <TextBlock Text="按鈕" FontSize="12" Foreground="LightCyan"
                               HorizontalAlignment="Center" VerticalAlignment="Center"/>
            </Border>
        </Grid>
    </ControlTemplate>
</ResourceDictionary>
                
  1. App.xaml裡添加資源字典檔案
<Application x:Class="CSDN01.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:CSDN01"
             StartupUri="MainWindow.xaml">
    <Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="TemplateMyButton.xaml"/>
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Application.Resources>
</Application>
                
  1. 主視窗檔案(MainWindow.xaml)

    屬性UseLayoutRounding=“True”

    的設定是一個注意點(後續♦2處說明)

<Window x:Class="CSDN01.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:CSDN01"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid UseLayoutRounding="True">
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="3*"/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
        <local:MyButton Template="{StaticResource TemplateMyButton}" Grid.Column="1"/>
        <local:MyButton Template="{StaticResource TemplateMyButton}" Grid.Column="2"/>
        <local:MyButton Template="{StaticResource TemplateMyButton}" Grid.Column="3"/>
        <local:MyButton Template="{StaticResource TemplateMyButton}" Grid.Column="4"/>
    </Grid>
</Window>
                
  1. 上述步驟後,大體視窗的樣子成型
    WPF自定義控件-按鈕-DoubleAnimation動畫
  2. 現在重寫MyButton.cs檔案添加DoubleAnimation動畫

    屬性FillBehavior

    的設定是一個注意點(後續♦3處說明)

using System;
using System.Windows.Media.Animation;
                
/// <summary>
        /// 動畫對象
        /// </summary>
        private DoubleAnimation myAnimation = new DoubleAnimation();

        /// <summary>
        /// 重寫MouseEnter事件
        /// </summary>
        /// <param name="e"></param>
        protected override void OnMouseEnter(MouseEventArgs e)
        {
            base.OnMouseEnter(e);

            // 擷取MyButton内部子控件
            Border bd1 = GetTemplateChild("bd1") as Border;
            Border bd2 = GetTemplateChild("bd2") as Border;

            // 設定屬性
            myAnimation.From = 0;                             // 動畫初始值
            myAnimation.To = bd1.ActualHeight;                // 動畫終了值
            myAnimation.Duration = TimeSpan.FromSeconds(0.5); // 0.5秒内執行動畫
            myAnimation.FillBehavior = FillBehavior.HoldEnd;  // 動畫結束後,屬性值保持終了狀态

            // 在高度屬性上執行動畫
            bd2.BeginAnimation(HeightProperty, myAnimation);
        }

        /// <summary>
        /// 重寫MouseLeave事件
        /// </summary>
        /// <param name="e"></param>
        protected override void OnMouseLeave(MouseEventArgs e)
        {
            base.OnMouseLeave(e);

            // 擷取MyButton内部子控件
            Border bd1 = GetTemplateChild("bd1") as Border;
            Border bd2 = GetTemplateChild("bd2") as Border;

            // 添加并執行動畫
            myAnimation.From = bd1.ActualHeight;              // 動畫初始值
            myAnimation.To = 0;                               // 動畫終了值
            myAnimation.Duration = TimeSpan.FromSeconds(0.5); // 0.5秒内執行動畫
            myAnimation.FillBehavior = FillBehavior.Stop;     // 動畫結束後,屬性值還原為動畫出師值

            // 在高度屬性上執行動畫
            bd2.BeginAnimation(HeightProperty, myAnimation);
        }
                
  1. 上述步驟後,整個按鈕算是大體全部結束了,後續要添加依賴屬性,這樣可以設定自己想要的背景色,文字大小顔色等屬性。
    WPF自定義控件-按鈕-DoubleAnimation動畫
  2. 依賴屬性的設定(這裡就隻設定2個依賴屬性做例子)

    依賴屬性的代碼有快捷鍵:輸入propdp,然後按2次Tab鍵

    往MyButton.cs添加下面的代碼

    PropertyMetadata裡設定的是初期值

using System.Windows.Media;
                
/// <summary>
        /// 上層背景色(變動層)
        /// </summary>
        public SolidColorBrush UpBackground
        {
            get { return (SolidColorBrush)GetValue(UpBackgroundProperty); }
            set { SetValue(UpBackgroundProperty, value); }
        }
        public static readonly DependencyProperty UpBackgroundProperty =
            DependencyProperty.Register("UpBackground", typeof(SolidColorBrush), typeof(MyButton), new PropertyMetadata(new SolidColorBrush(Colors.DarkCyan)));

        /// <summary>
        /// 動畫執行時間屬性(秒)
        /// </summary>
        public double AnimationSeconds
        {
            get { return (double)GetValue(AnimationSecondsProperty); }
            set { SetValue(AnimationSecondsProperty, value); }
        }
        public static readonly DependencyProperty AnimationSecondsProperty =
            DependencyProperty.Register("AnimationSeconds", typeof(double), typeof(MyButton), new PropertyMetadata(0.2));
                

對MyButton.cs檔案再次進行修改

// 修改前
            myAnimation.Duration = TimeSpan.FromSeconds(0.5); // 0.5秒内執行動畫
            // 修改後
            myAnimation.Duration = TimeSpan.FromSeconds(AnimationSeconds); // 制定時間内執行動畫(預設0.2秒)
                

對TemplateMyButton.xaml檔案再次進行修改

<!--變更前-->
            <Border x:Name="bd2" Background="DarkCyan" Height="0">
            <!--變更後-->
            <Border x:Name="bd2" Background="{TemplateBinding UpBackground}" Height="0">
                

也可以用Button固有的屬性來設定MyButton的樣式

<!--變更前-->
            <TextBlock Text="按鈕" FontSize="12" 
            <!--變更後-->
            <TextBlock Text="{TemplateBinding Content}"
                

當然要在MainWindow.xaml檔案裡設定Content的值

<local:MyButton Template="{StaticResource TemplateMyButton}" Grid.Column="1" Content="首頁"/>
        <local:MyButton Template="{StaticResource TemplateMyButton}" Grid.Column="2" Content="産品"/>
        <local:MyButton Template="{StaticResource TemplateMyButton}" Grid.Column="3" Content="技術"/>
        <local:MyButton Template="{StaticResource TemplateMyButton}" Grid.Column="4" Content="聯系我們"/>
                

到此代碼都結束了

WPF自定義控件-按鈕-DoubleAnimation動畫

補充上述3點我在做樣式的注意點

  1. 先說注意點♦2:UseLayoutRounding=“True”

    做個小例子

<Grid Grid.Row="2" Grid.Column="1">
            <Border Background="Black"/>
            <Border Background="White"/>
        </Grid>
                

上述在一個單元格裡放至2個Border,白色覆寫在黑色上面,下面是沒有UseLayoutRounding=“True”

的運作結果

WPF自定義控件-按鈕-DoubleAnimation動畫

有2根比較明顯的底色黑線漏出來了,我這次給的MyButton例子是不會出現這個的,因為一個是隐藏的,一個是顯示的。但是如果自己想做别的動畫,需要多個重疊控件的話,就可能會出現這個現象。

至于為什麼,我也說不出,給2張書上的說明:

WPF自定義控件-按鈕-DoubleAnimation動畫
WPF自定義控件-按鈕-DoubleAnimation動畫

引用的是【WPF程式設計寶典–使用C#2012和.NET 4.5 第4版】的64,65頁。

  1. 再說注意點♦1:UseLayoutRounding=“False”

    舉個小例子,去掉♦1處的:UseLayoutRounding=“False”

<Grid UseLayoutRounding="True">
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="*"/>
            <RowDefinition Height="*"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="120"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
        <local:MyButton Template="{StaticResource TemplateMyButton}" Grid.Column="1" Content="首頁" Padding="5"/>
                

為了防止黑線,我在最外面添加了UseLayoutRounding=“True”,如果MyButton被強制設定了寬度,又加上了Padding,MyButton的實際寬度程式設計110,我用慢動作看一下運作的實際動畫:

WPF自定義控件-按鈕-DoubleAnimation動畫

文字在上下抖動,原因大概就是高度是小數,不停變化高度的時候,由于布局舍入導緻居中的位置上下1像素浮動。這時候就要把布局舍入給關了。

  1. 再說注意點♦3:屬性FillBehavior

    用一個别的按鈕樣式說明,沒有FillBehavior屬性,被拉伸後的效果

    拉伸前:

    WPF自定義控件-按鈕-DoubleAnimation動畫
    拉伸後:
    WPF自定義控件-按鈕-DoubleAnimation動畫

應該很好了解,原本高寬是考Grid自動生成的,是以控件實際的Hight和Width的值是NaN,是以動畫我用的都是ActualHeight和ActualWidth,動畫執行的時候,改變的是Hight和Width,把實際的高寬給固定了,是以拉伸後還保持原來的Hight和Width而沒有跟随Grid的變化而變化,是以要在滑鼠離開後,把NaN的值還給他。

繼續閱讀