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

155 阅读4分钟

前言

大家好,今天我们将探讨如何在 WinForm 客户端程序中实现一个类似 Web 端的"通用遮罩层"。在 Web 开发中,当进行大数据处理或复杂逻辑运算时,我们通常会使用一个半透明的遮罩层,配合动态图标和提示文字(如"正在处理中..."),以提升用户体验。然而,在传统的 WinForm 应用中,这种效果往往难以实现,或实现后不够美观。

本文将详细介绍一种在 WinForm 中实现透明遮罩层、动态图标显示与文字提示的完整方案,帮助大家打造更专业、更友好的桌面应用界面。

遮罩层效果预览

遮罩层覆盖主界面,显示动态加载图标和提示文字,任务完成后自动关闭,用户体验显著提升。

遮罩层的核心功能

我们希望实现的遮罩层具备以下三个核心特性:

1、透明效果:遮罩层应为半透明,既能覆盖主界面防止用户误操作,又能让用户隐约看到后台内容。通过设置窗体的 Opacity 属性(本文采用 85%)即可轻松实现。

2、动态图标:WinForm 原生控件不支持直接播放 GIF 动画。为此,我们利用 .NET 提供的 ImageAnimator 类,通过逐帧渲染的方式实现动图播放。

3、文字提示:使用 Label 控件显示提示信息,并确保文字与动图在任何窗体大小下都能居中显示。

为了实现布局的灵活性与居中对齐,我们采用 TableLayoutPanel 控件,将其分为两行:

  • 上行放置 Label,用于显示提示文字。

  • 下行放置 Panel,作为动图的绘制区域。

代码实现与使用方法

核心遮罩窗体 FrmProcessing

以下是实现遮罩层功能的完整代码:

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();

                // 将动图绘制在Panel上
                int x = (int)(panImage.ClientRectangle.Width - m_Image.Width) / 2;
                int y = 0;
                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);
                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)
            {
                ImageAnimator.Animate(m_Image, evtHandler);
            }
        }

        // 动画触发时重绘窗体
        private void OnImageAnimate(Object sender, EventArgs e)
        {
            this.Invalidate();
        }

        // 更新动画帧
        private void UpdateImage()
        {
            ImageAnimator.UpdateFrames(m_Image);
        }

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

注意:代码中未重写 PanelOnPaint 方法,是因为局部重绘会导致动图闪烁。因此采用窗体重绘(Invalidate())来避免闪屏问题。

通用调用方法

为了便于在项目中复用,我们封装了一个静态方法 Common.ShowProcessing

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);

总结

本文介绍了一种在 WinForm 中实现通用遮罩层的完整解决方案,具备以下优势:

  • 高复用性:通过封装静态方法,可在任意窗体中一键调用。

  • 良好体验:半透明遮罩 + 动态图标 + 文字提示,媲美 Web 端效果。

  • 线程安全:耗时任务在后台线程执行,避免界面卡顿。

  • 自动关闭:任务完成后自动销毁遮罩窗体,无需手动管理。

该方案特别适用于数据导入、报表生成、批量处理、网络请求等耗时操作场景。开发者可直接复制代码集成到项目中,快速提升应用的专业度与用户体验。

关键词

WinForm、遮罩层、透明窗体、GIF动画、ImageAnimator、OnPaint、异步处理、用户体验、通用组件、C#

最后

如果你觉得这篇文章对你有帮助,不妨点个赞支持一下!你的支持是我继续分享知识的动力。如果有任何疑问或需要进一步的帮助,欢迎随时留言。

也可以加入微信公众号 [DotNet技术匠] 社区,与其他热爱技术的同行一起交流心得,共同成长!

优秀是一种习惯,欢迎大家留言学习!

作者:梦在旅途

出处:cnblogs.com/zuowj/p/4431856.html

声明:网络内容,仅供学习,尊重版权,侵权速删,歉意致谢!