Silverlight学习笔记十四自定义控件之加载时的图形

这一讲是关于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

原文地址:https://www.cnblogs.com/salam/p/1782925.html