WPF实现好看的Loading动画的示例代码

实现思路

框架使用大于等于.NET40;

Visual Studio 2022;

项目使用MIT开源许可协议;

老板觉得公司系统等待动画转圈太简单,所以需要做一个稍微好看点的,就有这篇等待RingLoading动画

最外层使用Viewbox为父控件内部嵌套创建三组Grid -> Ellipse 、 Border分别给它们指定不同的Angle从左侧开始-135 225 54,做永久Angle动画;

  • PART_Ring1.RotateTransform.Angle从From -135到-495;
  • PART_Ring2.RotateTransform.Angle从From 225到-585;
  • PART_Ring3.RotateTransform.Angle从From -54到-315;

如何绘制;

对Ellipse的StrokeDashArray进行设置23 100就能达到效果;

Border做为圆设置Effect可实现阴影效果;

实现代码

1)RingLoading.cs代码如下;

using System.Windows;
using System.Windows.Controls;

namespace WPFDevelopers.Controls
{
  public class RingLoading : Control
  {
      // Using a DependencyProperty as the backing store for IsStart.  This enables animation, styling, binding, etc...
      public static readonly DependencyProperty IsStartProperty =
          DependencyProperty.Register("IsStart", typeof(bool), typeof(RingLoading), new PropertyMetadata(default));

      // Using a DependencyProperty as the backing store for ProgressValue.  This enables animation, styling, binding, etc...
      public static readonly DependencyProperty ProgressValueProperty =
          DependencyProperty.Register("ProgressValue", typeof(double), typeof(RingLoading),
              new PropertyMetadata(0d, OnProgressValueChangedCallBack));

      // Using a DependencyProperty as the backing store for Progress.  This enables animation, styling, binding, etc...
      internal static readonly DependencyProperty ProgressProperty =
          DependencyProperty.Register("Progress", typeof(string), typeof(RingLoading), new PropertyMetadata(default));

      // Using a DependencyProperty as the backing store for Maximum.  This enables animation, styling, binding, etc...
      public static readonly DependencyProperty MaximumProperty =
          DependencyProperty.Register("Maximum", typeof(double), typeof(RingLoading),
              new PropertyMetadata(100d, OnMaximumPropertyChangedCallBack));

      // Using a DependencyProperty as the backing store for Description.  This enables animation, styling, binding, etc...
      public static readonly DependencyProperty DescriptionProperty =
          DependencyProperty.Register("Description", typeof(string), typeof(RingLoading),
              new PropertyMetadata(default));

      static RingLoading()
      {
          DefaultStyleKeyProperty.OverrideMetadata(typeof(RingLoading),
              new FrameworkPropertyMetadata(typeof(RingLoading)));
      }

      public bool IsStart
      {
          get => (bool)GetValue(IsStartProperty);
          set => SetValue(IsStartProperty, value);
      }


      public double ProgressValue
      {
          get => (double)GetValue(ProgressValueProperty);
          set => SetValue(ProgressValueProperty, value);
      }


      internal string Progress
      {
          get => (string)GetValue(ProgressProperty);
          set => SetValue(ProgressProperty, value);
      }


      public double Maximum
      {
          get => (double)GetValue(MaximumProperty);
          set => SetValue(MaximumProperty, value);
      }

      public string Description
      {
          get => (string)GetValue(DescriptionProperty);
          set => SetValue(DescriptionProperty, value);
      }

      private static void OnProgressValueChangedCallBack(DependencyObject d, DependencyPropertyChangedEventArgs e)
      {
          if (!(d is RingLoading control))
              return;

          if (!double.TryParse(e.NewValue?.ToString(), out var value))
              return;

          var progress = value / control.Maximum;
          control.SetCurrentValue(ProgressProperty, progress.ToString("P0"));
      }

      private static void OnMaximumPropertyChangedCallBack(DependencyObject d, DependencyPropertyChangedEventArgs e)
      {
          if (!(d is RingLoading control))
              return;

          if (!double.TryParse(e.NewValue?.ToString(), out var maxValue))
              return;

          if (maxValue <= 0)
              return;

          var progress = control.ProgressValue / maxValue;
          control.SetCurrentValue(ProgressProperty, progress.ToString("P0"));
      }
  }
}

2)RingLoading.xaml代码如下

 <Style TargetType="controls:RingLoading" BasedOn="{StaticResource ControlBasicStyle}">
      <Setter Property="Template">
          <Setter.Value>
              <ControlTemplate TargetType="controls:RingLoading">
                  <ControlTemplate.Resources>
                      <Storyboard x:Key="PART_Resource_Storyboard" RepeatBehavior="Forever">
                          <DoubleAnimation To="-495" Duration="0:0:1.5" Storyboard.TargetName="PART_Ring1"  Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[2].(RotateTransform.Angle)"/>
                          <DoubleAnimation To="585" Duration="0:0:1.5" Storyboard.TargetName="PART_Ring2"  Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[2].(RotateTransform.Angle)"/>
                          <DoubleAnimation To="-315" Duration="0:0:1.5" Storyboard.TargetName="PART_Ring3"  Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[2].(RotateTransform.Angle)"/>
                      </Storyboard>
                  </ControlTemplate.Resources>

                  <Grid>
                      <Grid.RowDefinitions>
                          <RowDefinition Height="*"/>
                          <RowDefinition Height="Auto"/>
                      </Grid.RowDefinitions>

                      <Viewbox HorizontalAlignment="Center" VerticalAlignment="Center" >
                          <Border Padding="10" Width="100" Height="100" >
                              <Grid>
                                  <Grid x:Name="PART_Ring1" Width="60" Height="60" HorizontalAlignment="Center" VerticalAlignment="Top" RenderTransformOrigin="0.5,0.5">
                                      <Grid.RenderTransform>
                                          <TransformGroup>
                                              <ScaleTransform/>
                                              <SkewTransform/>
                                              <RotateTransform Angle="-135"/>
                                              <TranslateTransform/>
                                          </TransformGroup>
                                      </Grid.RenderTransform>
                                      <Ellipse Stroke="Red" StrokeThickness="2" StrokeDashArray="23 100" RenderTransformOrigin="0.5,0.5"/>
                                      <Border Width="10" Height="10" CornerRadius="10" Background="Red" HorizontalAlignment="Right" Margin="0,0,-4,0">
                                          <Border.Effect>
                                              <DropShadowEffect BlurRadius="10" ShadowDepth="0" Color="Red"/>
                                          </Border.Effect>
                                      </Border>
                                  </Grid>

                                  <Grid x:Name="PART_Ring2" Width="60" Height="60" HorizontalAlignment="Left" VerticalAlignment="Bottom" RenderTransformOrigin="0.5,0.5">
                                      <Grid.RenderTransform>
                                          <TransformGroup>
                                              <ScaleTransform/>
                                              <SkewTransform/>
                                              <RotateTransform Angle="225"/>
                                              <TranslateTransform/>
                                          </TransformGroup>
                                      </Grid.RenderTransform>
                                      <Ellipse Stroke="Purple" StrokeThickness="2" StrokeDashArray="23 100"/>
                                      <Border Width="10" Height="10" CornerRadius="10" Background="Purple" VerticalAlignment="Bottom" HorizontalAlignment="Center" Margin="0,0,0,-4">
                                          <Border.Effect>
                                              <DropShadowEffect BlurRadius="10" ShadowDepth="0" Color="Purple"/>
                                          </Border.Effect>
                                      </Border>
                                  </Grid>

                                  <Grid x:Name="PART_Ring3" Width="60" Height="60" HorizontalAlignment="Right" VerticalAlignment="Bottom" RenderTransformOrigin="0.5,0.5">
                                      <Grid.RenderTransform>
                                          <TransformGroup>
                                              <ScaleTransform/>
                                              <SkewTransform/>
                                              <RotateTransform Angle="45"/>
                                              <TranslateTransform/>
                                          </TransformGroup>
                                      </Grid.RenderTransform>
                                      <Ellipse Stroke="#0fb8b2" StrokeThickness="2" StrokeDashArray="23 100"/>
                                      <Border Width="10" Height="10" CornerRadius="10" Background="#0fb8b2" HorizontalAlignment="Right" Margin="0,0,-4,0">
                                          <Border.Effect>
                                              <DropShadowEffect BlurRadius="10" ShadowDepth="0" Color="#0fb8b2"/>
                                          </Border.Effect>
                                      </Border>
                                  </Grid>
                              </Grid>
                          </Border>
                      </Viewbox>

                      <StackPanel Grid.Row="1" Grid.ColumnSpan="2" Margin="10">
                          <TextBlock HorizontalAlignment="Center" Text="Loading..." Margin="0,0,0,15"/>
                          <TextBlock HorizontalAlignment="Center" Text="{TemplateBinding Description}" Margin="0,0,0,15"/>
                          <TextBlock HorizontalAlignment="Center" Text="{TemplateBinding Progress}" FontSize="{StaticResource TitleFontSize}" 
                                     FontWeight="Bold"/>
                      </StackPanel>
                  </Grid>
                  <ControlTemplate.Triggers>
                      <Trigger Property="IsStart" Value="True">
                          <Trigger.EnterActions>
                              <BeginStoryboard Storyboard="{StaticResource PART_Resource_Storyboard}" x:Name="PART_BeginStoryboard"/>
                          </Trigger.EnterActions>
                          <Trigger.ExitActions>
                              <StopStoryboard BeginStoryboardName="PART_BeginStoryboard"/>
                          </Trigger.ExitActions>
                      </Trigger>

                  </ControlTemplate.Triggers>
              </ControlTemplate>
          </Setter.Value>
      </Setter>
  </Style>

3)RingLoadingExample.xaml代码如下

<UserControl x:Class="WPFDevelopers.Samples.ExampleViews.RingLoadingExample"
           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:wpfdev="https://github.com/WPFDevelopersOrg/WPFDevelopers"
           xmlns:local="clr-namespace:WPFDevelopers.Samples.ExampleViews"
           mc:Ignorable="d" 
           d:DesignHeight="450" d:DesignWidth="800">
  <Grid>
      <wpfdev:RingLoading IsStart="true" 
                          Width="400" Height="400"
                          Description="WPFDevelopers" Foreground="Black" ProgressValue="50"/>
  </Grid>
</UserControl>

以上就是WPF实现好看的Loading动画的示例代码的详细内容,更多关于WPF Loading动画的资料请关注编程宝库其它相关文章!

介绍框架使用大于等于.NET40;Visual Studio 2022;项目使用MIT开源许可协议;NugetInstall-Package WPFDevelopers.Minimal3 ...