Winform应用程序实现通用遮罩层

Winform应用程序实现通用遮罩层

在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;
            }

        }
    }
}

以下是自动生成的代码:

namespace TEMS
{
    partial class FrmProcessing
    {
        /// <summary>
        /// Required designer variable.
        /// </summary>
        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.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel();
            this.lbMessage = new System.Windows.Forms.Label();
            this.panImage = new System.Windows.Forms.Panel();
            this.tableLayoutPanel1.SuspendLayout();
            this.SuspendLayout();
            //
            // tableLayoutPanel1
            //
            this.tableLayoutPanel1.ColumnCount = 1;
            this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle());
            this.tableLayoutPanel1.Controls.Add(this.lbMessage, 0, 0);
            this.tableLayoutPanel1.Controls.Add(this.panImage, 0, 1);
            this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill;
            this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0);
            this.tableLayoutPanel1.Name = "tableLayoutPanel1";
            this.tableLayoutPanel1.RowCount = 2;
            this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50F));
            this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50F));
            this.tableLayoutPanel1.Size = new System.Drawing.Size(582, 318);
            this.tableLayoutPanel1.TabIndex = 1;
            //
            // lbMessage
            //
            this.lbMessage.BackColor = System.Drawing.Color.Transparent;
            this.lbMessage.Dock = System.Windows.Forms.DockStyle.Fill;
            this.lbMessage.Font = new System.Drawing.Font("微软雅黑", 14.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
            this.lbMessage.Location = new System.Drawing.Point(3, 0);
            this.lbMessage.Name = "lbMessage";
            this.lbMessage.Padding = new System.Windows.Forms.Padding(0, 0, 0, 30);
            this.lbMessage.Size = new System.Drawing.Size(576, 159);
            this.lbMessage.TabIndex = 1;
            this.lbMessage.Text = "lbMessage\r\nadsfadsf";
            this.lbMessage.TextAlign = System.Drawing.ContentAlignment.BottomCenter;
            //
            // panImage
            //
            this.panImage.Dock = System.Windows.Forms.DockStyle.Fill;
            this.panImage.Location = new System.Drawing.Point(3, 162);
            this.panImage.Name = "panImage";
            this.panImage.Size = new System.Drawing.Size(576, 153);
            this.panImage.TabIndex = 2;
            //
            // FrmProcessing
            //
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.BackColor = System.Drawing.SystemColors.Control;
            this.ClientSize = new System.Drawing.Size(582, 318);
            this.Controls.Add(this.tableLayoutPanel1);
            this.Cursor = System.Windows.Forms.Cursors.WaitCursor;
            this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
            this.Name = "FrmProcessing";
            this.Opacity = 0.85D;
            this.StartPosition = System.Windows.Forms.FormStartPosition.Manual;
            this.Text = "FrmProcessing";
            this.Load += new System.EventHandler(this.FrmProcessing_Load);
            this.Shown += new System.EventHandler(this.FrmProcessing_Shown);
            this.tableLayoutPanel1.ResumeLayout(false);
            this.ResumeLayout(false);

        }

        #endregion

        private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1;
        private System.Windows.Forms.Label lbMessage;
        private System.Windows.Forms.Panel panImage;

    }
}

代码中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);

使用效果如下:

时间: 2024-10-03 19:05:17

Winform应用程序实现通用遮罩层的相关文章

Winform应用程序实现通用遮罩层二

之前先后发表过:<Winform应用程序实现通用遮罩层>.<Winform应用程序实现通用消息窗口>,这两款遮罩层其实都是基于弹出窗口的,今天为大家分享一个比较简单但界面相对友好的另一种实现方案,废话不多说,直接进入主题. 一.实现思路(解决问题顺序): 透明遮罩: 1.实现可设置透明的Panel控件(MaskPanel): 2.Panel控件(MaskPanel)能够覆盖父容器(一般是当前窗体form对象)客户区区域(即:与父容器客户区区域大小相同),并处于最上层,保证父容器上的

Winform应用程序实现通用消息窗口

记得我之前发表过一篇文章<Winform应用程序实现通用遮罩层>,是实现了透明遮罩的消息窗口,功能侧重点在动图显示+消息提醒,效果看上去比较的炫,而本篇我又来重新设计通用消息窗口,功能重点在于消息提醒.进度报告,当然如果大家时间,可以将两种相结合,那样就会更完美了,我这里仍是以实现功能为主,由于代码相对简单,我就直接贴上所有代码,大家可以直接复制到本地测试,若发现问题可自行改正或反馈给我,我来完善,谢谢! using System; using System.Collections.Gener

WinForm特效:桌面上的遮罩层

一个窗体特效,帮你了解几个windows api函数.效果:windows桌面上增加一个简单的遮罩层,其中WS_EX_TRANSPARENT 比较重要,它实现了鼠标穿透的功能. [csharp] view plaincopy using System; using System.Drawing; using System.Windows.Forms; using System.Runtime.InteropServices; namespace WindowsApplication40 { pu

阻止小程序遮罩层下方图层滚动

原因是因为: touchmove事件冒泡: 解决方案: 阻止遮罩层冒泡,在小程序里面 利用catch+事件名就可以阻止冒泡, 所以 在遮罩层填上事件 catchtouchmove="preventdefault" 小程序1.5.0后可以写上 capture-catch:touchmove="preventdefault" 完美 哈哈: 原文地址:https://www.cnblogs.com/wang-xx/p/11405537.html

C# Winform 实现自定义半透明遮罩层介绍

在网页中通过div+css实现半透明效果不难,今天我们看看一种在winfrom中实现的方法: 效果图如下,正常时: 显示遮罩层时: 自定义遮罩层控件的源码如下: View Row Code 1 using System; 2 using System.Drawing; 3 using System.Windows.Forms; 4 using System.ComponentModel; 5   6 namespace MyOpaqueLayer 7 { 8     /// <summary>

C# Winform 实现自定义半透明loading加载遮罩层

在网页中通过div+css实现半透明效果不难,今天我们看看一种在winfrom中实现的方法: 效果图如下,正常时: 显示遮罩层时: 自定义遮罩层控件的源码如下: View Row Code 1 using System; 2 using System.Drawing; 3 using System.Windows.Forms; 4 using System.ComponentModel; 5   6 namespace MyOpaqueLayer 7 { 8 /// <summary> 9 /

微信小程序弹出和隐藏遮罩层动画以及五星评分

参考源码: http://www.see-source.com/weixinwidget/detail.html?wid=82 https://blog.csdn.net/pcaxb/article/details/56276180 https://developers.weixin.qq.com/miniprogram/dev/api/api-animation.html train.wxml <view class='train_con'> <view class='head'>

[微信小程序]实现一个自定义遮罩层

正文: 先上效果图: 点击按钮Show显示遮罩层,再次点击屏幕任何地方隐藏遮罩层; <button bindtap="showview">Show</button> <view class="bg" bindtap='hideview' style='display:{{display}}'></view> <view class="show" bindtap='hideview' style

小程序实现弹出遮罩层模态框

<view bindtap='showRule'>点击弹出模态框</view> <!--遮罩层--> <view class="ruleZhezhao {{isRuleTrue?'isRuleShow':'isRuleHide'}}"> <view class='ruleZhezhaoContent'> <image src='/images/zijia.png' class="select">