超炫酷的WPF實(shí)現(xiàn)Loading控件效果
Win8系統(tǒng)的Loading效果還是很不錯(cuò)的,網(wǎng)上也有人用CSS3等技術(shù)實(shí)現(xiàn),研究了一下,并打算用WPF自定義一個(gè)Loading控件實(shí)現(xiàn)類(lèi)似的效果,并可以讓用戶(hù)對(duì)Loading的顆粒(Particle)背景顏色進(jìn)行自定義,話(huà)不多說(shuō),直接上代碼:
1、用VS2012新建一個(gè)WPF的用戶(hù)控件庫(kù)項(xiàng)目WpfControlLibraryDemo,VS自動(dòng)生成如下結(jié)構(gòu):

2、刪除UserControl1.xaml,并新建一個(gè)Loading的CustomControl(不是UserControl),如下圖所示:

3、如果報(bào)錯(cuò)找不到Loading類(lèi)型,請(qǐng)編譯,下面在Generic.xaml主題文件中對(duì)Loading的樣式和內(nèi)容進(jìn)行定義(注意添加
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>
在構(gòu)建中發(fā)現(xiàn),一開(kāi)始在設(shè)定綁定時(shí),寫(xiě)成<SolidColorBrush x:Key = "ParticleColor" Color = "{Binding Path=FillColor}" />一直都無(wú)法綁定成功,后來(lái)查了資料,改成<SolidColorBrush x:Key = "ParticleColor" Color = "{Binding Path=FillColor,RelativeSource={RelativeSource TemplatedParent}}" /> 后成功。
4、編輯Loading.cs文件,對(duì)自定義屬性FillColor和邏輯進(jìn)行編碼:
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 操作,然后執(zhí)行步驟 2 以在 XAML 文件中使用此自定義控件。
///
/// 步驟 1a) 在當(dāng)前項(xiàng)目中存在的 XAML 文件中使用該自定義控件。
/// 將此 XmlNamespace 特性添加到要使用該特性的標(biāo)記文件的根
/// 元素中:
///
/// xmlns:MyNamespace="clr-namespace:WpfControlLibraryDemo"
///
///
/// 步驟 1b) 在其他項(xiàng)目中存在的 XAML 文件中使用該自定義控件。
/// 將此 XmlNamespace 特性添加到要使用該特性的標(biāo)記文件的根
/// 元素中:
///
/// xmlns:MyNamespace="clr-namespace:WpfControlLibraryDemo;assembly=WpfControlLibraryDemo"
///
/// 您還需要添加一個(gè)從 XAML 文件所在的項(xiàng)目到此項(xiàng)目的項(xiàng)目引用,
/// 并重新生成以避免編譯錯(cuò)誤:
///
/// 在解決方案資源管理器中右擊目標(biāo)項(xiàng)目,然后依次單擊
/// “添加引用”->“項(xiàng)目”->[瀏覽查找并選擇此項(xiàng)目]
///
///
/// 步驟 2)
/// 繼續(xù)操作并在 XAML 文件中使用控件。
///
/// <MyNamespace:Loading/>
///
/// </summary>
public class Loading : Control
{
static Loading()
{
//重載默認(rèn)樣式
DefaultStyleKeyProperty.OverrideMetadata(typeof(Loading), new FrameworkPropertyMetadata(typeof(Loading)));
//DependencyProperty 注冊(cè) FillColor
FillColorProperty = DependencyProperty.Register("FillColor",
typeof(Color),
typeof(Loading),
new UIPropertyMetadata(Colors.DarkBlue,
new PropertyChangedCallback(OnUriChanged))
);
//Colors.DarkBlue為控件初始化默認(rèn)值
}
//屬性變更回調(diào)函數(shù)
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設(shè)計(jì)器屬性支持
[Description("背景色"), Category("個(gè)性配置"), DefaultValue("#FF668899")]
public Color FillColor
{
//GetValue,SetValue為固定寫(xiě)法,此處一般不建議處理其他邏輯
get { return (Color)GetValue(FillColorProperty); }
set { SetValue(FillColorProperty, value); }
}
}
}
5、編譯,如果無(wú)誤后,可以添加WPF應(yīng)用程序WpfAppLoadingTest進(jìn)行測(cè)試(添加項(xiàng)目引用)。

打開(kāi)MainWindow.xaml,將Loading控件拖放到設(shè)計(jì)界面上,如下圖所示:

6、控件顏色修改,選中控件,在屬性欄中進(jìn)行配置即可:

7.總結(jié)
可以看到WPF自定義控件還是比較容易的,但是難點(diǎn)在于UI的設(shè)計(jì),如果需要做的美觀,需要美工的參與,而且需要轉(zhuǎn)換成XAML。
以上就是WPF實(shí)現(xiàn)炫酷Loading控件的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助。
- WPF自定義控件和樣式之自定義按鈕(Button)
- WPF如何自定義TabControl控件樣式示例詳解
- WPF的ListView控件自定義布局用法實(shí)例
- 在WPF中動(dòng)態(tài)加載XAML中的控件實(shí)例代碼
- C# 使用WPF 用MediaElement控件實(shí)現(xiàn)視頻循環(huán)播放
- WPF自定義TreeView控件樣式實(shí)現(xiàn)QQ聯(lián)系人列表效果
- WPF實(shí)現(xiàn)ScrollViewer滾動(dòng)到指定控件處
- WPF實(shí)現(xiàn)帶全選復(fù)選框的列表控件
- WPF開(kāi)發(fā)技巧之花式控件功能擴(kuò)展詳解
相關(guān)文章
Unity實(shí)現(xiàn)顏色漸變滑動(dòng)條
這篇文章主要為大家詳細(xì)介紹了Unity實(shí)現(xiàn)顏色漸變滑動(dòng)條,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-07-07
C#實(shí)現(xiàn)獲取電腦硬件顯卡核心代號(hào)信息
這篇文章主要為大家詳細(xì)介紹了如何利用C#實(shí)現(xiàn)獲取電腦硬件顯卡核心代號(hào)信息,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2024-01-01
基于數(shù)據(jù)類(lèi)型轉(zhuǎn)換(裝箱與拆箱)與常量詳解
下面小編就為大家分享一篇基于數(shù)據(jù)類(lèi)型轉(zhuǎn)換(裝箱與拆箱)與常量詳解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2017-11-11
C#中async/await之線(xiàn)程上下文工作原理
這篇文章主要為大家介紹了C#中async/await之線(xiàn)程上下文工作原理解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪<BR>2023-05-05
C#與SQL連接:GridView控件對(duì)數(shù)據(jù)庫(kù)的操作
GridView控件操作方面的知識(shí),需要的朋友可以參考一下2013-02-02
使用C#實(shí)現(xiàn)簡(jiǎn)單的線(xiàn)性回歸的代碼詳解
最近注意到了NumSharp,想學(xué)習(xí)一下,最好的學(xué)習(xí)方式就是去實(shí)踐,因此從github上找了一個(gè)用python實(shí)現(xiàn)的簡(jiǎn)單線(xiàn)性回歸代碼,然后基于NumSharp用C#進(jìn)行了改寫(xiě),需要的朋友可以參考下2024-01-01

