这一讲是关于Silverlight的自定义控件的加载图标学习,我做了一个自定义加载。效果图下
一。首先介绍一下关于Silverlight的自定义控件的步骤以加载图标为例:
步骤1.创建一个DependencyPropertyCloner的静态类用于克隆在界面输入的值,来改变效果。代码如下:
using System;
using System.Reflection;
using System.Windows;
namespace System.Windons.Control.Extend
{
/// <summary>
/// 克隆注入对象的类型
/// </summary>
public static class DependencyPropertyCloner
{
/// <summary>
/// 克隆一个注入对象的类型
/// </summary>
/// <typeparam name="T">克隆注入对象的类型</typeparam>
/// <param name="source">克隆注入对象source</param>
/// <returns></returns>
public static T Clone<T>(this T source) where T : DependencyObject
{
//用反射创建对象的实例
T clone = (T)Activator.CreateInstance(source.GetType());
//克隆对象的注入属性
CloneAllDependencyProperties<T>(source, clone);
//克隆对象的所以属性
CloneAllProperties<T>(source, clone);
//返回克隆后的对象
return clone;
}
/// <summary>
/// 克隆注入对象的所有属性
/// </summary>
/// <typeparam name="T">克隆注入对象的类型</typeparam>
/// <param name="source">克隆注入对象source</param>
/// <param name="clone">克隆后的对象</param>
private static void CloneAllProperties<T>(T source, T clone) where T : DependencyObject
{
Type t = source.GetType();
//取得对象的属性的数据
PropertyInfo[] propertyInfos = t.GetProperties();
foreach (PropertyInfo item in propertyInfos)
{
if (item.Name != "Name" &&
item.Name != "Parent" &&
item.CanRead && item.CanWrite &&
!item.PropertyType.IsArray &&
item.GetIndexParameters().Length == 0 &&
item.GetValue(source, null) != null)
{
try
{
//将item赋值clone
item.SetValue(clone, item.GetValue(source, null), null);
}
catch (TargetInvocationException e)
{
e.ToString();
}
catch (MethodAccessException e1)
{
e1.ToString();
}
}
//从item中获取IList的类型的集合
else if (item.PropertyType.GetInterface("IList", true) != null)
{
//当在派生类中重写时,使用指定的绑定约束并匹配指定的参数列表、修饰符和区域性,调用指定成员。
//在item.GetValue(source, null)调用get_Count方法(屏蔽构造器或初始化器)
int cnt = (int)item.PropertyType.InvokeMember(
"get_Count",
BindingFlags.InvokeMethod, null, item.GetValue(source, null), null);
for (int i = 0; i < cnt; i++)
{
object val = item.PropertyType.InvokeMember(
"get_Item",
BindingFlags.InvokeMethod, null, item.GetValue(source, null), new object[] { i });
object nval = val;
DependencyObject v = val as DependencyObject;
if (v != null)
{
nval = v.Clone();
}
try
{
item.PropertyType.InvokeMember("Add", BindingFlags.InvokeMethod, null, item.GetValue(clone, null), new object[] { nval });
}
catch (TargetInvocationException e)
{
e.ToString();
}
}
}
}
}
/// <summary>
/// 克隆注入对象的注入属性
/// </summary>
/// <typeparam name="T">克隆注入对象的类型</typeparam>
/// <param name="source">克隆注入对象source</param>
/// <param name="clone">克隆后的对象</param>
private static void CloneAllDependencyProperties<T>(T source, T clone) where T : DependencyObject
{
Type t = source.GetType();
Type wt = t;
//如果wt的直接父类不是DependencyObject
while (wt.BaseType != typeof(DependencyObject))
{
FieldInfo[] fields = wt.GetFields(BindingFlags.Static | BindingFlags.Public);
foreach (var fi in fields)
{
DependencyProperty dp = fi.GetValue(source) as DependencyProperty;
if (dp != null && fi.Name != "NameProperty")
{
DependencyObject obj = source.GetValue(dp) as DependencyObject;
if (obj != null)
{
object o = obj.Clone();
clone.SetValue(dp, o);
}
else
{
if (fi.Name != "CountProperty" &&
fi.Name != "GeometryTransformProperty" &&
fi.Name != "ActualWidthProperty" &&
fi.Name != "ActualHeightProperty" &&
fi.Name != "MaxWidthProperty" &&
fi.Name != "MaxHeightProperty" &&
fi.Name != "StyleProperty" &&
fi.Name != "TemplateProperty")
{
try
{
clone.SetValue(dp, source.GetValue(dp));
}
catch (InvalidOperationException)
{
throw;
}
}
}
}
}
wt = wt.BaseType;
}
}
}
}
步骤2.创建一个LoadingIndicator.cs的控件。代码如下:
using System;
using System.Net;
using System.Windows;
using System.Windows.Data;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Controls;
namespace System.Windons.Control.Extend
{
/// <summary>
/// 自定义加载图标控件
/// </summary>
[TemplatePart(Name = "PART_AnimationElement", Type = typeof(FrameworkElement))]
public class LoadingIndicator : System.Windows.Controls.Control
{
/// <summary>
/// 创建实例方法
/// </summary>
public LoadingIndicator()
{
this.DefaultStyleKey = typeof(LoadingIndicator);
}
/// <summary>
/// 获取半径属性
/// </summary>
public static readonly DependencyProperty RadiusProperty =
DependencyProperty.Register("Radius", typeof(int), typeof(LoadingIndicator),
new PropertyMetadata(10, new PropertyChangedCallback(ValueChangedCallback)));
public static readonly DependencyProperty StartOpacityProperty =
DependencyProperty.Register("StartOpacity", typeof(double), typeof(LoadingIndicator),
new PropertyMetadata(1.0, new PropertyChangedCallback(StartOpacityChangedCallback)));
public static readonly DependencyProperty EndOpacityProperty =
DependencyProperty.Register("EndOpacity", typeof(double), typeof(LoadingIndicator),
new PropertyMetadata(0.1, new PropertyChangedCallback(EndOpacityChangedCallback)));
public static readonly DependencyProperty DurationProperty =
DependencyProperty.Register("Duration", typeof(TimeSpan), typeof(LoadingIndicator),
new PropertyMetadata(TimeSpan.FromSeconds(1), new PropertyChangedCallback(ValueChangedCallback)));
public static readonly DependencyProperty CountProperty =
DependencyProperty.Register("Count", typeof(int), typeof(LoadingIndicator),
new PropertyMetadata(12, new PropertyChangedCallback(CountChangedCallback)));
private static readonly DependencyProperty ControlVisibilityProperty =
DependencyProperty.Register("ControlVisibility", typeof(Visibility), typeof(LoadingIndicator),
new PropertyMetadata(Visibility.Visible, new PropertyChangedCallback(ControlVisibilityCallback)));
/// <summary>
/// 获取或设置半径
/// </summary>
public int Radius
{
get { return (int)GetValue(RadiusProperty); }
set { SetValue(RadiusProperty, value); }
}
/// <summary>
/// 获取或设置开始的透明度值
/// </summary>
public double StartOpacity
{
get { return (double)GetValue(StartOpacityProperty); }
set { SetValue(StartOpacityProperty, value); }
}
/// <summary>
/// 获取或设置结束的透明度值
/// </summary>
public double EndOpacity
{
get { return (double)GetValue(EndOpacityProperty); }
set { SetValue(EndOpacityProperty, value); }
}
/// <summary>
/// 获取或设置持续的时间
/// </summary>
public TimeSpan Duration
{
get { return (TimeSpan)GetValue(DurationProperty); }
set { SetValue(DurationProperty, value); }
}
/// <summary>
/// 元素的个数
/// </summary>
public int Count
{
get { return (int)GetValue(CountProperty); }
set { SetValue(CountProperty, value); }
}
/// <summary>
/// 获取或设置控件的可见性
/// </summary>
private bool ControlVisibility
{
get { return (bool)GetValue(ControlVisibilityProperty); }
set { SetValue(ControlVisibilityProperty, value); }
}
/// <summary>
/// 获取或设置AnimationElement
/// </summary>
private FrameworkElement AnimationElement { get; set; }
/// <summary>
/// 获取或设置画布
/// </summary>
private Canvas LayoutRoot { get; set; }
/// <summary>
/// 在设置的值改变后发生
/// </summary>
/// <param name="obj">注入对象</param>
/// <param name="args">参数</param>
private static void ValueChangedCallback(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
LoadingIndicator ctl = (LoadingIndicator)obj;
ctl.CreateAnimation();
}
/// <summary>
/// 设置开始的透明度值改变时发生
/// </summary>
/// <param name="obj">注入对象</param>
/// <param name="args">参数</param>
private static void StartOpacityChangedCallback(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
LoadingIndicator ctl = (LoadingIndicator)obj;
ctl.StartOpacity = LoadingIndicator.CorrectOpacityValue((double)args.NewValue);
ctl.CreateAnimation();
}
/// <summary>
/// 设置结束的透明度值改变时发生
/// </summary>
/// <param name="obj">注入对象</param>
/// <param name="args">参数</param>
private static void EndOpacityChangedCallback(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
LoadingIndicator ctl = (LoadingIndicator)obj;
ctl.EndOpacity = LoadingIndicator.CorrectOpacityValue((double)args.NewValue);
ctl.CreateAnimation();
}
/// <summary>
/// 判断输入的值是否有效
/// </summary>
/// <param name="opacity">输入的值</param>
/// <returns>有效则返回该值</returns>
private static double CorrectOpacityValue(double opacity)
{
if (opacity < 0) return 0;
if (opacity > 1) return 1;
return opacity;
}
/// <summary>
/// 元素个数改变时发生
/// </summary>
/// <param name="obj">注入对象</param>
/// <param name="args">参数</param>
private static void CountChangedCallback(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
LoadingIndicator ctl = (LoadingIndicator)obj;
int count = (int)args.NewValue;
//如果所设置的值小于或等于0,则设置为12.默认值
if (count <= 0)
{
ctl.Count = 12;
}
ctl.CreateAnimation();
}
/// <summary>
/// 控件的可见性方法
/// </summary>
/// <param name="obj"></param>
/// <param name="args"></param>
private static void ControlVisibilityCallback(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
LoadingIndicator ctl = (LoadingIndicator)obj;
Visibility visibility = (Visibility)args.NewValue;
if (ctl.LayoutRoot != null)
{
if (visibility == Visibility.Collapsed)
{
//如果控件不可见,则清除属于该控件的字控件
ctl.LayoutRoot.Children.Clear();
}
else
{
ctl.CreateAnimation();
}
}
}
/// <summary>
/// 重载OnApplyTemplate方法
/// </summary>
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
LayoutRoot = GetTemplateChild("LayoutRoot") as Canvas;
if (LayoutRoot == null)
{
throw new NotImplementedException("没有可用的LayoutRoot");
}
AnimationElement = GetTemplateChild("PART_AnimationElement") as FrameworkElement;
if (AnimationElement == null)
{
throw new NotImplementedException("Template Part PART_AnimationElement is required to display LoadingIndicator.");
}
CreateAnimation();
}
/// <summary>
/// 创建动画方法
/// </summary>
private void CreateAnimation()
{
if (LayoutRoot != null)
{
LayoutRoot.Children.Clear();
double angle = 360.0 / this.Count;
double width = AnimationElement.Width;
double x = (Width - width) / 2;
double y = Height / 2 + Radius;
for (int i = 0; i < this.Count; i++)
{
FrameworkElement element = DependencyPropertyCloner.Clone<FrameworkElement>(AnimationElement);
element.Opacity = 0;
TranslateTransform tt = new TranslateTransform() { X = x, Y = y };
RotateTransform rt = new RotateTransform() { Angle = i * angle + 180, CenterX = (width / 2), CenterY = -Radius };
TransformGroup tg = new TransformGroup();
tg.Children.Add(rt);
tg.Children.Add(tt);
element.RenderTransform = tg;
LayoutRoot.Children.Add(element);
DoubleAnimation animation = new DoubleAnimation();
animation.From = this.StartOpacity;
animation.From = this.StartOpacity;
animation.To = this.EndOpacity;
animation.Duration = this.Duration;
animation.RepeatBehavior = RepeatBehavior.Forever;
animation.BeginTime = TimeSpan.FromMilliseconds((this.Duration.TotalMilliseconds / this.Count) * i);
Storyboard.SetTargetProperty(animation, new PropertyPath("Opacity"));
Storyboard.SetTarget(animation, element);
Storyboard sb = new Storyboard();
sb.Children.Add(animation);
sb.Begin();
}
Binding binding = new Binding();
binding.Source = this;
binding.Path = new PropertyPath("Visibility");
this.SetBinding(LoadingIndicator.ControlVisibilityProperty, binding);
}
}
}
}
它的默认样式:generic.xaml
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:System.Windons.Control.Extend">
<Style TargetType="local:LoadingIndicator">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:LoadingIndicator">
<!-- Template's Root Visual -->
<Canvas x:Name="LayoutRoot" Background="{TemplateBinding Background}">
<Ellipse x:Name="PART_AnimationElement" Width="12" Height="12" Fill="{TemplateBinding Foreground}" />
</Canvas>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
二。演示
创建LoadingDemo.xaml文件用于演示效果
<UserControl xmlns:my="clr-namespace:System.Windons.Control.Extend;assembly=System.Windons.Control.Extend" x:Class="Silverlight.Common.Other.LoadingDemo"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400">
<Grid x:Name="LayoutRoot" HorizontalAlignment="Center" VerticalAlignment="Center">
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100" />
<ColumnDefinition Width="100" />
<ColumnDefinition Width="100" />
<ColumnDefinition Width="100" />
<ColumnDefinition Width="100" />
</Grid.ColumnDefinitions>
<!--默认图形-->
<Grid Background="WhiteSmoke" Grid.Column="0">
<my:LoadingIndicator Width="50" Height="50" Count="8" Foreground="BlueViolet" x:Name="Indicator1"/>
<TextBlock Text="加载中..." HorizontalAlignment="Center" VerticalAlignment="Center"></TextBlock>
</Grid>
<!--样式一-->
<Grid Background="#FF124431" Grid.Column="1">
<Grid.Resources>
<Style x:Name="Style1" TargetType="my:LoadingIndicator">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="my:LoadingIndicator">
<Canvas x:Name="LayoutRoot" Background="{TemplateBinding Background}">
<Rectangle x:Name="PART_AnimationElement" Width="4" Height="15"
StrokeThickness="1" Stroke="LightSteelBlue"
RadiusX="1" RadiusY="1" Fill="MidnightBlue" />
</Canvas>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Grid.Resources>
<my:LoadingIndicator Width="100" Height="100" Radius="8" Count="24" Duration="0:0:1.5"
Style="{StaticResource Style1}" />
</Grid>
<!--样式二-->
<Grid Background="#FFFFECFE" Grid.Column="2">
<Grid.Resources>
<Style x:Name="Style2" TargetType="my:LoadingIndicator">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="my:LoadingIndicator">
<Canvas x:Name="LayoutRoot" Background="{TemplateBinding Background}">
<Ellipse x:Name="PART_AnimationElement" Width="16" Height="16" Fill="#FF4C9B18" />
</Canvas>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Grid.Resources>
<my:LoadingIndicator Width="100" Height="100" Radius="8" Count="24" Duration="0:0:1.2"
Style="{StaticResource Style2}" />
</Grid>
<!--样式三-->
<Grid Background="Black" Grid.Column="3">
<Grid.Resources>
<Style x:Name="Style3" TargetType="my:LoadingIndicator">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="my:LoadingIndicator">
<Canvas x:Name="LayoutRoot" Background="{TemplateBinding Background}">
<Ellipse x:Name="PART_AnimationElement" Width="12" Height="12" Fill="White" />
</Canvas>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Grid.Resources>
<my:LoadingIndicator Width="100" Height="100" Style="{StaticResource Style3}" />
</Grid>
<!--样式四-->
<Grid Background="White" Grid.Column="4">
<Grid.Resources>
<Style x:Name="Style4" TargetType="my:LoadingIndicator">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="my:LoadingIndicator">
<Canvas x:Name="LayoutRoot" Background="{TemplateBinding Background}">
<Ellipse x:Name="PART_AnimationElement" Width="3" Height="3" Fill="#FF4C9B18" />
</Canvas>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Grid.Resources>
<my:LoadingIndicator Count="10" Radius="5" Width="40" Height="50"
Style="{StaticResource Style4}" />
</Grid>
<Button x:Name="btnToggleVisibility" Grid.Row="1" Content="Hide" Click="ToggleVisibility_Click" />
</Grid>
</UserControl>
后台代码:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
namespace Silverlight.Common.Other
{
public partial class LoadingDemo : UserControl
{
public LoadingDemo()
{
InitializeComponent();
}
private void ToggleVisibility_Click(object sender, RoutedEventArgs e)
{
if (Indicator1.Visibility == System.Windows.Visibility.Collapsed)
{
Indicator1.Visibility = System.Windows.Visibility.Visible;
btnToggleVisibility.Content = "Hide";
}
else
{
Indicator1.Visibility = System.Windows.Visibility.Collapsed;
btnToggleVisibility.Content = "Show";
}
}
}
}
源代码下载地址:https://files.cnblogs.com/salam/Silverlight.Common.rar