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"),代碼如下:
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>
在構建中發現,一開始在設定綁定時,寫成<SolidColorBrush x:Key = "ParticleColor" Color = "{Binding Path=FillColor}" />一直都無法綁定成功,后來查了資料,改成<SolidColorBrush x:Key = "ParticleColor" Color = "{Binding Path=FillColor,RelativeSource={RelativeSource TemplatedParent}}" /> 后成功。
4、編輯Loading.cs文件,對自定義屬性FillColor和邏輯進行編碼:
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 }
5、編譯,如果無誤后,可以添加WPF應用程序WpfAppLoadingTest進行測試(添加項目引用)。
打開MainWindow.xaml,將Loading控件拖放到設計界面上,如下圖所示:
6、控件顏色修改,選中控件,在屬性欄中進行配置即可:
7.總結
可以看到WPF自定義控件還是比較容易的,但是難點在於UI的設計,如果需要做的美觀,需要美工的參與,而且需要轉換成XAML。