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

用WPF自定义一个Loading控件实现类似的效果,并可以让用户对Loading的颗粒(Particle)背景颜色进行自定义,话不多说,直接上代码:

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

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

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

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

3、如果报错找不到Loading类型,请编译,下面在Generic.xaml主题文件中对Loading的样式和内容进行定义(注意添加 xmlns:system = "clr-namespace:System;assembly=mscorlib"),代码如下:

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

  1 <ResourceDictionary
  2     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  3     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  4      xmlns:system = "clr-namespace:System;assembly=mscorlib"
  5     xmlns:local="clr-namespace:WpfControlLibraryDemo">
  6 
  7 
  8     <Style TargetType="{x:Type local:Loading}">
  9         <Setter Property="Template">
 10             <Setter.Value>
 11                 <ControlTemplate TargetType="{x:Type local:Loading}">
 12                     <Border Background="{TemplateBinding Background}"
 13                             BorderBrush="{TemplateBinding BorderBrush}"
 14                             BorderThickness="{TemplateBinding BorderThickness}">
 15                         <Grid Width = "50" Height = "50">
 16                             <Grid.Resources>
 17                                 <!-- Value Converters -->
 18                            
 19                                 <!-- Particle Styling ,must to has RelativeSource  -->
 20                                 <SolidColorBrush x:Key = "ParticleColor" Color = "{Binding Path=FillColor,RelativeSource={RelativeSource TemplatedParent}}" />
 21                                 <SolidColorBrush x:Key = "ParticleBackgroundColor" Color = "Transparent"/>
 22                                 <system:Double x:Key = "ParticleOpacity">1</system:Double>
 23                                 <system:Double x:Key = "ParticleRadius">5</system:Double>
 24 
 25                                 <system:Double x:Key = "StartingPointX">0</system:Double>
 26                                 <system:Double x:Key = "StartingPointY">-20</system:Double>
 27 
 28                                 <system:Double x:Key = "RotationPointX">0.5</system:Double>
 29                                 <system:Double x:Key = "RotationPointY">0.5</system:Double>
 30 
 31                                 <!-- StoryBoard -->
 32                                 <system:TimeSpan x:Key = "StoryBoardBeginTimeP0">00:00:00.000</system:TimeSpan>
 33                                 <system:TimeSpan x:Key = "StoryBoardBeginTimeP1">00:00:00.100</system:TimeSpan>
 34                                 <system:TimeSpan x:Key = "StoryBoardBeginTimeP2">00:00:00.200</system:TimeSpan>
 35                                 <system:TimeSpan x:Key = "StoryBoardBeginTimeP3">00:00:00.300</system:TimeSpan>
 36                                 <system:TimeSpan x:Key = "StoryBoardBeginTimeP4">00:00:00.400</system:TimeSpan>
 37                                 <Duration x:Key = "StoryBoardDuration">00:00:01.800</Duration>
 38 
 39                                 <!-- Particle Origin Angles -->
 40                                 <system:Double x:Key = "ParticleOriginAngleP0">0</system:Double>
 41                                 <system:Double x:Key = "ParticleOriginAngleP1">-10</system:Double>
 42                                 <system:Double x:Key = "ParticleOriginAngleP2">-20</system:Double>
 43                                 <system:Double x:Key = "ParticleOriginAngleP3">-30</system:Double>
 44                                 <system:Double x:Key = "ParticleOriginAngleP4">-40</system:Double>
 45 
 46                                 <!-- Particle Position & Timing 1 -->
 47                                 <system:Double x:Key = "ParticleBeginAngle1">0</system:Double>
 48                                 <system:Double x:Key = "ParticleEndAngle1">90</system:Double>
 49                                 <system:TimeSpan x:Key = "ParticleBeginTime1">00:00:00.000</system:TimeSpan>
 50                                 <Duration x:Key = "ParticleDuration1">00:00:00.750</Duration>
 51 
 52                                 <!-- Particle Position & Timing 2 -->
 53                                 <system:Double x:Key = "ParticleBeginAngle2">90</system:Double>
 54                                 <system:Double x:Key = "ParticleEndAngle2">270</system:Double>
 55                                 <system:TimeSpan x:Key = "ParticleBeginTime2">00:00:00.751</system:TimeSpan>
 56                                 <Duration x:Key = "ParticleDuration2">00:00:00.300</Duration>
 57 
 58                                 <!-- Particle Position & Timing 3 -->
 59                                 <system:Double x:Key = "ParticleBeginAngle3">270</system:Double>
 60                                 <system:Double x:Key = "ParticleEndAngle3">360</system:Double>
 61                                 <system:TimeSpan x:Key = "ParticleBeginTime3">00:00:01.052</system:TimeSpan>
 62                                 <Duration x:Key = "ParticleDuration3">00:00:00.750</Duration>
 63 
 64                                 <Style x:Key = "EllipseStyle" TargetType = "Ellipse">
 65                                     <Setter Property = "Width" Value = "{StaticResource ParticleRadius}"/>
 66                                     <Setter Property = "Height" Value = "{StaticResource ParticleRadius}"/>
 67                                     <Setter Property = "Fill" Value = "{StaticResource ParticleColor}"/>
 68                                     <Setter Property = "RenderTransformOrigin" Value = "0.5, 0.5"/>
 69                                     <Setter Property = "Opacity" Value = "{StaticResource ParticleOpacity}"/>
 70                                 </Style>
 71                             </Grid.Resources>
 72                             <Canvas Width = "1" Height = "1" Margin="0,0,0,0">
 73                                 <Canvas.Triggers>
 74                                     <EventTrigger RoutedEvent = "Canvas.Loaded">
 75                                         <EventTrigger.Actions>
 76                                             <BeginStoryboard>
 77                                                 <Storyboard
 78                          
 79                             BeginTime = "{StaticResource StoryBoardBeginTimeP0}"
 80                             Duration = "{StaticResource StoryBoardDuration}"
 81                             RepeatBehavior = "Forever">
 82                                                     <DoubleAnimation
 83                                 Storyboard.TargetName = "p0"
 84                                 Storyboard.TargetProperty = "(UIElement.RenderTransform).(RotateTransform.Angle)"
 85                                 From = "{StaticResource ParticleBeginAngle1}"
 86                                 To = "{StaticResource ParticleEndAngle1}"
 87                                 BeginTime = "{StaticResource ParticleBeginTime1}"
 88                                 Duration = "{StaticResource ParticleDuration1}"/>
 89                                                     <DoubleAnimation
 90                                 Storyboard.TargetName = "p0"
 91                                 Storyboard.TargetProperty = "(UIElement.RenderTransform).(RotateTransform.Angle)"
 92                                 From = "{StaticResource ParticleBeginAngle2}"
 93                                 To = "{StaticResource ParticleEndAngle2}"
 94                                 BeginTime = "{StaticResource ParticleBeginTime2}"
 95                                 Duration = "{StaticResource ParticleDuration2}"/>
 96                                                     <DoubleAnimation
 97                                 Storyboard.TargetName = "p0"
 98                                 Storyboard.TargetProperty = "(UIElement.RenderTransform).(RotateTransform.Angle)"
 99                                 From = "{StaticResource ParticleBeginAngle3}"
100                                 To = "{StaticResource ParticleEndAngle3}"
101                                 BeginTime = "{StaticResource ParticleBeginTime3}"
102                                 Duration = "{StaticResource ParticleDuration3}"/>
103                                                 </Storyboard>
104                                             </BeginStoryboard>
105                                             <BeginStoryboard>
106                                                 <Storyboard
107                             
108                             BeginTime = "{StaticResource StoryBoardBeginTimeP1}"
109                             Duration = "{StaticResource StoryBoardDuration}"
110                             RepeatBehavior = "Forever">
111 
112                                                     <DoubleAnimation
113                                 Storyboard.TargetName = "p1"
114                                 Storyboard.TargetProperty = "(UIElement.RenderTransform).(RotateTransform.Angle)"
115                                 From = "{StaticResource ParticleBeginAngle1}"
116                                 To = "{StaticResource ParticleEndAngle1}"
117                                 BeginTime = "{StaticResource ParticleBeginTime1}"
118                                 Duration = "{StaticResource ParticleDuration1}"/>
119                                                     <DoubleAnimation
120                                 Storyboard.TargetName = "p1"
121                                 Storyboard.TargetProperty = "(UIElement.RenderTransform).(RotateTransform.Angle)"
122                                 From = "{StaticResource ParticleBeginAngle2}"
123                                 To = "{StaticResource ParticleEndAngle2}"
124                                 BeginTime = "{StaticResource ParticleBeginTime2}"
125                                 Duration = "{StaticResource ParticleDuration2}"/>
126                                                     <DoubleAnimation
127                                 Storyboard.TargetName = "p1"
128                                 Storyboard.TargetProperty = "(UIElement.RenderTransform).(RotateTransform.Angle)"
129                                 From = "{StaticResource ParticleBeginAngle3}"
130                                 To = "{StaticResource ParticleEndAngle3}"
131                                 BeginTime = "{StaticResource ParticleBeginTime3}"
132                                 Duration = "{StaticResource ParticleDuration3}"/>
133                                                 </Storyboard>
134                                             </BeginStoryboard>
135                                             <BeginStoryboard>
136                                                 <Storyboard
137                             
138                             BeginTime = "{StaticResource StoryBoardBeginTimeP2}"
139                             Duration = "{StaticResource StoryBoardDuration}"
140                             RepeatBehavior = "Forever">
141 
142                                                     <DoubleAnimation
143                                 Storyboard.TargetName = "p2"
144                                 Storyboard.TargetProperty = "(UIElement.RenderTransform).(RotateTransform.Angle)"
145                                 From = "{StaticResource ParticleBeginAngle1}"
146                                 To = "{StaticResource ParticleEndAngle1}"
147                                 BeginTime = "{StaticResource ParticleBeginTime1}"
148                                 Duration = "{StaticResource ParticleDuration1}"/>
149                                                     <DoubleAnimation
150                                 Storyboard.TargetName = "p2"
151                                 Storyboard.TargetProperty = "(UIElement.RenderTransform).(RotateTransform.Angle)"
152                                 From = "{StaticResource ParticleBeginAngle2}"
153                                 To = "{StaticResource ParticleEndAngle2}"
154                                 BeginTime = "{StaticResource ParticleBeginTime2}"
155                                 Duration = "{StaticResource ParticleDuration2}"/>
156                                                     <DoubleAnimation
157                                 Storyboard.TargetName = "p2"
158                                 Storyboard.TargetProperty = "(UIElement.RenderTransform).(RotateTransform.Angle)"
159                                 From = "{StaticResource ParticleBeginAngle3}"
160                                 To = "{StaticResource ParticleEndAngle3}"
161                                 BeginTime = "{StaticResource ParticleBeginTime3}"
162                                 Duration = "{StaticResource ParticleDuration3}"/>
163                                                 </Storyboard>
164                                             </BeginStoryboard>
165 
166                                             <BeginStoryboard>
167                                                 <Storyboard
168                             
169                             BeginTime = "{StaticResource StoryBoardBeginTimeP3}"
170                             Duration = "{StaticResource StoryBoardDuration}"
171                             RepeatBehavior = "Forever">
172 
173                                                     <DoubleAnimation
174                                 Storyboard.TargetName = "p3"
175                                 Storyboard.TargetProperty = "(UIElement.RenderTransform).(RotateTransform.Angle)"
176                                 From = "{StaticResource ParticleBeginAngle1}"
177                                 To = "{StaticResource ParticleEndAngle1}"
178                                 BeginTime = "{StaticResource ParticleBeginTime1}"
179                                 Duration = "{StaticResource ParticleDuration1}"/>
180                                                     <DoubleAnimation
181                                 Storyboard.TargetName = "p3"
182                                 Storyboard.TargetProperty = "(UIElement.RenderTransform).(RotateTransform.Angle)"
183                                 From = "{StaticResource ParticleBeginAngle2}"
184                                 To = "{StaticResource ParticleEndAngle2}"
185                                 BeginTime = "{StaticResource ParticleBeginTime2}"
186                                 Duration = "{StaticResource ParticleDuration2}"/>
187                                                     <DoubleAnimation
188                                 Storyboard.TargetName = "p3"
189                                 Storyboard.TargetProperty = "(UIElement.RenderTransform).(RotateTransform.Angle)"
190                                 From = "{StaticResource ParticleBeginAngle3}"
191                                 To = "{StaticResource ParticleEndAngle3}"
192                                 BeginTime = "{StaticResource ParticleBeginTime3}"
193                                 Duration = "{StaticResource ParticleDuration3}"/>
194                                                 </Storyboard>
195                                             </BeginStoryboard>
196 
197                                             <BeginStoryboard>
198                                                 <Storyboard
199                             
200                             BeginTime = "{StaticResource StoryBoardBeginTimeP4}"
201                             Duration = "{StaticResource StoryBoardDuration}"
202                             RepeatBehavior = "Forever">
203 
204                                                     <DoubleAnimation
205                                 Storyboard.TargetName = "p4"
206                                 Storyboard.TargetProperty = "(UIElement.RenderTransform).(RotateTransform.Angle)"
207                                 From = "{StaticResource ParticleBeginAngle1}"
208                                 To = "{StaticResource ParticleEndAngle1}"
209                                 BeginTime = "{StaticResource ParticleBeginTime1}"
210                                 Duration = "{StaticResource ParticleDuration1}"/>
211                                                     <DoubleAnimation
212                                 Storyboard.TargetName = "p4"
213                                 Storyboard.TargetProperty = "(UIElement.RenderTransform).(RotateTransform.Angle)"
214                                 From = "{StaticResource ParticleBeginAngle2}"
215                                 To = "{StaticResource ParticleEndAngle2}"
216                                 BeginTime = "{StaticResource ParticleBeginTime2}"
217                                 Duration = "{StaticResource ParticleDuration2}"/>
218                                                     <DoubleAnimation
219                                 Storyboard.TargetName = "p4"
220                                 Storyboard.TargetProperty = "(UIElement.RenderTransform).(RotateTransform.Angle)"
221                                 From = "{StaticResource ParticleBeginAngle3}"
222                                 To = "{StaticResource ParticleEndAngle3}"
223                                 BeginTime = "{StaticResource ParticleBeginTime3}"
224                                 Duration = "{StaticResource ParticleDuration3}"/>
225                                                 </Storyboard>
226                                             </BeginStoryboard>
227                                         </EventTrigger.Actions>
228                                     </EventTrigger>
229                                 </Canvas.Triggers>
230                                 <Border
231             x:Name = "p0"
232             Background = "{StaticResource ParticleBackgroundColor}"
233             Opacity = "{StaticResource ParticleOpacity}">
234                                     <Border.RenderTransform>
235                                         <RotateTransform/>
236                                     </Border.RenderTransform>
237                                     <Border.RenderTransformOrigin>
238                                         <Point X = "{StaticResource RotationPointX}" Y = "{StaticResource RotationPointY}"/>
239                                     </Border.RenderTransformOrigin>
240                                     <Ellipse Style = "{StaticResource EllipseStyle}">
241                                         <Ellipse.RenderTransform>
242                                             <TransformGroup>
243                                                 <TranslateTransform X = "{StaticResource StartingPointX}" Y = "{StaticResource StartingPointY}"/>
244                                                 <RotateTransform Angle = "{StaticResource ParticleOriginAngleP0}"/>
245                                             </TransformGroup>
246                                         </Ellipse.RenderTransform>
247                                     </Ellipse>
248                                 </Border>
249                                 <Border
250             x:Name = "p1"
251             Background = "{StaticResource ParticleBackgroundColor}"
252             Opacity = "{StaticResource ParticleOpacity}">
253                                     <Border.RenderTransform>
254                                         <RotateTransform/>
255                                     </Border.RenderTransform>
256                                     <Border.RenderTransformOrigin>
257                                         <Point X = "{StaticResource RotationPointX}" Y = "{StaticResource RotationPointY}"/>
258                                     </Border.RenderTransformOrigin>
259                                     <Ellipse Style = "{StaticResource EllipseStyle}">
260                                         <Ellipse.RenderTransform>
261                                             <TransformGroup>
262                                                 <TranslateTransform X = "{StaticResource StartingPointX}" Y = "{StaticResource StartingPointY}"/>
263                                                 <RotateTransform Angle = "{StaticResource ParticleOriginAngleP1}"/>
264                                             </TransformGroup>
265                                         </Ellipse.RenderTransform>
266                                     </Ellipse>
267                                 </Border>
268                                 <Border
269             x:Name = "p2"
270             Background = "{StaticResource ParticleBackgroundColor}"
271             Opacity = "{StaticResource ParticleOpacity}">
272                                     <Border.RenderTransform>
273                                         <RotateTransform/>
274                                     </Border.RenderTransform>
275                                     <Border.RenderTransformOrigin>
276                                         <Point X = "{StaticResource RotationPointX}" Y = "{StaticResource RotationPointY}"/>
277                                     </Border.RenderTransformOrigin>
278                                     <Ellipse Style = "{StaticResource EllipseStyle}">
279                                         <Ellipse.RenderTransform>
280                                             <TransformGroup>
281                                                 <TranslateTransform X = "{StaticResource StartingPointX}" Y = "{StaticResource StartingPointY}"/>
282                                                 <RotateTransform Angle = "{StaticResource ParticleOriginAngleP2}"/>
283                                             </TransformGroup>
284                                         </Ellipse.RenderTransform>
285                                     </Ellipse>
286                                 </Border>
287                                 <Border
288             x:Name = "p3"
289             Background = "{StaticResource ParticleBackgroundColor}"
290             Opacity = "{StaticResource ParticleOpacity}">
291                                     <Border.RenderTransform>
292                                         <RotateTransform/>
293                                     </Border.RenderTransform>
294                                     <Border.RenderTransformOrigin>
295                                         <Point X = "{StaticResource RotationPointX}" Y = "{StaticResource RotationPointY}"/>
296                                     </Border.RenderTransformOrigin>
297                                     <Ellipse Style = "{StaticResource EllipseStyle}">
298                                         <Ellipse.RenderTransform>
299                                             <TransformGroup>
300                                                 <TranslateTransform X = "{StaticResource StartingPointX}" Y = "{StaticResource StartingPointY}"/>
301                                                 <RotateTransform Angle = "{StaticResource ParticleOriginAngleP3}"/>
302                                             </TransformGroup>
303                                         </Ellipse.RenderTransform>
304                                     </Ellipse>
305                                 </Border>
306                                 <Border
307             x:Name = "p4"
308             Background = "{StaticResource ParticleBackgroundColor}"
309             Opacity = "{StaticResource ParticleOpacity}">
310                                     <Border.RenderTransform>
311                                         <RotateTransform/>
312                                     </Border.RenderTransform>
313                                     <Border.RenderTransformOrigin>
314                                         <Point X = "{StaticResource RotationPointX}" Y = "{StaticResource RotationPointY}"/>
315                                     </Border.RenderTransformOrigin>
316                                     <Ellipse Style = "{StaticResource EllipseStyle}">
317                                         <Ellipse.RenderTransform>
318                                             <TransformGroup>
319                                                 <TranslateTransform X = "{StaticResource StartingPointX}" Y = "{StaticResource StartingPointY}"/>
320                                                 <RotateTransform Angle = "{StaticResource ParticleOriginAngleP4}"/>
321                                             </TransformGroup>
322                                         </Ellipse.RenderTransform>
323                                     </Ellipse>
324                                 </Border>
325                             </Canvas>
326                         </Grid>
327 
328 
329 
330                     </Border>
331                 </ControlTemplate>
332             </Setter.Value>
333         </Setter>
334     </Style>
335     
336     
337     
338 </ResourceDictionary>

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

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

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

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

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading.Tasks;
 6 using System.Windows;
 7 using System.Windows.Controls;
 8 using System.Windows.Data;
 9 using System.Windows.Documents;
10 using System.Windows.Input;
11 using System.Windows.Media;
12 using System.Windows.Media.Imaging;
13 using System.Windows.Navigation;
14 using System.Windows.Shapes;
15 
16 namespace WpfControlLibraryDemo
17 {
18     using System.ComponentModel;
19     /// <summary>
20     /// 按照步骤 1a 或 1b 操作,然后执行步骤 2 以在 XAML 文件中使用此自定义控件。
21     ///
22     /// 步骤 1a) 在当前项目中存在的 XAML 文件中使用该自定义控件。
23     /// 将此 XmlNamespace 特性添加到要使用该特性的标记文件的根 
24     /// 元素中:
25     ///
26     ///     xmlns:MyNamespace="clr-namespace:WpfControlLibraryDemo"
27     ///
28     ///
29     /// 步骤 1b) 在其他项目中存在的 XAML 文件中使用该自定义控件。
30     /// 将此 XmlNamespace 特性添加到要使用该特性的标记文件的根 
31     /// 元素中:
32     ///
33     ///     xmlns:MyNamespace="clr-namespace:WpfControlLibraryDemo;assembly=WpfControlLibraryDemo"
34     ///
35     /// 您还需要添加一个从 XAML 文件所在的项目到此项目的项目引用,
36     /// 并重新生成以避免编译错误:
37     ///
38     ///     在解决方案资源管理器中右击目标项目,然后依次单击
39     ///     “添加引用”->“项目”->[浏览查找并选择此项目]
40     ///
41     ///
42     /// 步骤 2)
43     /// 继续操作并在 XAML 文件中使用控件。
44     ///
45     ///     <MyNamespace:Loading/>
46     ///
47     /// </summary>
48     public class Loading : Control
49     {
50         static Loading()
51         {
52             //重载默认样式
53             DefaultStyleKeyProperty.OverrideMetadata(typeof(Loading), new FrameworkPropertyMetadata(typeof(Loading)));
54             //DependencyProperty 注册  FillColor
55             FillColorProperty = DependencyProperty.Register("FillColor",
56                 typeof(Color),
57                 typeof(Loading),
58                 new UIPropertyMetadata(Colors.DarkBlue,
59                 new PropertyChangedCallback(OnUriChanged))
60                 );
61             //Colors.DarkBlue为控件初始化默认值
62 
63         }
64         //属性变更回调函数
65         private static void OnUriChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
66         {
67             //Border b = (Border)d;
68             //MessageBox.Show(e.NewValue.ToString());
69 
70         }
71         #region 自定义Fields
72         // DependencyProperty属性定义   FillColorProperty=FillColor+Property组成
73         public static readonly DependencyProperty FillColorProperty;
74         #endregion
75         //VS设计器属性支持
76         [Description("背景色"), Category("个性配置"), DefaultValue("#FF668899")]
77         public Color FillColor
78         {
79             //GetValue,SetValue为固定写法,此处一般不建议处理其他逻辑
80             get { return (Color)GetValue(FillColorProperty); }
81             set { SetValue(FillColorProperty, value); }
82         }
83     }
84 }

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

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

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

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

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

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

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

7.总结

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