自定义加载动画控件


LoadingSquareUserControl

加载动画控件的最基本组成元素

<UserControl x:Class="FileWall.Controls.LoadingSquareUserControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             mc:Ignorable="d" 
             Height="12" Width="25" Background="#FFC4C4C4">
    <Grid>
        <Canvas x:Name="canvas_Value" HorizontalAlignment="Left" Margin="0" VerticalAlignment="Stretch" Background="#FF0FB6E2"></Canvas>
    </Grid>
</UserControl>

默认情况下,Canvas 的宽度为 0,即不显示,当开始加载时,通过动画改变 Canvas 的宽度,得出类似动画的效果。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
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 FileWall.Controls
{
    /// <summary>
    /// LoadingSquareUserControl.xaml 的交互逻辑
    /// </summary>
    public partial class LoadingSquareUserControl : UserControl
    {

        public double MaxValue { get; private set; }

        public double Value
        {
            get { return (double)GetValue(ValueProperty); }
            set { SetValue(ValueProperty, value); }
        }

        // Using a DependencyProperty as the backing store for ValueProperty.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty ValueProperty =
            DependencyProperty.Register("Value", typeof(double), typeof(LoadingSquareUserControl), new UIPropertyMetadata(.0, (d, e) =>
            {
                LoadingSquareUserControl userControl = d as LoadingSquareUserControl;
                userControl.canvas_Value.Width = 25 * (double)e.NewValue / userControl.MaxValue;
            }));

        public LoadingSquareUserControl()
        {
            InitializeComponent();
            MaxValue = 10;
        }
    }
}

MaxValue 表示 UserControl 代表的最大值。 依赖属性 ValueProperty 通过传入外部变量,决定改变 Canvas 的当前宽度。

LoadingProgressUserControl

LoadSquareUserControl 的集合,组合成类似进度条的效果。

<UserControl x:Class="FileWall.Controls.LoadingProgressUserControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:fc="clr-namespace:FileWall.Controls"
             mc:Ignorable="d">
    <UniformGrid Grid.Column="1" Height="40" Margin="20,0,0,0" Columns="10" x:Name="uniformGrid_UserControlCollection">
        <fc:LoadingSquareUserControl />
        <fc:LoadingSquareUserControl Margin="0,12.5" />
        <fc:LoadingSquareUserControl Margin="0,12.5" />
        <fc:LoadingSquareUserControl Margin="0,12.5" />
        <fc:LoadingSquareUserControl Margin="0,12.5" />
        <fc:LoadingSquareUserControl Margin="0,12.5" />
        <fc:LoadingSquareUserControl Margin="0,12.5" />
        <fc:LoadingSquareUserControl Margin="0,12.5" />
        <fc:LoadingSquareUserControl Margin="0,12.5" />
        <fc:LoadingSquareUserControl Margin="0,12.5" />
    </UniformGrid>
</UserControl>

UniformGrid 面板布局,通过定义列数,形成水平排列。
定义 LoadingSquareUserControl 的 Margin 属性,使得每个 LoadingSquareUserControl 间隔开,得到更好的用户体验。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
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;
using System.Windows.Media.Animation;

namespace FileWall.Controls
{
    /// <summary>
    /// LoadingProgressUserControl.xaml 的交互逻辑
    /// </summary>
    public partial class LoadingProgressUserControl : UserControl
    {
        private double m_Value = 0;
        private LoadingSquareUserControl[] m_LoadingSquareCollection = null;
        private Storyboard m_ProgressAnimation = null;

        public double MaxValue { get; private set; }

        public LoadingSquareUserControl[] LoadingSquareCollection
        {
            get
            {
                if (m_LoadingSquareCollection == null)
                {
                    m_LoadingSquareCollection = new LoadingSquareUserControl[10];
                    for (int i = 0; i < 10; i++)
                    {
                        m_LoadingSquareCollection[i] = uniformGrid_UserControlCollection.Children[i] as LoadingSquareUserControl;
                    }
                }
                return m_LoadingSquareCollection;
            }
        }

        public double Value
        {
            get
            {
                return m_Value;
            }
            set
            {
                int leftSquare = (int)value / 10;
                double currentValue = value % 10;
                for (int i = 0; i < leftSquare; i++)
                {
                    LoadingSquareCollection[i].ApplyAnimationClock(LoadingSquareUserControl.ValueProperty, null);
                    LoadingSquareCollection[i].Value = 10;
                }
                if (leftSquare < 10)
                {
                    if (m_ProgressAnimation != null)
                        m_ProgressAnimation.Stop();

                    DoubleAnimation animation = new DoubleAnimation
                    {
                        From = LoadingSquareCollection[leftSquare].Value,
                        To = currentValue,
                        Duration = TimeSpan.FromMilliseconds(100)
                    };

                    Storyboard.SetTarget(animation, LoadingSquareCollection[leftSquare]);
                    Storyboard.SetTargetProperty(animation, new PropertyPath(LoadingSquareUserControl.ValueProperty));
                    m_ProgressAnimation = new Storyboard();
                    m_ProgressAnimation.Children.Add(animation);
                    m_ProgressAnimation.Completed += delegate
                    {
                        if (LoadingSquareCollection[leftSquare].HasAnimatedProperties)
                        {
                            LoadingSquareCollection[leftSquare].ApplyAnimationClock(LoadingSquareUserControl.ValueProperty, null);
                            LoadingSquareCollection[leftSquare].Value = animation.To.Value;
                        }
                        m_ProgressAnimation = null;
                    };
                    m_ProgressAnimation.Begin();

                }
            }
        }

        public LoadingProgressUserControl()
        {
            InitializeComponent();
            MaxValue = 10;
        }
    }
}

定义 LoadingSquareUserControl 的数组集合用了存放 LoadingSquareUserControl。
leftSquare 表示剩余的 LoadingSquareUserControl 的个数。
UIElement.ApplyAnimationClock : 若要从属性中移除动画,为该属性指定该标识符作为 dp 并指定 clock 作为 null。 这将移除动画,事件的属性设置为其基值。 但是,最初关联的动画时钟不会停止。 其他动画分配给该时钟将继续运行。

LoadingUserControl

<UserControl x:Class="FileWall.Controls.LoadingUserControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:fc="clr-namespace:FileWall.Controls"
             mc:Ignorable="d" 
             d:DesignHeight="768" d:DesignWidth="1280" Background="#FF0E0F14">
    <Grid>
        <Grid HorizontalAlignment="Center" Height="100" VerticalAlignment="Center" Width="500" Margin="0,200,0,0">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="0.15*"/>
                <ColumnDefinition Width="0.85*"/>
            </Grid.ColumnDefinitions>
            <TextBlock HorizontalAlignment="Center" TextWrapping="Wrap" Text="0/0" VerticalAlignment="Center" Foreground="#FF0FB6E2" FontSize="18.667" x:Name="textBlock_LoadingTips"/>
            <fc:LoadingProgressUserControl Grid.Column="1" x:Name="loadingProgress"/>
        </Grid>    
    </Grid>
</UserControl>

Grid 布局,显示文件加载百分比和进度条。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
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 FileWall.Controls
{
    /// <summary>
    /// LoadingUserControl.xaml 的交互逻辑
    /// </summary>
    public partial class LoadingUserControl : UserControl
    {
        public event EventHandler LoadingCompleted;

        public LoadingUserControl()
        {
            InitializeComponent();
            Loaded += new RoutedEventHandler(LoadingUserControl_Loaded);
        }

        void LoadingUserControl_Loaded(object sender, RoutedEventArgs e)
        {
            new System.Threading.Thread(new System.Threading.ParameterizedThreadStart(delegate
            {
                int index = 1;
                if (MainWindow.PhotoInfos == null) 
                    return;
                foreach (var photo in MainWindow.PhotoInfos)
                {
                    this.Dispatcher.Invoke((Action)delegate
                    {
                        textBlock_LoadingTips.Text = index.ToString() + @"/" + MainWindow.PhotoInfos.Length.ToString();
                        loadingProgress.Value = (double)index / MainWindow.PhotoInfos.Length * 100;
                    }, null);

                    System.Threading.Thread.Sleep(1000);

                    index++;
                }
                if (LoadingCompleted != null)
                {
                    this.Dispatcher.BeginInvoke((Action)delegate
                    {
                        LoadingCompleted(this, null);
                    }, null);
                }
            })).Start();
        }
    }
}

自定义加载完成处理事件 LoadingCompleted:EventHandler。
创建新的线程去完成加载操作,加载完成之后,手动调用触发LoadingCompleted(this, null); 事件。

使用 LoadingUserControl

<Window xmlns:shell="clr-namespace:Microsoft.Windows.Shell;assembly=Microsoft.Windows.Shell"  x:Class="FileWall.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:FileWall"
        xmlns:fc="clr-namespace:FileWall.Controls"
        Title="MainWindow" Height="350" Width="525" xmlns:cc="clr-namespace:CustomerControls;assembly=CustomerControls">
        <Grid x:Name="gridRoot">
            <fc:LoadingUserControl x:Name="loadingUserControl" />
        </Grid>
</Window>

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
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;
using FileWall.Controls;

namespace FileWall
{
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : Window
    {
        public static System.IO.FileInfo[] PhotoInfos { get; private set; }

        public MainWindow()
        {
            InitializeComponent();

            this.DataContext = this;

            PhotoInfos = (from f in System.IO.Directory.GetFiles(Environment.GetFolderPath(Environment.SpecialFolder.MyPictures), "*.jpg")
                          select new System.IO.FileInfo(f)).ToArray();

            loadingUserControl.LoadingCompleted += delegate
            {
                gridRoot.Children.RemoveAt(gridRoot.Children.Count - 1);
                LibraryBarViewUserControl view = new LibraryBarViewUserControl();
                view.surfaceScatterViewer.ItemsSource = PhotoInfos.ToList();
                gridRoot.Children.Add(view);

                this.Topmost = true;
                this.Topmost = false;
            };

        }

    }
}

知识共享许可协议
《自定义加载动画控件》 常伟华 创作。
本作品采用知识共享署名-相同方式共享 4.0 国际许可协议 | 3.0 中国大陆许可协议进行许可。

站内公告

A PHP Error was encountered

Severity: Core Warning

Message: PHP Startup: zip: Unable to initialize module Module compiled with module API=20060613 PHP compiled with module API=20090626 These options need to match

Filename: Unknown

Line Number: 0

Backtrace: