WPF 创建加载中的圆形进度条

WPF学习中

需求:现在需要一个转圈的进度条,不需要进度

效果如下:

image

 

一个加载中控件完整代码如下,注释都写在代码中:

public class LoadingCircle : ContentControl
    {
        protected Storyboard Storyboard;

        //圆点的数量
        public static readonly DependencyProperty DotCountProperty = DependencyProperty.Register("DotCount", typeof(int), typeof(LoadingCircle),
            new FrameworkPropertyMetadata(5, FrameworkPropertyMetadataOptions.AffectsRender));
        public int DotCount
        {
            get => (int)GetValue(DotCountProperty);
            set => SetValue(DotCountProperty, value);
        }


        protected readonly Canvas PrivateCanvas = new Canvas { ClipToBounds = true };

        public LoadingCircle()
        {
            Content = PrivateCanvas;
        }
        protected override void OnRender(DrawingContext drawingContext)
        {
            base.OnRender(drawingContext);
            UpdateDots();
        }
        //创建一个圆点,Ellipse
        protected Ellipse CreateEllipse()
        {
            var ellipse = new Ellipse()
            {
                Width = 6.0,
                Height = 6.0,
            };
            ellipse.SetValue(Shape.FillProperty, new SolidColorBrush(Colors.White));
            ellipse.SetValue(Shape.StrokeThicknessProperty, .0);
            ellipse.SetValue(Shape.StrokeProperty, new SolidColorBrush(Colors.Black));
            return ellipse;
        }
        //圆点初始位置的偏移角度
        public static readonly DependencyProperty DotOffSetProperty = DependencyProperty.Register(
           "DotOffSet", typeof(double), typeof(LoadingCircle),
           new FrameworkPropertyMetadata(60.0, FrameworkPropertyMetadataOptions.AffectsRender));

        public double DotOffSet
        {
            get => (double)GetValue(DotOffSetProperty);
            set => SetValue(DotOffSetProperty, value);
        }
        private void UpdateDots()
        {
            //下面5个变量,也可以写成 DependencyProperty ,以便在xaml文件里面动态修改
            var dotCount = DotCount;//圆点数量
            double dotInterval = 30.0;//两个圆点之间的距离
            double dotSpeed = 4.0;//完成一次动画用时,圆点转两圈的时间
            double dotDelayTime = 90.0;//两个圆点执行动画的时间间隔
            bool needHidden = true;//完成一次动画,是否要隐藏一下

            if (dotCount < 1) return;//没有圆点,不用执行了
            PrivateCanvas.Children.Clear();//先清空
            var easeOut = new PowerEase { EasingMode = EasingMode.EaseOut };
            var easeIn = new PowerEase { EasingMode = EasingMode.EaseIn };
            //创建一个一直重复的动画
            Storyboard = new Storyboard() { RepeatBehavior = RepeatBehavior.Forever };
            //逐个创建圆点
            for (int i = 0; i < dotCount; i++)
            {
                //创建一个Border
                var border = CreateBorder(i, dotInterval, needHidden);
                //关键帧动画
                var framesMove = new DoubleAnimationUsingKeyFrames()
                {
                    //每个圆点,动画开始的时间间隔不同
                    BeginTime = TimeSpan.FromMilliseconds(dotDelayTime * i)
                };
                //每个圆点,起始位置(角度)不同
                var subAngle = -dotInterval * i;
                //开始位置
                var frame0 = new LinearDoubleKeyFrame()
                {
                    Value = subAngle,
                    KeyTime = KeyTime.FromTimeSpan(TimeSpan.Zero)
                };
                //开始位置到第一次匀速开始
                var frame1 = new EasingDoubleKeyFrame()
                {
                    EasingFunction = easeOut,
                    Value = 180 + subAngle,
                    KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(dotSpeed * (0.75 / 7)))
                };
                //第一次匀速开始到第一次匀速结束
                var frame2 = new LinearDoubleKeyFrame()
                {
                    Value = 180 + DotOffSet + subAngle,
                    KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(dotSpeed * (2.75 / 7)))
                };
                //第一次匀速结束到匀加速结束
                var frame3 = new EasingDoubleKeyFrame()
                {
                    EasingFunction =easeIn,
                    Value = 360 + subAngle,
                    KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(dotSpeed * (3.5 / 7)))
                };
                //匀加速结束到匀减速结束
                var frame4 = new EasingDoubleKeyFrame()
                {
                    EasingFunction = easeOut,
                    Value = 540 + subAngle,
                    KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(dotSpeed * (4.25 / 7)))
                };
                //匀减速结束到第二次匀速结束
                var frame5 = new LinearDoubleKeyFrame()
                {
                    Value = 540 + DotOffSet + subAngle,
                    KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(dotSpeed * (6.25 / 7)))
                };
                //第二次匀速结束到匀加速结束
                var frame6 = new EasingDoubleKeyFrame()
                {
                    EasingFunction = easeIn,
                    Value = 720 + subAngle,
                    KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(dotSpeed))
                };
                //添加关键帧
                framesMove.KeyFrames.Add(frame0);
                framesMove.KeyFrames.Add(frame1);
                framesMove.KeyFrames.Add(frame2);
                framesMove.KeyFrames.Add(frame3);
                framesMove.KeyFrames.Add(frame4);
                framesMove.KeyFrames.Add(frame5);
                framesMove.KeyFrames.Add(frame6);
                //设置动画的目标控件
                Storyboard.SetTarget(framesMove, border);
                //设置动画的目标属性
                Storyboard.SetTargetProperty(framesMove, new PropertyPath("(UIElement.RenderTransform).(TransformGroup.Children)[0].(RotateTransform.Angle)"));
                Storyboard.Children.Add(framesMove);

                if (needHidden)
                {
                    //隐藏一段时间
                    var frame7 = new DiscreteObjectKeyFrame
                    {
                        Value = Visibility.Collapsed,
                        KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(dotSpeed))
                    };

                    var frame8 = new DiscreteObjectKeyFrame
                    {
                        Value = Visibility.Collapsed,
                        KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(dotSpeed + 0.4))
                    };

                    var frame9 = new DiscreteObjectKeyFrame
                    {
                        Value = Visibility.Visible,
                        KeyTime = KeyTime.FromTimeSpan(TimeSpan.Zero)
                    };
                    var framesVisibility = new ObjectAnimationUsingKeyFrames
                    {
                        BeginTime = TimeSpan.FromMilliseconds(dotDelayTime * i)
                    };
                    framesVisibility.KeyFrames.Add(frame9);
                    framesVisibility.KeyFrames.Add(frame7);
                    framesVisibility.KeyFrames.Add(frame8);
                    Storyboard.SetTarget(framesVisibility, border);
                    Storyboard.SetTargetProperty(framesVisibility, new PropertyPath("(UIElement.Visibility)"));
                    Storyboard.Children.Add(framesVisibility);
                }
                PrivateCanvas.Children.Add(border);
            }
            Storyboard.Begin();
        }
        private Border CreateBorder(int index, double dotInterval, bool needHidden)
        {
            //创建一个圆点
            var ellipse = CreateEllipse();
            ellipse.HorizontalAlignment = HorizontalAlignment.Center;
            ellipse.VerticalAlignment = VerticalAlignment.Bottom;
            //计算初始位置
            var rt = new RotateTransform() { Angle = -dotInterval * index };
            var myTransGroup = new TransformGroup();
            myTransGroup.Children.Add(rt);
            //创建一个border,并把圆点添加到border上
            var border = new Border()
            {
                RenderTransformOrigin = new Point(0.5, 0.5),
                RenderTransform = myTransGroup,
                Child = ellipse,
                Visibility = needHidden ? Visibility.Collapsed : Visibility.Visible
            };
            //绑定border的宽度高度
            border.SetBinding(WidthProperty, new Binding("Width") { Source = this });
            border.SetBinding(HeightProperty, new Binding("Height") { Source = this });

            return border;
        }
    }

创建好了类,在xaml中使用示例:

<Window x:Class="wpfDemo.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:ctl="clr-namespace:wpfDemo.Controls"
        Height="250" Width="350"
        Background="CadetBlue"
        Title="加载中..."
        >
    <StackPanel  VerticalAlignment="Center" HorizontalAlignment="Center" Orientation="Horizontal">
        <Border CornerRadius="10" Width="60" Height="60" Background="Green" Margin="5">
            <ctl:LoadingCircle Width="40" Height="40" DotCount="3"/>
        </Border>
        <Border CornerRadius="10" Width="60" Height="60" >
            <ctl:LoadingCircle Width="40" Height="40"/>
        </Border>
        <Border CornerRadius="30" Width="60" Height="60" Background="Green" Margin="5">
            <ctl:LoadingCircle Width="40" Height="40" DotCount="3"/>
        </Border>
    </StackPanel>
</Window>

效果就是,开篇的视频所示。

本文代码参考:HandyControl,Github上一个优秀的开源项目

由于是学习中,手写代码精简了很多属性,可自行优化。

© 版权声明
THE END
喜欢就支持一下吧
点赞14 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情

    暂无评论内容