目标是显示应用程序正在工作的信息。因此,我正在寻找一种智能的实现示例,使用 WPF / MVVM 来创建一个加载旋转器。
一个非常简单的“即插即用”旋转器可以是来自Font Awesome Wpf Package(旋转图标)之一。
使用方法非常简单,只需安装nuget包即可:
PM> Install-Package FontAwesome.WPF
然后将该引用添加到命名空间中。
xmlns:fa="http://schemas.fontawesome.io/icons/"
使用ImageAwesome控件。设置Spin="True"属性并选择一个"Spinner"、"Refresh"、"Cog"和"CircleOutlinedNotched"图标。它可扩展,并可通过设置宽度和高度来调整大小。
<Window x:Class="Example.FontAwesome.WPF.Single"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:fa="http://schemas.fontawesome.io/icons/"
Title="Single" Height="300" Width="300">
<Grid Margin="20">
<fa:ImageAwesome Icon="Refresh" Spin="True" Height="48" Width="48" />
</Grid>
</Window>
要实现这个效果:
将以下内容放入用户控件中:
<UserControl.Resources>
<Color x:Key="FilledColor" A="255" B="155" R="155" G="155"/>
<Color x:Key="UnfilledColor" A="0" B="155" R="155" G="155"/>
<Style x:Key="BusyAnimationStyle" TargetType="Control">
<Setter Property="Background" Value="#7F000000"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Control">
<ControlTemplate.Resources>
<Storyboard x:Key="Animation0" BeginTime="00:00:00.0" RepeatBehavior="Forever">
<ColorAnimationUsingKeyFrames Storyboard.TargetName="ellipse0" Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)">
<SplineColorKeyFrame KeyTime="00:00:00.0" Value="{StaticResource FilledColor}"/>
<SplineColorKeyFrame KeyTime="00:00:01.6" Value="{StaticResource UnfilledColor}"/>
</ColorAnimationUsingKeyFrames>
</Storyboard>
<Storyboard x:Key="Animation1" BeginTime="00:00:00.2" RepeatBehavior="Forever">
<ColorAnimationUsingKeyFrames Storyboard.TargetName="ellipse1" Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)">
<SplineColorKeyFrame KeyTime="00:00:00.0" Value="{StaticResource FilledColor}"/>
<SplineColorKeyFrame KeyTime="00:00:01.6" Value="{StaticResource UnfilledColor}"/>
</ColorAnimationUsingKeyFrames>
</Storyboard>
<Storyboard x:Key="Animation2" BeginTime="00:00:00.4" RepeatBehavior="Forever">
<ColorAnimationUsingKeyFrames Storyboard.TargetName="ellipse2" Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)">
<SplineColorKeyFrame KeyTime="00:00:00.0" Value="{StaticResource FilledColor}"/>
<SplineColorKeyFrame KeyTime="00:00:01.6" Value="{StaticResource UnfilledColor}"/>
</ColorAnimationUsingKeyFrames>
</Storyboard>
<Storyboard x:Key="Animation3" BeginTime="00:00:00.6" RepeatBehavior="Forever">
<ColorAnimationUsingKeyFrames Storyboard.TargetName="ellipse3" Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)">
<SplineColorKeyFrame KeyTime="00:00:00.0" Value="{StaticResource FilledColor}"/>
<SplineColorKeyFrame KeyTime="00:00:01.6" Value="{StaticResource UnfilledColor}"/>
</ColorAnimationUsingKeyFrames>
</Storyboard>
<Storyboard x:Key="Animation4" BeginTime="00:00:00.8" RepeatBehavior="Forever">
<ColorAnimationUsingKeyFrames Storyboard.TargetName="ellipse4" Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)">
<SplineColorKeyFrame KeyTime="00:00:00.0" Value="{StaticResource FilledColor}"/>
<SplineColorKeyFrame KeyTime="00:00:01.6" Value="{StaticResource UnfilledColor}"/>
</ColorAnimationUsingKeyFrames>
</Storyboard>
<Storyboard x:Key="Animation5" BeginTime="00:00:01.0" RepeatBehavior="Forever">
<ColorAnimationUsingKeyFrames Storyboard.TargetName="ellipse5" Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)">
<SplineColorKeyFrame KeyTime="00:00:00.0" Value="{StaticResource FilledColor}"/>
<SplineColorKeyFrame KeyTime="00:00:01.6" Value="{StaticResource UnfilledColor}"/>
</ColorAnimationUsingKeyFrames>
</Storyboard>
<Storyboard x:Key="Animation6" BeginTime="00:00:01.2" RepeatBehavior="Forever">
<ColorAnimationUsingKeyFrames Storyboard.TargetName="ellipse6" Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)">
<SplineColorKeyFrame KeyTime="00:00:00.0" Value="{StaticResource FilledColor}"/>
<SplineColorKeyFrame KeyTime="00:00:01.6" Value="{StaticResource UnfilledColor}"/>
</ColorAnimationUsingKeyFrames>
</Storyboard>
<Storyboard x:Key="Animation7" BeginTime="00:00:01.4" RepeatBehavior="Forever">
<ColorAnimationUsingKeyFrames Storyboard.TargetName="ellipse7" Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)">
<SplineColorKeyFrame KeyTime="00:00:00.0" Value="{StaticResource FilledColor}"/>
<SplineColorKeyFrame KeyTime="00:00:01.6" Value="{StaticResource UnfilledColor}"/>
</ColorAnimationUsingKeyFrames>
</Storyboard>
</ControlTemplate.Resources>
<ControlTemplate.Triggers>
<Trigger Property="IsVisible" Value="True">
<Trigger.EnterActions>
<BeginStoryboard Storyboard="{StaticResource Animation0}" x:Name="Storyboard0" />
<BeginStoryboard Storyboard="{StaticResource Animation1}" x:Name="Storyboard1"/>
<BeginStoryboard Storyboard="{StaticResource Animation2}" x:Name="Storyboard2"/>
<BeginStoryboard Storyboard="{StaticResource Animation3}" x:Name="Storyboard3"/>
<BeginStoryboard Storyboard="{StaticResource Animation4}" x:Name="Storyboard4"/>
<BeginStoryboard Storyboard="{StaticResource Animation5}" x:Name="Storyboard5"/>
<BeginStoryboard Storyboard="{StaticResource Animation6}" x:Name="Storyboard6"/>
<BeginStoryboard Storyboard="{StaticResource Animation7}" x:Name="Storyboard7"/>
</Trigger.EnterActions>
<Trigger.ExitActions>
<StopStoryboard BeginStoryboardName="Storyboard0"/>
<StopStoryboard BeginStoryboardName="Storyboard1"/>
<StopStoryboard BeginStoryboardName="Storyboard2"/>
<StopStoryboard BeginStoryboardName="Storyboard3"/>
<StopStoryboard BeginStoryboardName="Storyboard4"/>
<StopStoryboard BeginStoryboardName="Storyboard5"/>
<StopStoryboard BeginStoryboardName="Storyboard6"/>
<StopStoryboard BeginStoryboardName="Storyboard7"/>
</Trigger.ExitActions>
</Trigger>
</ControlTemplate.Triggers>
<Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}">
<Grid>
<Canvas Height="60" Width="60">
<Canvas.Resources>
<Style TargetType="Ellipse">
<Setter Property="Width" Value="15"/>
<Setter Property="Height" Value="15" />
<Setter Property="Fill" Value="#009B9B9B" />
</Style>
</Canvas.Resources>
<Ellipse x:Name="ellipse0" Canvas.Left="1.75" Canvas.Top="21"/>
<Ellipse x:Name="ellipse1" Canvas.Top="7" Canvas.Left="6.5"/>
<Ellipse x:Name="ellipse2" Canvas.Left="20.5" Canvas.Top="0.75"/>
<Ellipse x:Name="ellipse3" Canvas.Left="34.75" Canvas.Top="6.75"/>
<Ellipse x:Name="ellipse4" Canvas.Left="40.5" Canvas.Top="20.75" />
<Ellipse x:Name="ellipse5" Canvas.Left="34.75" Canvas.Top="34.5"/>
<Ellipse x:Name="ellipse6" Canvas.Left="20.75" Canvas.Top="39.75"/>
<Ellipse x:Name="ellipse7" Canvas.Top="34.25" Canvas.Left="7" />
<Ellipse Width="39.5" Height="39.5" Canvas.Left="8.75" Canvas.Top="8" Visibility="Hidden"/>
</Canvas>
<Label Content="{Binding Path=Text}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
<Control Style="{StaticResource BusyAnimationStyle}"/>
为了在每个椭圆上获得酷炫的消失效果,请在每个 ColorAnimationUsingKeyFrames
元素之后添加以下内容。一定要将其指向正确的椭圆。<ColorAnimationUsingKeyFrames Storyboard.TargetName="ellipse0" Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)">
<SplineColorKeyFrame KeyTime="00:00:00.0" Value="{StaticResource FilledColor}"/>
<SplineColorKeyFrame KeyTime="00:00:01.6" Value="{StaticResource UnfilledColor}"/>
</ColorAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="ellipse0" Storyboard.TargetProperty="Width" >
<DoubleAnimationUsingKeyFrames.KeyFrames>
<SplineDoubleKeyFrame KeyTime="00:00:00.0" Value="15" />
<SplineDoubleKeyFrame KeyTime="00:00:01.0" Value="12" />
<SplineDoubleKeyFrame KeyTime="00:00:01.6" Value="0" />
</DoubleAnimationUsingKeyFrames.KeyFrames>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="ellipse0" Storyboard.TargetProperty="Height" >
<DoubleAnimationUsingKeyFrames.KeyFrames>
<SplineDoubleKeyFrame KeyTime="00:00:00.0" Value="15" />
<SplineDoubleKeyFrame KeyTime="00:00:01.0" Value="12" />
<SplineDoubleKeyFrame KeyTime="00:00:01.6" Value="0" />
</DoubleAnimationUsingKeyFrames.KeyFrames>
</DoubleAnimationUsingKeyFrames>
我编写了这个用户控件,它可以帮助显示一个进度条,并旋转以表示正在加载某些东西。
<ctr:LoadingPanel x:Name="loadingPanel"
IsLoading="{Binding PanelLoading}"
Message="{Binding PanelMainMessage}"
SubMessage="{Binding PanelSubMessage}"
ClosePanelCommand="{Binding PanelCloseCommand}" />
它有几个基本属性,您可以将其绑定。
旋转图标选项的视觉总结。 使用Screen To Gif记录。
通过NuGet安装:
PM> Install-Package FontAwesome.WPF
效果如下:
XAML:
<fa:ImageAwesome Icon="Spinner" Spin="True" SpinDuration="4" />
所示的图标包括Spinner
、CircleOutlineNotch
、Refresh
和Cog
,还有许多其他图标。
可复制粘贴的XAML代码。
在WPF中,现在你可以简单地这样做:
Mouse.OverrideCursor = System.Windows.Input.Cursors.Wait; // set the cursor to loading spinner
Mouse.OverrideCursor = System.Windows.Input.Cursors.Arrow; // set the cursor back to arrow
你可以仅使用图像控件和变换操作,无需任何额外的控件和库来完成它:
<Image
Source="/images/spinner.png"
Width="100"
Height="100"
RenderTransformOrigin="0.5, 0.5" Visibility="{Binding IsLoading, Converter={StaticResource BooleanToVisibilityConverter}}">
<Image.RenderTransform>
<RotateTransform x:Name="noFreeze" />
</Image.RenderTransform>
<Image.Triggers>
<EventTrigger RoutedEvent="Loaded">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetProperty="(Image.RenderTransform).(RotateTransform.Angle)"
To="360" Duration="0:0:1" RepeatBehavior="Forever" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Image.Triggers>
</Image>
将/images/spinner.png
替换为你的图片。如果你想逆时针旋转,请将To="360"
改为To="-360"
。Duration="0:0:1"
表示每次旋转需要1秒。
这是对@HAdes提供的代码进行更新,以便参数化宽度、高度和椭圆大小。
该实现会自动计算所需的角度、宽度和高度。
用户控件绑定到自身(code-behind),负责所有计算。
XAML
<UserControl x:Class="WpfApplication2.Spinner"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:WpfApplication2"
mc:Ignorable="d"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
d:DesignHeight="300" d:DesignWidth="300">
<UserControl.Resources>
<Color x:Key="FilledColor" A="255" B="155" R="155" G="155"/>
<Color x:Key="UnfilledColor" A="0" B="155" R="155" G="155"/>
<Style x:Key="BusyAnimationStyle" TargetType="Control">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Control">
<ControlTemplate.Resources>
<Storyboard x:Key="Animation0" BeginTime="00:00:00.0" RepeatBehavior="Forever">
<ColorAnimationUsingKeyFrames Storyboard.TargetName="ellipseN" Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)">
<SplineColorKeyFrame KeyTime="00:00:00.0" Value="{StaticResource FilledColor}"/>
<SplineColorKeyFrame KeyTime="00:00:01.6" Value="{StaticResource UnfilledColor}"/>
</ColorAnimationUsingKeyFrames>
</Storyboard>
<Storyboard x:Key="Animation1" BeginTime="00:00:00.2" RepeatBehavior="Forever">
<ColorAnimationUsingKeyFrames Storyboard.TargetName="ellipseNE" Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)">
<SplineColorKeyFrame KeyTime="00:00:00.0" Value="{StaticResource FilledColor}"/>
<SplineColorKeyFrame KeyTime="00:00:01.6" Value="{StaticResource UnfilledColor}"/>
</ColorAnimationUsingKeyFrames>
</Storyboard>
<Storyboard x:Key="Animation2" BeginTime="00:00:00.4" RepeatBehavior="Forever">
<ColorAnimationUsingKeyFrames Storyboard.TargetName="ellipseE" Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)">
<SplineColorKeyFrame KeyTime="00:00:00.0" Value="{StaticResource FilledColor}"/>
<SplineColorKeyFrame KeyTime="00:00:01.6" Value="{StaticResource UnfilledColor}"/>
</ColorAnimationUsingKeyFrames>
</Storyboard>
<Storyboard x:Key="Animation3" BeginTime="00:00:00.6" RepeatBehavior="Forever">
<ColorAnimationUsingKeyFrames Storyboard.TargetName="ellipseSE" Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)">
<SplineColorKeyFrame KeyTime="00:00:00.0" Value="{StaticResource FilledColor}"/>
<SplineColorKeyFrame KeyTime="00:00:01.6" Value="{StaticResource UnfilledColor}"/>
</ColorAnimationUsingKeyFrames>
</Storyboard>
<Storyboard x:Key="Animation4" BeginTime="00:00:00.8" RepeatBehavior="Forever">
<ColorAnimationUsingKeyFrames Storyboard.TargetName="ellipseS" Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)">
<SplineColorKeyFrame KeyTime="00:00:00.0" Value="{StaticResource FilledColor}"/>
<SplineColorKeyFrame KeyTime="00:00:01.6" Value="{StaticResource UnfilledColor}"/>
</ColorAnimationUsingKeyFrames>
</Storyboard>
<Storyboard x:Key="Animation5" BeginTime="00:00:01.0" RepeatBehavior="Forever">
<ColorAnimationUsingKeyFrames Storyboard.TargetName="ellipseSW" Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)">
<SplineColorKeyFrame KeyTime="00:00:00.0" Value="{StaticResource FilledColor}"/>
<SplineColorKeyFrame KeyTime="00:00:01.6" Value="{StaticResource UnfilledColor}"/>
</ColorAnimationUsingKeyFrames>
</Storyboard>
<Storyboard x:Key="Animation6" BeginTime="00:00:01.2" RepeatBehavior="Forever">
<ColorAnimationUsingKeyFrames Storyboard.TargetName="ellipseW" Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)">
<SplineColorKeyFrame KeyTime="00:00:00.0" Value="{StaticResource FilledColor}"/>
<SplineColorKeyFrame KeyTime="00:00:01.6" Value="{StaticResource UnfilledColor}"/>
</ColorAnimationUsingKeyFrames>
</Storyboard>
<Storyboard x:Key="Animation7" BeginTime="00:00:01.4" RepeatBehavior="Forever">
<ColorAnimationUsingKeyFrames Storyboard.TargetName="ellipseNW" Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)">
<SplineColorKeyFrame KeyTime="00:00:00.0" Value="{StaticResource FilledColor}"/>
<SplineColorKeyFrame KeyTime="00:00:01.6" Value="{StaticResource UnfilledColor}"/>
</ColorAnimationUsingKeyFrames>
</Storyboard>
</ControlTemplate.Resources>
<ControlTemplate.Triggers>
<Trigger Property="IsVisible" Value="True">
<Trigger.EnterActions>
<BeginStoryboard Storyboard="{StaticResource Animation0}" x:Name="Storyboard0" />
<BeginStoryboard Storyboard="{StaticResource Animation1}" x:Name="Storyboard1"/>
<BeginStoryboard Storyboard="{StaticResource Animation2}" x:Name="Storyboard2"/>
<BeginStoryboard Storyboard="{StaticResource Animation3}" x:Name="Storyboard3"/>
<BeginStoryboard Storyboard="{StaticResource Animation4}" x:Name="Storyboard4"/>
<BeginStoryboard Storyboard="{StaticResource Animation5}" x:Name="Storyboard5"/>
<BeginStoryboard Storyboard="{StaticResource Animation6}" x:Name="Storyboard6"/>
<BeginStoryboard Storyboard="{StaticResource Animation7}" x:Name="Storyboard7"/>
</Trigger.EnterActions>
<Trigger.ExitActions>
<StopStoryboard BeginStoryboardName="Storyboard0"/>
<StopStoryboard BeginStoryboardName="Storyboard1"/>
<StopStoryboard BeginStoryboardName="Storyboard2"/>
<StopStoryboard BeginStoryboardName="Storyboard3"/>
<StopStoryboard BeginStoryboardName="Storyboard4"/>
<StopStoryboard BeginStoryboardName="Storyboard5"/>
<StopStoryboard BeginStoryboardName="Storyboard6"/>
<StopStoryboard BeginStoryboardName="Storyboard7"/>
</Trigger.ExitActions>
</Trigger>
</ControlTemplate.Triggers>
<Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}">
<Grid>
<Canvas>
<Canvas.Resources>
<Style TargetType="Ellipse">
<Setter Property="Width" Value="{Binding Path=EllipseSize}"/>
<Setter Property="Height" Value="{Binding Path=EllipseSize}" />
<Setter Property="Fill" Value="Transparent" />
</Style>
</Canvas.Resources>
<Ellipse x:Name="ellipseN" Canvas.Left="{Binding Path=EllipseN.Left}" Canvas.Top="{Binding Path=EllipseN.Top}"/>
<Ellipse x:Name="ellipseNE" Canvas.Left="{Binding Path=EllipseNE.Left}" Canvas.Top="{Binding Path=EllipseNE.Top}"/>
<Ellipse x:Name="ellipseE" Canvas.Left="{Binding Path=EllipseE.Left}" Canvas.Top="{Binding Path=EllipseE.Top}"/>
<Ellipse x:Name="ellipseSE" Canvas.Left="{Binding Path=EllipseSE.Left}" Canvas.Top="{Binding Path=EllipseSE.Top}"/>
<Ellipse x:Name="ellipseS" Canvas.Left="{Binding Path=EllipseS.Left}" Canvas.Top="{Binding Path=EllipseS.Top}"/>
<Ellipse x:Name="ellipseSW" Canvas.Left="{Binding Path=EllipseSW.Left}" Canvas.Top="{Binding Path=EllipseSW.Top}"/>
<Ellipse x:Name="ellipseW" Canvas.Left="{Binding Path=EllipseW.Left}" Canvas.Top="{Binding Path=EllipseW.Top}"/>
<Ellipse x:Name="ellipseNW" Canvas.Left="{Binding Path=EllipseNW.Left}" Canvas.Top="{Binding Path=EllipseNW.Top}"/>
</Canvas>
<Label Content="{Binding Path=Text}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
<Border>
<Control Style="{StaticResource BusyAnimationStyle}"/>
</Border>
</UserControl>
代码后台 (C#)
using System;
using System.Windows;
using System.Windows.Controls;
namespace WpfApplication2
{
/// <summary>
/// Interaction logic for Spinner.xaml
/// </summary>
public partial class Spinner : UserControl
{
public int EllipseSize { get; set; } = 8;
public int SpinnerHeight { get; set; } = 0;
public int SpinnerWidth { get; set; } = 0;
// start positions
public EllipseStartPosition EllipseN { get; private set; }
public EllipseStartPosition EllipseNE { get; private set; }
public EllipseStartPosition EllipseE { get; private set; }
public EllipseStartPosition EllipseSE { get; private set; }
public EllipseStartPosition EllipseS { get; private set; }
public EllipseStartPosition EllipseSW { get; private set; }
public EllipseStartPosition EllipseW { get; private set; }
public EllipseStartPosition EllipseNW { get; private set; }
public Spinner()
{
InitializeComponent();
}
private void initialSetup()
{
float horizontalCenter = (float)(SpinnerWidth / 2);
float verticalCenter = (float)(SpinnerHeight / 2);
float distance = (float)Math.Min(SpinnerHeight, SpinnerWidth) /2;
double angleInRadians = 44.8;
float cosine = (float)Math.Cos(angleInRadians);
float sine = (float)Math.Sin(angleInRadians);
EllipseN = newPos(left: horizontalCenter, top: verticalCenter - distance);
EllipseNE = newPos(left: horizontalCenter + (distance * cosine), top: verticalCenter - (distance * sine));
EllipseE = newPos(left: horizontalCenter + distance, top: verticalCenter);
EllipseSE = newPos(left: horizontalCenter + (distance * cosine), top: verticalCenter + (distance * sine));
EllipseS = newPos(left: horizontalCenter, top: verticalCenter + distance);
EllipseSW = newPos(left: horizontalCenter - (distance * cosine), top: verticalCenter + (distance * sine));
EllipseW = newPos(left: horizontalCenter - distance, top: verticalCenter);
EllipseNW = newPos(left: horizontalCenter - (distance * cosine), top: verticalCenter - (distance * sine));
}
private EllipseStartPosition newPos(float left, float top)
{
return new EllipseStartPosition() { Left = left, Top = top };
}
protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
{
if(e.Property.Name == "Height")
{
SpinnerHeight = Convert.ToInt32(e.NewValue);
}
if (e.Property.Name == "Width")
{
SpinnerWidth = Convert.ToInt32(e.NewValue);
}
if(SpinnerHeight > 0 && SpinnerWidth > 0)
{
initialSetup();
}
base.OnPropertyChanged(e);
}
}
public struct EllipseStartPosition
{
public float Left { get; set; }
public float Top { get; set; }
}
}
示例使用
<Window x:Class="WpfApplication2.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:WpfApplication2"
xmlns:animated="WpfApplication2.MainWindow"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<StackPanel Background="DarkGoldenrod" Width="200" Height="200" VerticalAlignment="Top" HorizontalAlignment="Left" >
<Button Height="35">
<Button.Content >
<DockPanel LastChildFill="True" Height="NaN" Width="NaN" HorizontalAlignment="Left">
<local:Spinner EllipseSize="4" DockPanel.Dock="Left" HorizontalAlignment="Left" Margin="0,0,10,5" Height="16" Width="16"/>
<TextBlock Text="Cancel" VerticalAlignment="Center"/>
</DockPanel>
</Button.Content>
</Button>
</StackPanel>
</Window>
https://github.com/blackspikeltd/Xaml-Spinners-WPF
旋转器都是轻量级的,可以轻松地放置在需要的位置。存储库中包含一个示例项目,展示如何使用它们。以下是一个全XAML解决方案的示例。它将绑定到视图模型中的“IsWorking”布尔值,以显示控件并启动动画。
<UserControl x:Class="MainApp.Views.SpinnerView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<UserControl.Resources>
<BooleanToVisibilityConverter x:Key="BoolToVisConverter"/>
</UserControl.Resources>
<StackPanel Orientation="Horizontal" Margin="5"
Visibility="{Binding IsWorking, Converter={StaticResource BoolToVisConverter}}">
<Label>Wait...</Label>
<Ellipse x:Name="spinnerEllipse"
Width="20" Height="20">
<Ellipse.Fill>
<LinearGradientBrush StartPoint="1,1" EndPoint="0,0" >
<GradientStop Color="White" Offset="0"/>
<GradientStop Color="CornflowerBlue" Offset="1"/>
</LinearGradientBrush>
</Ellipse.Fill>
<Ellipse.RenderTransform>
<RotateTransform x:Name="SpinnerRotate" CenterX="10" CenterY="10"/>
</Ellipse.RenderTransform>
<Ellipse.Style>
<Style TargetType="Ellipse">
<Style.Triggers>
<DataTrigger Binding="{Binding IsWorking}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard x:Name="SpinStoryboard">
<Storyboard TargetProperty="RenderTransform.Angle" >
<DoubleAnimation
From="0" To="360" Duration="0:0:01"
RepeatBehavior="Forever" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<StopStoryboard BeginStoryboardName="SpinStoryboard"></StopStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Ellipse.Style>
</Ellipse>
</StackPanel>
</UserControl>
public enum ViewModeType
{
Default,
Busy
//etc.
}
然后在您的ViewModels基类中使用一个属性
public ViewModeType ViewMode
{
get { return this.viewMode; }
set
{
if (this.viewMode != value)
{
this.viewMode = value;
//You should notify property changed here
}
}
}
在视图中触发ViewMode,如果它正在忙,则显示busyindicator:
<Trigger Property="ViewMode" Value="Busy">
<!-- Show BusyIndicator -->
</Trigger>
FontAwesome.WPF
不兼容最近的 .NET 版本,如 .Net Core 3.x 或 .NET 5 及以上版本,因为它只是一个严格的 .NET Framework 项目。这使得这个解决方案在现代项目中难以使用。 - Etienne de Martel