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 = };
//Invalidate()强制重绘,绘图操作在OnPaint中实现
_tmrGraphics.Tick += (sender, e) => PnlImage.Invalidate(false);
_dotSize = PnlImage.Width / 10f;
//初始化"点"
_dots = new LoadingDot[];
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 = };
//Invalidate()强制重绘,绘图操作在OnPaint中实现
_tmrGraphics.Tick += (sender, e) => PnlImage.Invalidate(false);
_dotSize = PnlImage.Width / 10f;
//初始化"点"
_dots = new LoadingDot[];
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) / , (screenRect.Height - Height) / );
}
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 = ; [Description("计数基数:用于计算每个点启动延迟:index * timerCountRadix")] private const int TimerCountRadix = ; #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 > ) == ;
} /// <summary>
/// 初始化点元素
/// </summary>
private void CreateLoadingDots()
{
for (var i = ; i < _dots.Length; ++i)
_dots[i] = new LoadingDot(CircleCenter, CircleRadius);
} /// <summary>
/// 开始
/// </summary>
public void Start()
{
CreateLoadingDots();
_timerCount = ;
foreach (var dot in _dots)
{
dot.Reset();
}
_tmrGraphics.Start();
//初始化动作timer
_tmrAction = new ThreadingTimer(
state =>
{
//动画动作
for (var i = ; i < _dots.Length; i++)
{
if (_timerCount++ > i * TimerCountRadix)
{
_dots[i].LoadingDotAction();
}
}
//是否重置
if (CheckToReset())
{
//重置前暂停绘图
_isDrawing = false;
_timerCount = ;
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(, ))
{
//缓冲绘制
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 / , dot.Location.Y - _dotSize / ),
new SizeF(_dotSize, _dotSize));
bufferGraphics.FillEllipse(new SolidBrush(Color.FromArgb(dot.Opacity, Color)),
rectangleF);
}
}
//贴图
e.Graphics.DrawImage(bitmap, new PointF(, ));
} //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 = ;
[Description("最大速度")] private const int MaxSpeed = ; [Description("出现区的相对角度")] private const int AppearAngle = ;
[Description("减速区的相对角度")] private const int SlowAngle = ;
[Description("加速区的相对角度")] private const int QuickAngle = ; [Description("最小角度")] private const int MinAngle = ;
[Description("最大角度")] private const int MaxAngle = ; [Description("淡出速度")] private const int AlphaSub = ; [Description("最小透明度")] private const int MinOpacity = ;
[Description("最大透明度")] private const int MaxOpacity = ; #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 :
{
_opacity = MaxOpacity;
AddSpeed();
if (_angle + _speed >= SlowAngle && _angle + _speed < QuickAngle)
{
_progress = ;
_angle = SlowAngle - _speed;
}
}
break;
case :
{
SubSpeed();
if (_angle + _speed >= QuickAngle || _angle + _speed < SlowAngle)
{
_progress = ;
_angle = QuickAngle - _speed;
}
}
break;
case :
{
AddSpeed();
if (_angle + _speed >= SlowAngle && _angle + _speed < QuickAngle)
{
_progress = ;
_angle = SlowAngle - _speed;
}
}
break;
case :
{
SubSpeed();
if (_angle + _speed >= QuickAngle && _angle + _speed < MaxAngle)
{
_progress = ;
_angle = QuickAngle - _speed;
}
}
break;
case :
{
SubSpeed();
if (_angle + _speed >= MinAngle && _angle + _speed < AppearAngle)
{
_progress = ;
_angle = MinAngle;
}
}
break;
case :
{
AddSpeed();
FadeOut();
}
break;
} //移动
_angle = _angle >= (MaxAngle - _speed) ? MinAngle : _angle + _speed;
//重新计算坐标
ReCalcLocation();
} /// <summary>
/// 淡出
/// </summary>
private void FadeOut()
{
if ((_opacity -= AlphaSub) <= )
_angle = AppearAngle;
} /// <summary>
/// 重置状态
/// </summary>
public void Reset()
{
_angle = AppearAngle;
_speed = MinSpeed;
_progress = ;
_opacity = ;
} /// <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 / ));
var y = (float)(center.Y + radius * Math.Sin(angle * Math.PI / )); return new PointF(x, y);
}
}
绘制圆点动画的LoadingDot代码
二、窗体和动画有了,怎么使用呢?
private void ShowLoadingForm()
{
if (Debugger.IsAttached)
{
return;
}
Fm20Loading fm20Loading = new Fm20Loading
{
Name = "Fm20Loading" + DateTime.Now.Ticks
};
Thread.Sleep();
fm20Loading.ShowDialog();
return ;
} private void CloseLoadingForm()
{
if (Debugger.IsAttached) return;
for (int i = (Application.OpenForms.Count-); i >=; i--)
{
Form tForm = Application.OpenForms[i];
string fmName = tForm.GetType().Name;
if (OString.Left(fmName,) == "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;
}
这部分可以改成你喜欢的使用环境。
如此,一个友好的加载等待页面就完成了。它和耗时后台任务提示窗口两种界面结合,可以解决大部分的友好提示界面需求。
C# Winform 加载窗体/对象时的等待页面设计的更多相关文章
- c# Winform 加载窗体
先来一个加载窗体代码 public partial class FrmLoading : Form { public BackgroundWorker updateDBWorker=new Backg ...
- WinForm 加载大数据时不闪烁的ListView
自己之前做小工具的时候,遇到这个问题,记录一下... namespace 相册处理 { //将ListView重载为此新类,解决加载过程中闪烁的问题 //在designer.cs中改写: //priv ...
- WinForm 加载自定义控件闪烁问题
WinForm加载多个自定义控件时,会出现很严重的闪烁问题,很卡,一块一块的加载(像打开网页时,网络很卡的那种感觉)简直没法忍受. 在网上搜索了好久,网上大部分的方法是一下4种,但是都不能有效的解决问 ...
- 图片_ _Android有效解决加载大图片时内存溢出的问题 2
Android有效解决加载大图片时内存溢出的问题 博客分类: Android Android游戏虚拟机算法JNI 尽量不要使用setImageBitmap或 setImageResource或 Bit ...
- [转] 从 dll 程序集中动态加载窗体
无涯 原文 从 dll 程序集中动态加载窗体 [原创] 昨天晚上花了一晚上时间写了一个从程序集中动态加载窗体的程序.将任何包含窗体的代码编译成 dll 文件,再把 dll 文件拷贝到本程序的目录下,本 ...
- 【Java基础】Java类的加载和对象创建流程的详细分析
相信我们在面试Java的时候总会有一些公司要做笔试题目的,而Java类的加载和对象创建流程的知识点也是常见的题目之一.接下来通过实例详细的分析一下. 实例问题 实例代码 Parent类 package ...
- 【转】C# winform 加载网页 模拟键盘输入自动接入访问网络
[转]C# winform 加载网页 模拟键盘输入自动接入访问网络 声明: 本文原创,首发于博客园 http://www.cnblogs.com/EasyInvoice/p/6070563.html ...
- WPF 异步加载窗体
加载某个界面时,需要获取数据,而数据返回的时间比较长,这个时候可以异步加载界面. 1.在该窗体的加载事件(Load)中编写以下代码: new Thread(p=>{DataBinding();} ...
- vs 2015 "加载该页时出错。" 解决方案
错误信息: 加载该页时出错. 未找到与约束 ContractName Microsoft.CodeAnalysis.Editor.TypeScript.ScriptContexts.ITypeSc ...
随机推荐
- python与桶排序
问题提出: 将以下数据: 6, 8, 2, 3, 4, 0, 9, 1, 5,1 按从小到达排列. 桶排序原理: 桶排序也叫计数排序,简单来说,就是将数据集里面所有元素按顺序列举出来,然后统计元素出现 ...
- SonarQube在CentOS上的安装
1 简介 SonarQube 是一个用于代码质量管理的开放平台.通过插件机制,Sonar 可以集成不同的测试工具,代码分析工具,以及持续集成工具.与持续集成工具(例如 Hudson/Jenkins 等 ...
- RequestParam注解的Url参数被省略时该如何处理
RequestParam注解的Url参数被省略时该如何处理 1.RequestParam的用法 ==================== RequestParam注解可以把包含在Url中的参数映射到U ...
- js监听文本框内容变化
js监听文本框内容变化 原理很简单,就是在外部先声明一个用来记录input值的变量,然后每0.1秒比较这个值与input的值,如果发生改变,则运行自己的代码,同时改变变量.从而实现对input值改变的 ...
- Python名称空间和闭包
一.名称空间 1.定义:又名 name space,顾名思义,就是存放名字的地方.比如:若变量x = 1,1存放在内存中, 而名称空间正是存放名字x与1绑定关系的地方. 2.分类: locals : ...
- Linux的基本指令--其他命令
一 . 终端翻页: shift-pageup shift-pagedown 二 . 看手册:man man man 2 read 查看read系统函数的man page(在第二个section中,表示 ...
- Linux的基本指令--服务器
ftp: 1.安装vsftpd服务器 sudo apt-get install vsftpd 2.创建一个空目录,供用户上传:创建服务器文件夹,ftp服务器,服务器端和客户端,我建立的是/home/c ...
- Screen - BOM对象
Screen 对象 Screen 对象包含有关客户端显示屏幕的信息. 注释:没有应用于 screen 对象的公开标准,不过所有浏览器都支持该对象. Screen 对象属性 属性 描述 availHei ...
- 使用laravel实现用户的登陆
首先在 php artisan 里面 make:auth 生产一个门脸类 修改配置文件里面要哪个模型登陆 模型得继承一下才能 先写一下注册 密码必须要使用laravel的加密方法,使用MD5都没用 l ...
- php 数据导出csv 注意问题。
总共10W数据每次下载到9.5W就停了. 加上这个就好了 ini_set('memory_limit','512M'): //脚本运行无时间限制. set_time_limit(0); 要设置一个se ...