winform耗时代码处理,仿win10加载动画Loading

CheckForIllegalCrossThreadCalls = false;

1.画点
using System;
using System.ComponentModel;
using System.Drawing;

namespace Loading
{
///


/// 表示一个"点"
///

internal sealed class LoadingDot
{
#region 字段/属性

    [Description("圆心")] private readonly PointF _circleCenter;
    [Description("半径")] private readonly float _circleRadius;

    /// <summary>  
    /// 当前帧绘图坐标,在每次DoAction()时重新计算  
    /// </summary>  
    public PointF Location;

    [Description("点相对于圆心的角度,用于计算点的绘图坐标")] private int _angle;
    [Description("透明度")] private int _opacity;
    [Description("动画进度")] private int _progress;
    [Description("速度")] private int _speed;

    [Description("透明度")]
    public int Opacity => _opacity < MinOpacity ? MinOpacity : (_opacity > MaxOpacity ? MaxOpacity : _opacity);

    #endregion

    #region 常量  

    [Description("最小速度")] private const int MinSpeed = 2;
    [Description("最大速度")] private const int MaxSpeed = 11;

    [Description("出现区的相对角度")] private const int AppearAngle = 90;
    [Description("减速区的相对角度")] private const int SlowAngle = 225;
    [Description("加速区的相对角度")] private const int QuickAngle = 315;

    [Description("最小角度")] private const int MinAngle = 0;
    [Description("最大角度")] private const int MaxAngle = 360;

    [Description("淡出速度")] private const int AlphaSub = 25;

    [Description("最小透明度")] private const int MinOpacity = 0;
    [Description("最大透明度")] private const int MaxOpacity = 255;

    #endregion 常量  

    #region 构造器  

    public LoadingDot(PointF circleCenter, float circleRadius)
    {
        Reset();
        _circleCenter = circleCenter;
        _circleRadius = circleRadius;
    }

    #endregion 构造器  

    #region 方法  

    /// <summary>  
    /// 重新计算当前帧绘图坐标
    /// </summary>  
    private void ReCalcLocation()
    {
        Location = GetDotLocationByAngle(_circleCenter, _circleRadius, _angle);
    }

    /// <summary>  
    /// 点动作
    /// </summary>  
    public void LoadingDotAction()
    {
        switch (_progress)
        {
            case 0:
            {
                _opacity = MaxOpacity;
                AddSpeed();
                if (_angle + _speed >= SlowAngle && _angle + _speed < QuickAngle)
                {
                    _progress = 1;
                    _angle = SlowAngle - _speed;
                }
            }
                break;
            case 1:
            {
                SubSpeed();
                if (_angle + _speed >= QuickAngle || _angle + _speed < SlowAngle)
                {
                    _progress = 2;
                    _angle = QuickAngle - _speed;
                }
            }
                break;
            case 2:
            {
                AddSpeed();
                if (_angle + _speed >= SlowAngle && _angle + _speed < QuickAngle)
                {
                    _progress = 3;
                    _angle = SlowAngle - _speed;
                }
            }
                break;
            case 3:
            {
                SubSpeed();
                if (_angle + _speed >= QuickAngle && _angle + _speed < MaxAngle)
                {
                    _progress = 4;
                    _angle = QuickAngle - _speed;
                }
            }
                break;
            case 4:
            {
                SubSpeed();
                if (_angle + _speed >= MinAngle && _angle + _speed < AppearAngle)
                {
                    _progress = 5;
                    _angle = MinAngle;
                }
            }
                break;
            case 5:
            {
                AddSpeed();
                FadeOut();
            }
                break;
        }

        //移动  
        _angle = _angle >= (MaxAngle - _speed) ? MinAngle : _angle + _speed;
        //重新计算坐标  
        ReCalcLocation();
    }

    /// <summary>
    /// 淡出
    /// </summary>
    private void FadeOut()
    {
        if ((_opacity -= AlphaSub) <= 0)
            _angle = AppearAngle;
    }


    /// <summary>
    /// 重置状态
    /// </summary>
    public void Reset()
    {
        _angle = AppearAngle;
        _speed = MinSpeed;
        _progress = 0;
        _opacity = 1;
    }

    /// <summary>
    /// 加速
    /// </summary>
    private void AddSpeed()
    {
        if (++_speed >= MaxSpeed) _speed = MaxSpeed;
    }

    /// <summary>
    /// 减速
    /// </summary>
    private void SubSpeed()
    {
        if (--_speed <= MinSpeed) _speed = MinSpeed;
    }

    #endregion 方法  

    /// <summary>  
    /// 根据半径、角度求圆上坐标
    /// </summary>  
    /// <param name="center">圆心</param>  
    /// <param name="radius">半径</param>  
    /// <param name="angle">角度</param>  
    /// <returns>坐标</returns>  
    public static PointF GetDotLocationByAngle(PointF center, float radius, int angle)
    {
        var x = (float) (center.X + radius*Math.Cos(angle*Math.PI/180));
        var y = (float) (center.Y + radius*Math.Sin(angle*Math.PI/180));

        return new PointF(x, y);
    }
}

}
2.创建Loading窗体,包括遮罩层,加载动画,文字提示。
namespace Loading
{
partial class FrmLoading
{
///


/// Required designer variable.
///

private System.ComponentModel.IContainer components = null;

    /// <summary>
    /// Clean up any resources being used.
    /// </summary>
    /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
    protected override void Dispose(bool disposing)
    {
        if (disposing && (components != null))
        {
            components.Dispose();
        }
        base.Dispose(disposing);
    }

    #region Windows Form Designer generated code

    /// <summary>
    /// Required method for Designer support - do not modify
    /// the contents of this method with the code editor.
    /// </summary>
    private void InitializeComponent()
    {
        this.LblMessage = new System.Windows.Forms.Label();
        this.PnlImage = new System.Windows.Forms.Panel();
        this.SuspendLayout();
        // 
        // LblMessage
        // 
        this.LblMessage.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right)));
        this.LblMessage.BackColor = System.Drawing.Color.Transparent;
        this.LblMessage.ForeColor = System.Drawing.Color.White;
        this.LblMessage.Location = new System.Drawing.Point(36, 224);
        this.LblMessage.Name = "LblMessage";
        this.LblMessage.Size = new System.Drawing.Size(328, 64);
        this.LblMessage.TabIndex = 0;
        this.LblMessage.Text = "正在处理中,请稍候……";
        this.LblMessage.TextAlign = System.Drawing.ContentAlignment.TopCenter;
        // 
        // PnlImage
        // 
        this.PnlImage.Anchor = System.Windows.Forms.AnchorStyles.None;
        this.PnlImage.BackColor = System.Drawing.Color.Transparent;
        this.PnlImage.Location = new System.Drawing.Point(100, 12);
        this.PnlImage.Name = "PnlImage";
        this.PnlImage.Size = new System.Drawing.Size(200, 200);
        this.PnlImage.TabIndex = 1;
        this.PnlImage.Paint += new System.Windows.Forms.PaintEventHandler(this.PnlImage_Paint);
        this.PnlImage.Resize += new System.EventHandler(this.PnlImage_Resize);
        // 
        // FrmLoading
        // 
        this.AutoScaleDimensions = new System.Drawing.SizeF(12F, 27F);
        this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
        this.BackColor = System.Drawing.Color.Black;
        this.ClientSize = new System.Drawing.Size(400, 300);
        this.Controls.Add(this.LblMessage);
        this.Controls.Add(this.PnlImage);
        this.Font = new System.Drawing.Font("微软雅黑", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
        this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
        this.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
        this.Name = "FrmLoading";
        this.Opacity = 0.5D;
        this.ShowInTaskbar = false;
        this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
        this.Text = "FrmLoading";
        this.Load += new System.EventHandler(this.FrmLoading_Load);
        this.Shown += new System.EventHandler(this.FrmLoading_Shown);
        this.ResumeLayout(false);

    }

    #endregion

    private System.Windows.Forms.Label LblMessage;
    private System.Windows.Forms.Panel PnlImage;
}

}
using System;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using ThreadingTimer = System.Threading.Timer;
using UITimer = System.Windows.Forms.Timer;

namespace Loading
{
public partial class FrmLoading : Form
{
///


/// 构造器
///

public FrmLoading()
{
InitializeComponent();
SetStyle(
ControlStyles.AllPaintingInWmPaint |
ControlStyles.UserPaint |
ControlStyles.OptimizedDoubleBuffer,
true);
//初始化绘图timer
_tmrGraphics = new UITimer { Interval = 1 };
//Invalidate()强制重绘,绘图操作在OnPaint中实现
_tmrGraphics.Tick += (sender, e) => PnlImage.Invalidate(false);
_dotSize = PnlImage.Width / 10f;
//初始化"点"
_dots = new LoadingDot[5];
Color = Color.Orange;
}

    /// <summary>
    /// 构造器
    /// </summary>
    /// <param name="message"></param>
    public FrmLoading(string message)
    {
        InitializeComponent();
        //双缓冲,禁擦背景
        SetStyle(
            ControlStyles.AllPaintingInWmPaint |
            ControlStyles.UserPaint |
            ControlStyles.OptimizedDoubleBuffer,
            true);
        //初始化绘图timer
        _tmrGraphics = new UITimer {Interval = 1};
        //Invalidate()强制重绘,绘图操作在OnPaint中实现
        _tmrGraphics.Tick += (sender, e) => PnlImage.Invalidate(false);
        _dotSize = PnlImage.Width/10f;
        //初始化"点"
        _dots = new LoadingDot[5];
        Color = Color.Orange;
        Message = message;
    }

    private void FrmLoading_Load(object sender, EventArgs e)
    {
        LblMessage.ForeColor = Color;
        if (Owner != null)
        {
            StartPosition = FormStartPosition.Manual;
            Location = new Point(Owner.Left, Owner.Top);
            Width = Owner.Width;
            Height = Owner.Height;
        }
        else
        {
            var screenRect = Screen.PrimaryScreen.WorkingArea;
            Location = new Point((screenRect.Width - Width) / 2, (screenRect.Height - Height) / 2);
        }
        Start();
    }

    private void FrmLoading_Shown(object sender, EventArgs e)
    {
        if (_workAction != null)
        {
            _workThread = new Thread(ExecWorkAction);
            _workThread.IsBackground = true;
            _workThread.Start();
        }
    }

    #region 属性  

    [Description("消息")]
    public string Message
    {
        get { return LblMessage.Text; }
        set { LblMessage.Text = value; }
    }

    [Browsable(false), Description("圆心")]
    public PointF CircleCenter => new PointF(PnlImage.Width /2f, PnlImage.Height /2f);

    [Browsable(false), Description("半径")]
    public float CircleRadius => PnlImage.Width /2f - _dotSize;

    [Browsable(true), Category("Appearance"), Description("设置\"点\"的前景色")]
    public Color Color { get; set; }

    #endregion 属性  

    #region 字段  

    [Description("工作是否完成")]
    public bool IsWorkCompleted;

    [Description("工作动作")]
    private ParameterizedThreadStart _workAction;

    [Description("工作动作参数")]
    private object _workActionArg;

    [Description("工作线程")]
    private Thread _workThread;

    [Description("工作异常")]
    public Exception WorkException { get; private set; }

    [Description("点数组")] private readonly LoadingDot[] _dots;

    [Description("UITimer")] private readonly UITimer _tmrGraphics;

    [Description("ThreadingTimer")] private ThreadingTimer _tmrAction;

    [Description("点大小")] private float _dotSize;

    [Description("是否活动")] private bool _isActived;

    [Description("是否绘制:用于状态重置时挂起与恢复绘图")] private bool _isDrawing = true;

    [Description("Timer计数:用于延迟启动每个点 ")] private int _timerCount;

    #endregion 字段  

    #region 常量  

    [Description("动作间隔(Timer)")] private const int ActionInterval = 30;

    [Description("计数基数:用于计算每个点启动延迟:index * timerCountRadix")] private const int TimerCountRadix = 45;

    #endregion 常量  

    #region 方法  

    /// <summary>
    /// 设置工作动作
    /// </summary>
    /// <param name="workAction"></param>
    /// <param name="arg"></param>
    public void SetWorkAction(ParameterizedThreadStart workAction, object arg)
    {
        _workAction = workAction;
        _workActionArg = arg;
    }

    /// <summary>
    /// 执行工作动作
    /// </summary>
    private void ExecWorkAction()
    {
        try
        {
            var workTask = new Task(arg =>
            {
                _workAction(arg);
            }, _workActionArg);
            workTask.Start();
            Task.WaitAll(workTask);
        }
        catch (Exception exception)
        {
            WorkException = exception;
        }
        finally
        {
            IsWorkCompleted = true;
        }
    }

    /// <summary>
    /// 检查是否重置
    /// </summary>
    /// <returns></returns>
    private bool CheckToReset()
    {
        return _dots.Count(d => d.Opacity > 0) == 0;
    }

    /// <summary>
    /// 初始化点元素
    /// </summary>
    private void CreateLoadingDots()
    {
        for (var i = 0; i < _dots.Length; ++i)
            _dots[i] = new LoadingDot(CircleCenter, CircleRadius);
    }

    /// <summary>  
    /// 开始  
    /// </summary>  
    public void Start()
    {
        CreateLoadingDots();
        _timerCount = 0;
        foreach (var dot in _dots)
        {
            dot.Reset();
        }
        _tmrGraphics.Start();
        //初始化动作timer  
        _tmrAction = new ThreadingTimer(
            state =>
            {
                //动画动作  
                for (var i = 0; i < _dots.Length; i++)
                {
                    if (_timerCount++ > i*TimerCountRadix)
                    {
                        _dots[i].LoadingDotAction();
                    }
                }
                //是否重置  
                if (CheckToReset())
                {
                    //重置前暂停绘图  
                    _isDrawing = false;
                    _timerCount = 0;
                    foreach (var dot in _dots)
                    {
                        dot.Reset();
                    }
                    //恢复绘图  
                    _isDrawing = true;
                }
                _tmrAction.Change(ActionInterval, Timeout.Infinite);
            },
            null, ActionInterval, Timeout.Infinite);
        _isActived = true;
    }

    /// <summary>  
    /// 停止  
    /// </summary>  
    public void Stop()
    {
        _tmrGraphics.Stop();
        _tmrAction.Dispose();
        _isActived = false;
    }

    #endregion 方法  

    #region 重写  

    protected override void OnPaint(PaintEventArgs e)
    {
        if (IsWorkCompleted)
        {
            Stop();
            Close();
        }
    }

    private void PnlImage_Paint(object sender, PaintEventArgs e)
    {
        if (_isActived && _isDrawing)
        {
            //抗锯齿  
            e.Graphics.SmoothingMode = SmoothingMode.HighQuality;
            using (var bitmap = new Bitmap(200, 200))
            {
                //缓冲绘制  
                using (var bufferGraphics = Graphics.FromImage(bitmap))
                {
                    //抗锯齿  
                    bufferGraphics.SmoothingMode = SmoothingMode.HighQuality;
                    foreach (var dot in _dots)
                    {
                        var rectangleF = new RectangleF(
                            new PointF(dot.Location.X - _dotSize / 2, dot.Location.Y - _dotSize / 2),
                            new SizeF(_dotSize, _dotSize));
                        bufferGraphics.FillEllipse(new SolidBrush(Color.FromArgb(dot.Opacity, Color)),
                            rectangleF);
                    }
                }
                //贴图  
                e.Graphics.DrawImage(bitmap, new PointF(0, 0));
            } //bmp disposed  
        }
        base.OnPaint(e);
    }

    private void PnlImage_Resize(object sender, EventArgs e)
    {
        PnlImage.Height = PnlImage.Width;
        _dotSize = PnlImage.Width / 12f;
        OnResize(e);
    }

    #endregion 重写  
}

}
3.执行工作,展示Loading
using System.Dynamic;
using System.Threading;
using System.Windows.Forms;

namespace Loading
{
public class LoadingHelper
{
///


/// 开始加载
///

/// 消息
/// 父窗体
/// 待执行工作
/// 工作参数
public static void ShowLoading(string message, Form ownerForm, ParameterizedThreadStart work, object workArg = null)
{
var loadingForm = new FrmLoading(message);
dynamic expandoObject = new ExpandoObject();
expandoObject.Form = loadingForm;
expandoObject.WorkArg = workArg;
loadingForm.SetWorkAction(work, expandoObject);
loadingForm.ShowDialog(ownerForm);
if (loadingForm.WorkException != null)
{
throw loadingForm.WorkException;
}
}
}
}
4.调用
LoadingHelper.ShowLoading("正在加载,请稍后!!", this, (obj) =>
{
//这里写处理耗时的代码,代码处理完成则自动关闭该窗口
Action action = () =>
{
Application.OpenForms["FrmLoading"].Close();
};
Invoke(action);
// Thread.Sleep(10000);
})

posted @ 2020-02-07 16:04  zhujie-  阅读(967)  评论(0编辑  收藏  举报