博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Winform应用程序实现通用遮罩层
阅读量:7105 次
发布时间:2019-06-28

本文共 6447 字,大约阅读时间需要 21 分钟。

在WEB上,我们在需要进行大数据或复杂逻辑处理时,由于耗时较长,一般我们会在处理过程中的页面上显示一个半透明的遮罩层,上面放个图标或提示:正在处理中...等字样,这样用户体验就比较好了,然而如果在Winform客户端程序,通常遮罩层的处理就显得不那么简单或不那么好看,而我今天要说明的是,我实现的这个Winform通用遮罩层,却可以实现类似WEB上的遮罩层,既可以透明,而且还可以显示动态图片以及文字,那如何实现的呢,我现在一一讲解。

首先要明确我们要实现的效果:透明+动态图标+文字

透明:这个简单,只需要将窗体的Opacity设为100%以下的值就可以了,这里我采用85%;

动态图标:这个相对复杂一些,因为Winform目前没有现成的支持直接显示动图的控件,但幸好有一个组件ImageAnimator支持逐帧动画,我们只需要将图片绑定到ImageAnimator的Animate方法上(即:ImageAnimator.Animate(m_Image,EventHandler委托);),然后重写窗体的OnPaint即可,具体的代码实现见下面公布的源码。

文字:这个简单,放在一个Label控件即可

还有为了能够让图标与文字在相对的位置(即不论大小)保持居中,我这里采用了一个TableLayoutPanel,分成两行,上行放置Label,并设为居中,下行放置Panel,提供绘制动图的区域。

完整代码实现如下(部份代码参考网络上它人的文章):

using System;using System.Collections.Generic;using System.ComponentModel;using System.Data;using System.Drawing;using System.Linq;using System.Text;using System.Windows.Forms;using System.Reflection;using System.Threading;using System.Threading.Tasks;namespace TEMS{    public partial class FrmProcessing : Form    {        private static Image m_Image = null;        private EventHandler evtHandler = null;        private ParameterizedThreadStart workAction = null;        private object workActionArg = null;        private Thread workThread = null;        public string Message        {            get            {                return lbMessage.Text;            }            set            {                lbMessage.Text = value;            }        }        public bool WorkCompleted = false;        public Exception WorkException        { get; private set; }        public void SetWorkAction(ParameterizedThreadStart workAction, object arg)        {            this.workAction = workAction;            this.workActionArg = arg;        }        public FrmProcessing(string msg)        {            InitializeComponent();            this.Message = msg;        }        protected override  void OnPaint(PaintEventArgs e)        {            base.OnPaint(e);            if (m_Image != null)            {                //获得当前gif动画下一步要渲染的帧。                UpdateImage();                //将获得的当前gif动画需要渲染的帧显示在界面上的某个位置。                int x = (int)(panImage.ClientRectangle.Width - m_Image.Width) / 2;                int y = 0;                //e.Graphics.DrawImage(m_Image, new Rectangle(x, y, m_Image.Width, m_Image.Height));                panImage.CreateGraphics().DrawImage(m_Image, new Rectangle(x, y, m_Image.Width, m_Image.Height));            }            if (this.WorkCompleted)            {                this.Close();            }        }        private void FrmProcessing_Load(object sender, EventArgs e)        {            if (this.Owner != null)            {                this.StartPosition = FormStartPosition.Manual;                this.Location = new Point(this.Owner.Left, this.Owner.Top);                //MessageBox.Show(string.Format("X={0},Y={1}", this.Owner.Left, this.Owner.Top));                this.Width = this.Owner.Width;                this.Height = this.Owner.Height;            }            else            {                Rectangle screenRect = Screen.PrimaryScreen.WorkingArea;                this.Location = new Point((screenRect.Width - this.Width) / 2, (screenRect.Height - this.Height) / 2);            }            //为委托关联一个处理方法            evtHandler = new EventHandler(OnImageAnimate);            if (m_Image == null)            {                Assembly assy = Assembly.GetExecutingAssembly();                //获取要加载的gif动画文件                m_Image = Image.FromStream(assy.GetManifestResourceStream(assy.GetName().Name + ".Resources.loading2.gif"));            }            //调用开始动画方法            BeginAnimate();        }        //开始动画方法        private void BeginAnimate()        {            if (m_Image != null)            {                //当gif动画每隔一定时间后,都会变换一帧,那么就会触发一事件,该方法就是将当前image每变换一帧时,都会调用当前这个委托所关联的方法。                ImageAnimator.Animate(m_Image, evtHandler);            }        }        //委托所关联的方法        private void OnImageAnimate(Object sender, EventArgs e)        {            //该方法中,只是使得当前这个winform重绘,然后去调用该winform的OnPaint()方法进行重绘)            this.Invalidate();        }        //获得当前gif动画的下一步需要渲染的帧,当下一步任何对当前gif动画的操作都是对该帧进行操作)        private void UpdateImage()        {            ImageAnimator.UpdateFrames(m_Image);        }        //关闭显示动画,该方法可以在winform关闭时,或者某个按钮的触发事件中进行调用,以停止渲染当前gif动画。        private void StopAnimate()        {            m_Image = null;            ImageAnimator.StopAnimate(m_Image, evtHandler);        }        private void FrmProcessing_Shown(object sender, EventArgs e)        {            if (this.workAction != null)            {                workThread = new Thread(ExecWorkAction);                workThread.IsBackground = true;                workThread.Start();            }        }        private void ExecWorkAction()        {            try            {                var workTask = new Task((arg) =>                                {                                    this.workAction(arg);                                },                            this.workActionArg);                workTask.Start();                Task.WaitAll(workTask);            }            catch (Exception ex)            {                this.WorkException = ex;            }            finally            {                this.WorkCompleted = true;            }        }    }}

以下是自动生成的代码:

 
View Code

代码中SetWorkAction方法是用来设置异步需要处理的委托方法,在窗体显示出来后(FrmProcessing_Shown),创建新线程,用以处理耗时的逻辑代码段,其中有一个WorkCompleted属性,这个主要是表明处理耗时的逻辑代码已完成(不论是否报错),在窗体重绘时(OnPaint),会持续判断该值是否为true,若为true则关闭当前窗口。

另之所以没重写Panel的OnPaint方法,原因是虽然可以显示动图,但由于局部重绘,造成动图出现闪屏,所以仍需要采用窗体重绘

为了便于通用,我还定义了一个通用方法,专门用来显示遮罩层窗体,方法定义如下:

public static class Common    {        public static void ShowProcessing(string msg, Form owner, ParameterizedThreadStart work, object workArg = null)        {            FrmProcessing processingForm = new FrmProcessing(msg);            dynamic expObj = new ExpandoObject();            expObj.Form = processingForm;            expObj.WorkArg = workArg;            processingForm.SetWorkAction(work, expObj);            processingForm.ShowDialog(owner);            if (processingForm.WorkException != null)            {                throw processingForm.WorkException;            }        }            }

现在使用就很简单了,如下:

Common.ShowProcessing("正在处理中,请稍候...", this, (obj) =>{     //这里写处理耗时的代码,代码处理完成则自动关闭该窗口},null);

 使用效果如下:

大家可以将上述代码直接复制到新建的窗体中,即可立即使用,上述代码若有不足之处,还请大家评论并指出,谢谢!

本文转自 梦在旅途 博客园博客,原文链接:http://www.cnblogs.com/zuowj/p/4431856.html  ,如需转载请自行联系原作者

你可能感兴趣的文章
RIP理论知识
查看>>
清空memcached中缓存的数据的方法
查看>>
jsp通过include指令引入html乱码的解决方法
查看>>
解决mysql:The server quit without updating PID file
查看>>
网络实验要求
查看>>
linux7 ntp 开机不自动启动
查看>>
做一次面向对象的体操:将 JSON 字符串转换为嵌套对象的一种方法
查看>>
高可用Redis服务架构分析与搭建
查看>>
论JVM爆炸的几种姿势及自救方法
查看>>
Tomcat远程debug
查看>>
Java的BIO,NIO和AIO的区别于演进
查看>>
核心标签库-------------------二
查看>>
人工智能的就业替代效应
查看>>
生成树理论内容
查看>>
AJPFX总结正则表达式的概述和简单使用
查看>>
git命令
查看>>
httpclient post 发送Json数据
查看>>
git 将branch转为master
查看>>
在 CentOS 上安装和配置 OpenStack Nova
查看>>
mysql的innodb中事务日志ib_logfile
查看>>