教你用 WinForm 创建动态 Splash 启动画面

35 阅读4分钟

前言

在软件开发中,用户体验是衡量一个应用是否成功的重要标准。当我们的应用程序需要加载大量资源或执行耗时操作时,用户往往需要等待一段时间。如果在这期间界面没有任何反馈,用户可能会误以为程序卡死或无响应,从而产生不良体验。

为了解决这个问题,引入一个美观、友好的启动加载界面(Splash Screen)就显得尤为重要。它不仅能向用户展示当前的加载进度,还能提升整体应用的专业感和亲和力。

本文将一步步实现一个基于 Windows 窗体应用的动态加载界面,利用多线程技术展示一个独立运行的"欢迎窗体",并在主窗体加载过程中实时更新状态信息,最后平滑地关闭加载界面,完成整个启动流程。

实现一个动态加载界面

1、创建项目与主界面

我们首先创建一个名为 thinger.com.SplashProject 的 Windows 窗体应用项目,并添加一个主窗体 FrmMain

这个窗体代表应用的主要操作界面。你可以根据需求绘制简单的 UI 元素,用于模拟主程序的功能。

2、定义接口与加载窗体

为了实现加载界面的通用性和可扩展性,我们定义一个 ISplashForm 接口,用于规范加载窗体必须具备的功能:

public interface ISplashForm  
{  
    void SetStatusInfo(string NewStatusInfo);  
}

接着,创建一个名为 FrmSplash 的新窗体,并让其继承 ISplashForm 接口。在该窗体中添加一个 Label 控件(命名为 lbStatusInfo)用于显示加载状态。

实现接口方法:

void ISplashForm.SetStatusInfo(string NewStatusInfo)  
{  
    lbStatusInfo.Text = NewStatusInfo;  
}

FrmSplash 窗体进行如下设置:

  • FormBorderStyle:None(无边框)

  • StartPosition:CenterScreen(居中显示)

  • TopMost:True(始终置顶)

  • UseWaitCursor:True(显示等待光标)

  • 添加一个 PictureBox 控件,Dock 设置为 Fill,并设置一张美观的背景图片

设计效果如下:

3、核心类 Splasher 的实现

Splasher 类是整个加载机制的核心,负责管理加载窗体的生命周期。

3.1 基本结构

public class Splasher  
{  
    private delegate void SplashStatusChangedHandle(string NewStatusInfo);  
    private static Form m_SplashForm = null;  
    private static ISplashForm m_SplashInterface = null;  
    private static Thread m_SplashThread = null;  
    private static string m_TempStatus = string.Empty;  
}

3.2 显示加载窗体

public static void Show(Type splashFormType)  
{  
    if (m_SplashThread != null)  
        return;  
    if (splashFormType == null)  
        return;  

    m_SplashThread = new Thread(new ThreadStart(delegate()  
    {  
        CreateInstance(splashFormType);  
        Application.Run(m_SplashForm);  
    }));  

    m_SplashThread.IsBackground = true;  
    m_SplashThread.SetApartmentState(ApartmentState.STA);  
    m_SplashThread.Start();  
}

3.3 创建窗体实例

private static void CreateInstance(Type FormType)  
{  
    object obj = FormType.InvokeMember(null,  
        BindingFlags.DeclaredOnly |  
        BindingFlags.Public | BindingFlags.NonPublic |  
        BindingFlags.Instance | BindingFlags.CreateInstance, null, null, null);  
    m_SplashForm = obj as Form;  
    m_SplashInterface = obj as ISplashForm;  
    if (m_SplashForm == null)  
    {  
        throw new Exception("动画窗体必须为Form窗体");  
    }  
    if (m_SplashInterface == null)  
    {  
        throw new Exception("动画窗体必须继承ISplashForm");  
    }  

    if (!string.IsNullOrEmpty(m_TempStatus))  
        m_SplashInterface.SetStatusInfo(m_TempStatus);  
}

3.4 更新加载状态

public static string Status  
{  
    set  
    {  
        if (m_SplashInterface == null || m_SplashForm == null)  
        {  
            m_TempStatus = value;  
            return;  
        }  
        m_SplashForm.Invoke(  
            new SplashStatusChangedHandle(delegate(string str) { m_SplashInterface.SetStatusInfo(str); }),  
            new object[] { value }  
        );  
    }  
}

3.5 关闭加载窗体

public static void Close()  
{  
    if (m_SplashThread == null || m_SplashForm == null) return;  

    try  
    {  
        m_SplashForm.Invoke(new MethodInvoker(m_SplashForm.Close));  
    }  
    catch (Exception)  
    {  
    }  
    m_SplashThread = null;  
    m_SplashForm = null;  
}

4、集成与功能实现

4.1 在程序入口处显示加载界面

修改 Program.cs 文件,在 Main 方法中调用 Splasher.Show

static class Program  
{
    /// <summary>
    /// 应用程序的主入口点。
    /// </summary>
    [STAThread]
    static void Main()
    {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        Splasher.Show(typeof(FrmSplash));
        Application.Run(new FrmMain());
    }
}

4.2 模拟加载过程并更新状态

FrmMainLoad 事件中模拟耗时操作:

private void FrmMain_Load(object sender, EventArgs e)  
{
    Thread.Sleep(500);
    Splasher.Status = "正在加载系统模块......";  

    Thread.Sleep(1000);
    Splasher.Status = "正在加载控件信息......";  

    Thread.Sleep(1000);
    Splasher.Status = "正在加载配置文件......";  

    Thread.Sleep(1000);
    Splasher.Status = "正在连接PLC设备......";  

    this.Opacity = 0;
    this.Shown += FrmMain_Shown;
}

4.3 完成加载并关闭界面

FrmMainShown 事件中完成最后的收尾工作:

private void FrmMain_Shown(object sender, EventArgs e)
{
    this.Opacity = 100;
    Splasher.Status = "初始化完毕......";
    Splasher.Close();
}

5、最终效果

通过上述步骤,我们成功实现了一个动态加载界面。程序启动时,首先显示一个美观的 FrmSplash 窗体,主窗体在后台加载资源,同时实时更新加载状态。

当主窗体准备就绪后,加载界面平滑关闭,主窗体完全显示。整个过程流畅自然,极大地提升了用户体验。

最终运行效果如下所示:

总结

本文通过一个完整的示例,展示了如何在 Windows 窗体应用中实现一个动态加载界面。我们利用多线程技术将加载界面与主程序逻辑分离,通过接口和委托机制实现了状态的跨线程更新。

这种方法不仅结构清晰、易于维护,而且具有良好的扩展性,可以轻松适配不同的加载场景和界面设计。对于需要提升启动体验的桌面应用,这种技术方案值得借鉴和应用。

关键词

加载界面、Windows窗体、多线程、Splash Screen、用户体验、ISplashForm、FrmSplash、Splasher、状态更新、程序启动

最后

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

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

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