WPF自定義控件-按鈕-DoubleAnimation動畫
-
- 效果展示
- 原理
- 下面為其中一個樣式的代碼
- 到此代碼都結束了
- 補充上述3點我在做樣式的注意點
效果展示
最近在看Wpf的書,初了解Wpf,也沒做過C#和Wpf的項目。
如果下述有不妥的地方請多多包含,有錯誤,還請指教。
原理
自定義控件繼承Button,模闆為兩個重疊的Border
下面為其中一個樣式的代碼
- 自定義控件檔案(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)));
}
}
}
-
項目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>
- 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>
-
主視窗檔案(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>
- 上述步驟後,大體視窗的樣子成型
-
現在重寫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);
}
- 上述步驟後,整個按鈕算是大體全部結束了,後續要添加依賴屬性,這樣可以設定自己想要的背景色,文字大小顔色等屬性。
-
依賴屬性的設定(這裡就隻設定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="聯系我們"/>
到此代碼都結束了
補充上述3點我在做樣式的注意點
-
先說注意點♦2:UseLayoutRounding=“True”
做個小例子
<Grid Grid.Row="2" Grid.Column="1">
<Border Background="Black"/>
<Border Background="White"/>
</Grid>
上述在一個單元格裡放至2個Border,白色覆寫在黑色上面,下面是沒有UseLayoutRounding=“True”
的運作結果
有2根比較明顯的底色黑線漏出來了,我這次給的MyButton例子是不會出現這個的,因為一個是隐藏的,一個是顯示的。但是如果自己想做别的動畫,需要多個重疊控件的話,就可能會出現這個現象。
至于為什麼,我也說不出,給2張書上的說明:
引用的是【WPF程式設計寶典–使用C#2012和.NET 4.5 第4版】的64,65頁。
-
再說注意點♦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,我用慢動作看一下運作的實際動畫:
文字在上下抖動,原因大概就是高度是小數,不停變化高度的時候,由于布局舍入導緻居中的位置上下1像素浮動。這時候就要把布局舍入給關了。
-
再說注意點♦3:屬性FillBehavior
用一個别的按鈕樣式說明,沒有FillBehavior屬性,被拉伸後的效果
拉伸前:
拉伸後:
應該很好了解,原本高寬是考Grid自動生成的,是以控件實際的Hight和Width的值是NaN,是以動畫我用的都是ActualHeight和ActualWidth,動畫執行的時候,改變的是Hight和Width,把實際的高寬給固定了,是以拉伸後還保持原來的Hight和Width而沒有跟随Grid的變化而變化,是以要在滑鼠離開後,把NaN的值還給他。