超炫酷的WPF实现Loading控件效果

前端技术 2023/09/05 C#

Win8系统的Loading效果还是很不错的,网上也有人用CSS3等技术实现,研究了一下,并打算用WPF自定义一个Loading控件实现类似的效果,并可以让用户对Loading的颗粒(Particle)背景颜色进行自定义,话不多说,直接上代码:

1、用VS2012新建一个WPF的用户控件库项目WpfControlLibraryDemo,VS自动生成如下结构:

2、删除UserControl1.xaml,并新建一个Loading的CustomControl(不是UserControl),如下图所示:

3、如果报错找不到Loading类型,请编译,下面在Generic.xaml主题文件中对Loading的样式和内容进行定义(注意添加

xmlns:system = \"clr-namespace:System;assembly=mscorlib\"),代码如下:
<ResourceDictionary
 xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"
 xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"
 xmlns:system = \"clr-namespace:System;assembly=mscorlib\"
 xmlns:local=\"clr-namespace:WpfControlLibraryDemo\">


 <Style TargetType=\"{x:Type local:Loading}\">
 <Setter Property=\"Template\">
  <Setter.Value>
  <ControlTemplate TargetType=\"{x:Type local:Loading}\">
   <Border Background=\"{TemplateBinding Background}\"
    BorderBrush=\"{TemplateBinding BorderBrush}\"
    BorderThickness=\"{TemplateBinding BorderThickness}\">
   <Grid Width = \"50\" Height = \"50\">
    <Grid.Resources>
    <!-- Value Converters -->
    
    <!-- Particle Styling ,must to has RelativeSource -->
    <SolidColorBrush x:Key = \"ParticleColor\" Color = \"{Binding Path=FillColor,RelativeSource={RelativeSource TemplatedParent}}\" />
    <SolidColorBrush x:Key = \"ParticleBackgroundColor\" Color = \"Transparent\"/>
    <system:Double x:Key = \"ParticleOpacity\">1</system:Double>
    <system:Double x:Key = \"ParticleRadius\">5</system:Double>

    <system:Double x:Key = \"StartingPointX\">0</system:Double>
    <system:Double x:Key = \"StartingPointY\">-20</system:Double>

    <system:Double x:Key = \"RotationPointX\">0.5</system:Double>
    <system:Double x:Key = \"RotationPointY\">0.5</system:Double>

    <!-- StoryBoard -->
    <system:TimeSpan x:Key = \"StoryBoardBeginTimeP0\">00:00:00.000</system:TimeSpan>
    <system:TimeSpan x:Key = \"StoryBoardBeginTimeP1\">00:00:00.100</system:TimeSpan>
    <system:TimeSpan x:Key = \"StoryBoardBeginTimeP2\">00:00:00.200</system:TimeSpan>
    <system:TimeSpan x:Key = \"StoryBoardBeginTimeP3\">00:00:00.300</system:TimeSpan>
    <system:TimeSpan x:Key = \"StoryBoardBeginTimeP4\">00:00:00.400</system:TimeSpan>
    <Duration x:Key = \"StoryBoardDuration\">00:00:01.800</Duration>

    <!-- Particle Origin Angles -->
    <system:Double x:Key = \"ParticleOriginAngleP0\">0</system:Double>
    <system:Double x:Key = \"ParticleOriginAngleP1\">-10</system:Double>
    <system:Double x:Key = \"ParticleOriginAngleP2\">-20</system:Double>
    <system:Double x:Key = \"ParticleOriginAngleP3\">-30</system:Double>
    <system:Double x:Key = \"ParticleOriginAngleP4\">-40</system:Double>

    <!-- Particle Position & Timing 1 -->
    <system:Double x:Key = \"ParticleBeginAngle1\">0</system:Double>
    <system:Double x:Key = \"ParticleEndAngle1\">90</system:Double>
    <system:TimeSpan x:Key = \"ParticleBeginTime1\">00:00:00.000</system:TimeSpan>
    <Duration x:Key = \"ParticleDuration1\">00:00:00.750</Duration>

    <!-- Particle Position & Timing 2 -->
    <system:Double x:Key = \"ParticleBeginAngle2\">90</system:Double>
    <system:Double x:Key = \"ParticleEndAngle2\">270</system:Double>
    <system:TimeSpan x:Key = \"ParticleBeginTime2\">00:00:00.751</system:TimeSpan>
    <Duration x:Key = \"ParticleDuration2\">00:00:00.300</Duration>

    <!-- Particle Position & Timing 3 -->
    <system:Double x:Key = \"ParticleBeginAngle3\">270</system:Double>
    <system:Double x:Key = \"ParticleEndAngle3\">360</system:Double>
    <system:TimeSpan x:Key = \"ParticleBeginTime3\">00:00:01.052</system:TimeSpan>
    <Duration x:Key = \"ParticleDuration3\">00:00:00.750</Duration>

    <Style x:Key = \"EllipseStyle\" TargetType = \"Ellipse\">
     <Setter Property = \"Width\" Value = \"{StaticResource ParticleRadius}\"/>
     <Setter Property = \"Height\" Value = \"{StaticResource ParticleRadius}\"/>
     <Setter Property = \"Fill\" Value = \"{StaticResource ParticleColor}\"/>
     <Setter Property = \"RenderTransformOrigin\" Value = \"0.5, 0.5\"/>
     <Setter Property = \"Opacity\" Value = \"{StaticResource ParticleOpacity}\"/>
    </Style>
    </Grid.Resources>
    <Canvas Width = \"1\" Height = \"1\" Margin=\"0,0,0,0\">
    <Canvas.Triggers>
     <EventTrigger RoutedEvent = \"Canvas.Loaded\">
     <EventTrigger.Actions>
      <BeginStoryboard>
      <Storyboard
    
    BeginTime = \"{StaticResource StoryBoardBeginTimeP0}\"
    Duration = \"{StaticResource StoryBoardDuration}\"
    RepeatBehavior = \"Forever\">
       <DoubleAnimation
    Storyboard.TargetName = \"p0\"
    Storyboard.TargetProperty = \"(UIElement.RenderTransform).(RotateTransform.Angle)\"
    From = \"{StaticResource ParticleBeginAngle1}\"
    To = \"{StaticResource ParticleEndAngle1}\"
    BeginTime = \"{StaticResource ParticleBeginTime1}\"
    Duration = \"{StaticResource ParticleDuration1}\"/>
       <DoubleAnimation
    Storyboard.TargetName = \"p0\"
    Storyboard.TargetProperty = \"(UIElement.RenderTransform).(RotateTransform.Angle)\"
    From = \"{StaticResource ParticleBeginAngle2}\"
    To = \"{StaticResource ParticleEndAngle2}\"
    BeginTime = \"{StaticResource ParticleBeginTime2}\"
    Duration = \"{StaticResource ParticleDuration2}\"/>
       <DoubleAnimation
    Storyboard.TargetName = \"p0\"
    Storyboard.TargetProperty = \"(UIElement.RenderTransform).(RotateTransform.Angle)\"
    From = \"{StaticResource ParticleBeginAngle3}\"
    To = \"{StaticResource ParticleEndAngle3}\"
    BeginTime = \"{StaticResource ParticleBeginTime3}\"
    Duration = \"{StaticResource ParticleDuration3}\"/>
      </Storyboard>
      </BeginStoryboard>
      <BeginStoryboard>
      <Storyboard
    
    BeginTime = \"{StaticResource StoryBoardBeginTimeP1}\"
    Duration = \"{StaticResource StoryBoardDuration}\"
    RepeatBehavior = \"Forever\">

       <DoubleAnimation
    Storyboard.TargetName = \"p1\"
    Storyboard.TargetProperty = \"(UIElement.RenderTransform).(RotateTransform.Angle)\"
    From = \"{StaticResource ParticleBeginAngle1}\"
    To = \"{StaticResource ParticleEndAngle1}\"
    BeginTime = \"{StaticResource ParticleBeginTime1}\"
    Duration = \"{StaticResource ParticleDuration1}\"/>
       <DoubleAnimation
    Storyboard.TargetName = \"p1\"
    Storyboard.TargetProperty = \"(UIElement.RenderTransform).(RotateTransform.Angle)\"
    From = \"{StaticResource ParticleBeginAngle2}\"
    To = \"{StaticResource ParticleEndAngle2}\"
    BeginTime = \"{StaticResource ParticleBeginTime2}\"
    Duration = \"{StaticResource ParticleDuration2}\"/>
       <DoubleAnimation
    Storyboard.TargetName = \"p1\"
    Storyboard.TargetProperty = \"(UIElement.RenderTransform).(RotateTransform.Angle)\"
    From = \"{StaticResource ParticleBeginAngle3}\"
    To = \"{StaticResource ParticleEndAngle3}\"
    BeginTime = \"{StaticResource ParticleBeginTime3}\"
    Duration = \"{StaticResource ParticleDuration3}\"/>
      </Storyboard>
      </BeginStoryboard>
      <BeginStoryboard>
      <Storyboard
    
    BeginTime = \"{StaticResource StoryBoardBeginTimeP2}\"
    Duration = \"{StaticResource StoryBoardDuration}\"
    RepeatBehavior = \"Forever\">

       <DoubleAnimation
    Storyboard.TargetName = \"p2\"
    Storyboard.TargetProperty = \"(UIElement.RenderTransform).(RotateTransform.Angle)\"
    From = \"{StaticResource ParticleBeginAngle1}\"
    To = \"{StaticResource ParticleEndAngle1}\"
    BeginTime = \"{StaticResource ParticleBeginTime1}\"
    Duration = \"{StaticResource ParticleDuration1}\"/>
       <DoubleAnimation
    Storyboard.TargetName = \"p2\"
    Storyboard.TargetProperty = \"(UIElement.RenderTransform).(RotateTransform.Angle)\"
    From = \"{StaticResource ParticleBeginAngle2}\"
    To = \"{StaticResource ParticleEndAngle2}\"
    BeginTime = \"{StaticResource ParticleBeginTime2}\"
    Duration = \"{StaticResource ParticleDuration2}\"/>
       <DoubleAnimation
    Storyboard.TargetName = \"p2\"
    Storyboard.TargetProperty = \"(UIElement.RenderTransform).(RotateTransform.Angle)\"
    From = \"{StaticResource ParticleBeginAngle3}\"
    To = \"{StaticResource ParticleEndAngle3}\"
    BeginTime = \"{StaticResource ParticleBeginTime3}\"
    Duration = \"{StaticResource ParticleDuration3}\"/>
      </Storyboard>
      </BeginStoryboard>

      <BeginStoryboard>
      <Storyboard
    
    BeginTime = \"{StaticResource StoryBoardBeginTimeP3}\"
    Duration = \"{StaticResource StoryBoardDuration}\"
    RepeatBehavior = \"Forever\">

       <DoubleAnimation
    Storyboard.TargetName = \"p3\"
    Storyboard.TargetProperty = \"(UIElement.RenderTransform).(RotateTransform.Angle)\"
    From = \"{StaticResource ParticleBeginAngle1}\"
    To = \"{StaticResource ParticleEndAngle1}\"
    BeginTime = \"{StaticResource ParticleBeginTime1}\"
    Duration = \"{StaticResource ParticleDuration1}\"/>
       <DoubleAnimation
    Storyboard.TargetName = \"p3\"
    Storyboard.TargetProperty = \"(UIElement.RenderTransform).(RotateTransform.Angle)\"
    From = \"{StaticResource ParticleBeginAngle2}\"
    To = \"{StaticResource ParticleEndAngle2}\"
    BeginTime = \"{StaticResource ParticleBeginTime2}\"
    Duration = \"{StaticResource ParticleDuration2}\"/>
       <DoubleAnimation
    Storyboard.TargetName = \"p3\"
    Storyboard.TargetProperty = \"(UIElement.RenderTransform).(RotateTransform.Angle)\"
    From = \"{StaticResource ParticleBeginAngle3}\"
    To = \"{StaticResource ParticleEndAngle3}\"
    BeginTime = \"{StaticResource ParticleBeginTime3}\"
    Duration = \"{StaticResource ParticleDuration3}\"/>
      </Storyboard>
      </BeginStoryboard>

      <BeginStoryboard>
      <Storyboard
    
    BeginTime = \"{StaticResource StoryBoardBeginTimeP4}\"
    Duration = \"{StaticResource StoryBoardDuration}\"
    RepeatBehavior = \"Forever\">

       <DoubleAnimation
    Storyboard.TargetName = \"p4\"
    Storyboard.TargetProperty = \"(UIElement.RenderTransform).(RotateTransform.Angle)\"
    From = \"{StaticResource ParticleBeginAngle1}\"
    To = \"{StaticResource ParticleEndAngle1}\"
    BeginTime = \"{StaticResource ParticleBeginTime1}\"
    Duration = \"{StaticResource ParticleDuration1}\"/>
       <DoubleAnimation
    Storyboard.TargetName = \"p4\"
    Storyboard.TargetProperty = \"(UIElement.RenderTransform).(RotateTransform.Angle)\"
    From = \"{StaticResource ParticleBeginAngle2}\"
    To = \"{StaticResource ParticleEndAngle2}\"
    BeginTime = \"{StaticResource ParticleBeginTime2}\"
    Duration = \"{StaticResource ParticleDuration2}\"/>
       <DoubleAnimation
    Storyboard.TargetName = \"p4\"
    Storyboard.TargetProperty = \"(UIElement.RenderTransform).(RotateTransform.Angle)\"
    From = \"{StaticResource ParticleBeginAngle3}\"
    To = \"{StaticResource ParticleEndAngle3}\"
    BeginTime = \"{StaticResource ParticleBeginTime3}\"
    Duration = \"{StaticResource ParticleDuration3}\"/>
      </Storyboard>
      </BeginStoryboard>
     </EventTrigger.Actions>
     </EventTrigger>
    </Canvas.Triggers>
    <Border
  x:Name = \"p0\"
  Background = \"{StaticResource ParticleBackgroundColor}\"
  Opacity = \"{StaticResource ParticleOpacity}\">
     <Border.RenderTransform>
     <RotateTransform/>
     </Border.RenderTransform>
     <Border.RenderTransformOrigin>
     <Point X = \"{StaticResource RotationPointX}\" Y = \"{StaticResource RotationPointY}\"/>
     </Border.RenderTransformOrigin>
     <Ellipse Style = \"{StaticResource EllipseStyle}\">
     <Ellipse.RenderTransform>
      <TransformGroup>
      <TranslateTransform X = \"{StaticResource StartingPointX}\" Y = \"{StaticResource StartingPointY}\"/>
      <RotateTransform Angle = \"{StaticResource ParticleOriginAngleP0}\"/>
      </TransformGroup>
     </Ellipse.RenderTransform>
     </Ellipse>
    </Border>
    <Border
  x:Name = \"p1\"
  Background = \"{StaticResource ParticleBackgroundColor}\"
  Opacity = \"{StaticResource ParticleOpacity}\">
     <Border.RenderTransform>
     <RotateTransform/>
     </Border.RenderTransform>
     <Border.RenderTransformOrigin>
     <Point X = \"{StaticResource RotationPointX}\" Y = \"{StaticResource RotationPointY}\"/>
     </Border.RenderTransformOrigin>
     <Ellipse Style = \"{StaticResource EllipseStyle}\">
     <Ellipse.RenderTransform>
      <TransformGroup>
      <TranslateTransform X = \"{StaticResource StartingPointX}\" Y = \"{StaticResource StartingPointY}\"/>
      <RotateTransform Angle = \"{StaticResource ParticleOriginAngleP1}\"/>
      </TransformGroup>
     </Ellipse.RenderTransform>
     </Ellipse>
    </Border>
    <Border
  x:Name = \"p2\"
  Background = \"{StaticResource ParticleBackgroundColor}\"
  Opacity = \"{StaticResource ParticleOpacity}\">
     <Border.RenderTransform>
     <RotateTransform/>
     </Border.RenderTransform>
     <Border.RenderTransformOrigin>
     <Point X = \"{StaticResource RotationPointX}\" Y = \"{StaticResource RotationPointY}\"/>
     </Border.RenderTransformOrigin>
     <Ellipse Style = \"{StaticResource EllipseStyle}\">
     <Ellipse.RenderTransform>
      <TransformGroup>
      <TranslateTransform X = \"{StaticResource StartingPointX}\" Y = \"{StaticResource StartingPointY}\"/>
      <RotateTransform Angle = \"{StaticResource ParticleOriginAngleP2}\"/>
      </TransformGroup>
     </Ellipse.RenderTransform>
     </Ellipse>
    </Border>
    <Border
  x:Name = \"p3\"
  Background = \"{StaticResource ParticleBackgroundColor}\"
  Opacity = \"{StaticResource ParticleOpacity}\">
     <Border.RenderTransform>
     <RotateTransform/>
     </Border.RenderTransform>
     <Border.RenderTransformOrigin>
     <Point X = \"{StaticResource RotationPointX}\" Y = \"{StaticResource RotationPointY}\"/>
     </Border.RenderTransformOrigin>
     <Ellipse Style = \"{StaticResource EllipseStyle}\">
     <Ellipse.RenderTransform>
      <TransformGroup>
      <TranslateTransform X = \"{StaticResource StartingPointX}\" Y = \"{StaticResource StartingPointY}\"/>
      <RotateTransform Angle = \"{StaticResource ParticleOriginAngleP3}\"/>
      </TransformGroup>
     </Ellipse.RenderTransform>
     </Ellipse>
    </Border>
    <Border
  x:Name = \"p4\"
  Background = \"{StaticResource ParticleBackgroundColor}\"
  Opacity = \"{StaticResource ParticleOpacity}\">
     <Border.RenderTransform>
     <RotateTransform/>
     </Border.RenderTransform>
     <Border.RenderTransformOrigin>
     <Point X = \"{StaticResource RotationPointX}\" Y = \"{StaticResource RotationPointY}\"/>
     </Border.RenderTransformOrigin>
     <Ellipse Style = \"{StaticResource EllipseStyle}\">
     <Ellipse.RenderTransform>
      <TransformGroup>
      <TranslateTransform X = \"{StaticResource StartingPointX}\" Y = \"{StaticResource StartingPointY}\"/>
      <RotateTransform Angle = \"{StaticResource ParticleOriginAngleP4}\"/>
      </TransformGroup>
     </Ellipse.RenderTransform>
     </Ellipse>
    </Border>
    </Canvas>
   </Grid>



   </Border>
  </ControlTemplate>
  </Setter.Value>
 </Setter>
 </Style>
 
 
 
</ResourceDictionary>

在构建中发现,一开始在设定绑定时,写成<SolidColorBrush x:Key = \"ParticleColor\" Color = \"{Binding Path=FillColor}\" />一直都无法绑定成功,后来查了资料,改成<SolidColorBrush x:Key = \"ParticleColor\" Color = \"{Binding Path=FillColor,RelativeSource={RelativeSource TemplatedParent}}\" /> 后成功。

4、编辑Loading.cs文件,对自定义属性FillColor和逻辑进行编码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace WpfControlLibraryDemo
{
 using System.ComponentModel;
 /// <summary>
 /// 按照步骤 1a 或 1b 操作,然后执行步骤 2 以在 XAML 文件中使用此自定义控件。
 ///
 /// 步骤 1a) 在当前项目中存在的 XAML 文件中使用该自定义控件。
 /// 将此 XmlNamespace 特性添加到要使用该特性的标记文件的根 
 /// 元素中:
 ///
 /// xmlns:MyNamespace=\"clr-namespace:WpfControlLibraryDemo\"
 ///
 ///
 /// 步骤 1b) 在其他项目中存在的 XAML 文件中使用该自定义控件。
 /// 将此 XmlNamespace 特性添加到要使用该特性的标记文件的根 
 /// 元素中:
 ///
 /// xmlns:MyNamespace=\"clr-namespace:WpfControlLibraryDemo;assembly=WpfControlLibraryDemo\"
 ///
 /// 您还需要添加一个从 XAML 文件所在的项目到此项目的项目引用,
 /// 并重新生成以避免编译错误:
 ///
 /// 在解决方案资源管理器中右击目标项目,然后依次单击
 /// “添加引用”->“项目”->[浏览查找并选择此项目]
 ///
 ///
 /// 步骤 2)
 /// 继续操作并在 XAML 文件中使用控件。
 ///
 /// <MyNamespace:Loading/>
 ///
 /// </summary>
 public class Loading : Control
 {
 static Loading()
 {
  //重载默认样式
  DefaultStyleKeyProperty.OverrideMetadata(typeof(Loading), new FrameworkPropertyMetadata(typeof(Loading)));
  //DependencyProperty 注册 FillColor
  FillColorProperty = DependencyProperty.Register(\"FillColor\",
  typeof(Color),
  typeof(Loading),
  new UIPropertyMetadata(Colors.DarkBlue,
  new PropertyChangedCallback(OnUriChanged))
  );
  //Colors.DarkBlue为控件初始化默认值

 }
 //属性变更回调函数
 private static void OnUriChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
 {
  //Border b = (Border)d;
  //MessageBox.Show(e.NewValue.ToString());

 }
 #region 自定义Fields
 // DependencyProperty属性定义 FillColorProperty=FillColor+Property组成
 public static readonly DependencyProperty FillColorProperty;
 #endregion
 //VS设计器属性支持
 [Description(\"背景色\"), Category(\"个性配置\"), DefaultValue(\"#FF668899\")]
 public Color FillColor
 {
  //GetValue,SetValue为固定写法,此处一般不建议处理其他逻辑
  get { return (Color)GetValue(FillColorProperty); }
  set { SetValue(FillColorProperty, value); }
 }
 }
}

 5、编译,如果无误后,可以添加WPF应用程序WpfAppLoadingTest进行测试(添加项目引用)。

打开MainWindow.xaml,将Loading控件拖放到设计界面上,如下图所示:

 6、控件颜色修改,选中控件,在属性栏中进行配置即可:

 7.总结

可以看到WPF自定义控件还是比较容易的,但是难点在于UI的设计,如果需要做的美观,需要美工的参与,而且需要转换成XAML。

以上就是WPF实现炫酷Loading控件的全部内容,希望对大家的学习有所帮助。

本文地址:https://www.stayed.cn/item/14333

转载请注明出处。

本站部分内容来源于网络,如侵犯到您的权益,请 联系我

我的博客

人生若只如初见,何事秋风悲画扇。