zoukankan      html  css  js  c++  java
  • C# Winform 加载窗体/对象时的等待页面设计

    在设计应用程序过程中,有时候加载对象需时较长,我们可以显示一个Loading等待页面,对用户来说就比较友好了。

     这个还是涉及到多线程,下面是步骤。

    一、创建好Loading窗体:

    一个Panel用于显示转圈动画(仿Win10的Loading),一个Loading文本标签。动画的代码来自网络。

        public partial class Fm20Loading : Form
        {
            public Fm20Loading()
            {
                InitializeComponent();
                //LblMessage.Text = MultiLang.Surface(null, "OnLoading", "目标对象正在加载中, 请您稍等...");
    
                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.CadetBlue;
            }
    
            /// <summary>
            /// 构造器
            /// </summary>
            /// <param name="message"></param>
            public Fm20Loading(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.CadetBlue;
                Message = message;
            }
    
            private void Fm20Loading_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 Fm20Loading_Shown(object sender, EventArgs e)
            {
                if (_workAction != null)
                {
                    _workThread = new Thread(ExecWorkAction)
                    {
                        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 重写  
    
            private void LblMessage_DoubleClick(object sender, EventArgs e)
            {
                this.Close();
            }
        }
    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);
            }
        }
    绘制圆点动画的LoadingDot代码

    二、窗体和动画有了,怎么使用呢?

            private void ShowLoadingForm()
            {
                if (Debugger.IsAttached)
                {
                    return;
                }
                Fm20Loading fm20Loading = new Fm20Loading
                {
                    Name = "Fm20Loading" + DateTime.Now.Ticks
                };
                Thread.Sleep(100);
                fm20Loading.ShowDialog();
                return ;
            }
    
            private void CloseLoadingForm()
            {
                if (Debugger.IsAttached) return;
                for (int i = (Application.OpenForms.Count-1); i >=0; i--)
                {
                    Form tForm = Application.OpenForms[i];
                    string fmName = tForm.GetType().Name;
                    if (OString.Left(fmName,11) == "Fm20Loading")
                    {
                        tForm.Close();
                    }
                }
            }
    创建和关闭页面代码

    三、调用创建和关闭代码的代码(有点绕了)

                    try
                    {
                        Form child = ActiveChildForm(dllFormNameWithNameSpace);
                        if (child != null) return child;
    
                        Action handler = new Action(ShowLoadingForm);
                        handler.BeginInvoke(null, null);  //在另外一个线程打开,否则会阻塞
                        Form form = OpenPluginFormInMainDomain(dllFileSimpleName, dllFormNameWithNameSpace, initParam);
    
                        if (form != null && form is Form)
                        {
                            child = form as Form;
                            ((Fm11Base)child).RightsList = rightsList.ToLower();
                            ((Fm11Base)child).OnLoadParams = onLoadParams;
                            child.Text = tagTitle;
                            child.MdiParent = (Form)this;
                            child.FormClosed += Child_FormClosed;
                            child.Show();
                            child.WindowState = FormWindowState.Maximized;
                            this.ActivateMdiChild(child);
                            if (child.HasChildren)
                            {
                                child.Controls[0].Focus();
                            }
                            CloseLoadingForm();
                            return child;
                        }
                        else
                        {
                            CloseLoadingForm();
                            return null;
                            throw new Exception("未找到窗体文件或加载了未知的窗体类型!");
                        }
                    }
                    catch (Exception ex)
                    {
                        CloseLoadingForm();
                        MyMsg.Information("窗体实例化出错,请重试.", ex.Message);
                        return null;
                    }
    

      这部分可以改成你喜欢的使用环境。

      如此,一个友好的加载等待页面就完成了。它和耗时后台任务提示窗口两种界面结合,可以解决大部分的友好提示界面需求。

  • 相关阅读:
    使用客户端模型编程处理外部列表
    在 SharePoint2010 中使用 XML 查看器取得其他站点的内容列表
    在 SharePoint2010 中编程方式使用 BCS
    使用 zip 压缩包手动安装 Orchard
    NuGet Action Plan 更新到 1.1, 设置自动更新,获取 NuGet 包浏览器
    Percentage Closer Filtering
    Direct3D轮回:游戏特效之全屏泛光(Bloom)
    高斯模糊
    Shadow Map
    像素级动态模糊(Pixel Motion Blur)
  • 原文地址:https://www.cnblogs.com/imes/p/9779331.html
Copyright © 2011-2022 走看看