Button加入等待动画,先运行软件看下效果
建立BusyFrameAnimation类,在类中创建Button中依赖属性IsBusyProperty
public class BusyFrameAnimation : DependencyObject
{
public static bool GetIsBusy(DependencyObject property)
{
return (bool)property.GetValue(IsBusyProperty);
}
public static void SetIsBusy(DependencyObject property, bool value)
{
property.SetValue(IsBusyProperty, value);
}
public static readonly DependencyProperty IsBusyProperty =
DependencyProperty.RegisterAttached("IsBusy", typeof(bool), typeof(BusyFrameAnimation), new PropertyMetadata(false));
}
local:BusyFrameAnimation.IsBusy="{Binding LoginIsRunning}" 属性绑定LoginIsRunning,设置LoginIsRunning值显示或隐藏等待。
<Button Content="登录"
IsDefault="True"
Command="{Binding LoginCommand}"
local:BusyFrameAnimation.IsBusy="{Binding LoginIsRunning}"
CommandParameter="{Binding RelativeSource={RelativeSource AncestorType={x:Type pages:LoginPage}}}"
HorizontalAlignment="Center"/>
public class BoolenToVisiblityConverter : BaseValueConverter<BoolenToVisiblityConverter>
{
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();
}
}
重点部分来了,实现加载动画和旋转效果功能,先去iconfont找个等待矢量图。
<Geometry x:Key="Icon-Waiting">M60.014 542.385c0-37.982 30.385-68.367 68.367-68.367 45.578 0 75.964 30.385 75.964 68.367 0 45.578-30.385 75.964-75.964 75.964-37.982 0-68.367-30.385-68.367-75.964zM151.171 276.512v0c0-53.175 37.982-98.753 91.157-98.753s98.753 45.578 98.753 98.753v0c0 53.175-45.578 91.157-98.753 91.157s-91.157-37.982-91.157-91.157v0zM189.153 815.856v0c0-30.385 22.789-53.175 53.175-53.175s53.175 22.789 53.175 53.175v0c0 30.385-22.789 53.175-53.175 53.175s-53.175-22.789-53.175-53.175zM455.027 929.802v0c0-37.982 22.789-60.772 60.772-60.772s60.772 22.789 60.772 60.772v0c0 37.982-22.789 60.772-60.772 60.772-37.982 0-60.772-22.789-60.772-60.772zM751.287 815.856c0-22.789 15.193-37.982 37.982-37.982s37.982 15.193 37.982 37.982c0 22.789-15.193 37.982-37.982 37.982-22.789 0-37.982-15.193-37.982-37.982zM834.847 542.385c0-30.385 30.385-60.772 68.367-60.772 30.385 0 60.772 30.385 60.772 60.772 0 37.982-30.385 68.367-60.772 68.367-37.982 0-68.367-30.385-68.367-68.367zM660.13 276.512c0-75.964 60.772-129.139 129.139-129.139s129.139 53.175 129.139 129.139c0 68.367-60.772 129.139-129.139 129.139-68.367 0-129.139-60.772-129.139-129.139zM386.66 162.566c0-68.367 60.772-129.139 129.139-129.139s129.139 60.772 129.139 129.139c0 68.367-60.772 129.139-129.139 129.139-68.367 0-129.139-60.772-129.139-129.139z</Geometry>
用label实现加载矢量图,PathData="{DynamicResource Icon-Waiting}",动画旋转360度和时长设置等属性,Storyboard.TargetProperty="(UIElement.RenderTransform).(RotateTransform.Angle)"From="0"To="360"Duration="0:0:2"
<Style x:Key="SpinningLabel" TargetType="{x:Type Label}">
<Setter Property="RenderTransformOrigin" Value="0.5,0.5"/>
<Setter Property="RenderTransform">
<Setter.Value>
<RotateTransform/>
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Label}">
<Viewbox x:Name="IconBox" Width="{TemplateBinding FontSize}" Height="{TemplateBinding FontSize}" HorizontalAlignment="Center" VerticalAlignment="Center">
<Path Data="{DynamicResource Icon-Waiting}" Fill="{TemplateBinding Foreground}" Stretch="Fill"></Path>
</Viewbox>
</ControlTemplate>
</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>
之前讲过触发有三种方式,事件触发和属性触发都使用过,这里用到数据触发功能。
<DataTriggerBinding="{Binding RelativeSource={x:Static RelativeSource.Self}, Path=IsVisible}"Value="True"> 此处代码表示当显示的时候调用Storyboard="{StaticResource Spin}"名称是上面Spin的键。
标签控件显示和旋转功能已经实现了,需要加入到按钮中。
<Style TargetType="{x:Type Button}">
<Setter Property="Background" Value="{StaticResource WordOrangeBrush}"/>
<Setter Property="Foreground" Value="{StaticResource ForegroundLightBrush}"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="FontSize" Value="30"/>
<Setter Property="Margin" Value="0,10,0,0"/>
<Setter Property="Padding" Value="50,10"/>
<Setter Property="local:BusyFrameAnimation.IsBusy" Value="False"/>
<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">
<Grid>
<TextBlock Text="{TemplateBinding Content}"
Visibility="{TemplateBinding local:BusyFrameAnimation.IsBusy, Converter={cv:BoolenToVisiblityConverter}}"
Focusable="False"
FontFamily="{TemplateBinding FontFamily}"
FontSize="{TemplateBinding FontSize}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
Margin="{TemplateBinding Padding}"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
<Label Style="{DynamicResource SpinningLabel}"
FontSize="{TemplateBinding FontSize}"
Foreground="{TemplateBinding Foreground}"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
Visibility="{TemplateBinding local:BusyFrameAnimation.IsBusy, Converter={cv:BoolenToVisiblityConverter}, ConverterParameter=True}"/>
</Grid>
</Border>
<ControlTemplate.Triggers>
<EventTrigger RoutedEvent="MouseEnter">
<BeginStoryboard>
<Storyboard>
<ColorAnimation To="{StaticResource WordBlue}" Duration="0:0:0.3" Storyboard.TargetName="border" Storyboard.TargetProperty="Background.Color"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="MouseLeave">
<BeginStoryboard>
<Storyboard>
<ColorAnimation From="{StaticResource WordBlue}" Duration="0:0:0.3" Storyboard.TargetName="border" Storyboard.TargetProperty="Background.Color"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Background" TargetName="border" Value="{StaticResource ForegroundDarkBrush}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
默认等待动画是不显示,所以设置依赖属性<SetterProperty="local:BusyFrameAnimation.IsBusy"Value="False"/>
当Visibility="{TemplateBinding local:BusyFrameAnimation.IsBusy, Converter={cv:BoolenToVisiblityConverter}, ConverterParameter=True}" 转换类型cv:BoolenToVisiblityConverter和设置依赖属性BusyFrameAnimation.IsBusy=true时就会显示等待动画。
最后登录按钮绑定命令是异步等待,这里我设置显示等待10秒。
public ICommand LoginCommand
{
get
{
return new DelegateCommand(async parameter =>
{
await RunCommandAsync(() => this.LoginIsRunning, async () =>
{
await Task.Delay(10000);
});
});
}
}
RunCommandAsync异步方法,参数updatingFlag是LoginIsRunning传的值, updatingFlag.SetPropertyValue(true);显示动画,await action();等待时间后再设置false,隐藏动画效果,显示Button默认文本内容
protected async Task RunCommandAsync(Expression<Func<bool>> updatingFlag, Func<Task> action)
{
lock (updatingFlag)
{
if (updatingFlag.GetPropertyValue())
return;
updatingFlag.SetPropertyValue(true);
}
try
{
await action();
}
finally
{
updatingFlag.SetPropertyValue(false);
}
}