【问题标题】:How to build splash screen in windows forms application?如何在 Windows 窗体应用程序中构建启动画面?
【发布时间】:2011-12-18 19:58:09
【问题描述】:

我需要在我的应用程序启动时显示启动画面几秒钟。有人知道如何实现吗?

非常感谢您的帮助。

【问题讨论】:

标签: c# winforms splash-screen


【解决方案1】:

这里有一些指导步骤...

  1. 创建无边框表单(这将是您的初始屏幕)
  2. 在应用程序启动时,启动一个计时器(间隔几秒)
  3. 显示您的启动表单
  4. 在 Timer.Tick 事件中,停止计时器并关闭 Splash 表单 - 然后显示您的主申请表单

试一试,如果您遇到困难,请回来询问与您的问题相关的更具体问题

【讨论】:

    【解决方案2】:

    首先,将您的初始屏幕创建为无边框、不可移动的形式,上面有您的图像,设置为最初显示在屏幕的中心,按照您想要的方式着色。所有这些都可以在设计器中设置;具体来说,您希望:

    • 将表单的 ControlBox、MaximizeBox、MinimizeBox 和 ShowIcon 属性设置为“False”
    • 将 StartPosition 属性设置为“CenterScreen”
    • 将 FormBorderStyle 属性设置为“无”
    • 将表单的 MinimumSize 和 MaximumSize 设置为与其初始大小相同。

    然后,您需要决定在哪里显示和在哪里关闭它。这两个任务需要发生在程序主启动逻辑的相反两侧。这可能在您的应用程序的 main() 例程中,或者可能在您的主应用程序表单的 Load 处理程序中;无论您在哪里创建大型昂贵对象、从硬盘读取设置,以及在主应用程序屏幕显示之前通常需要很长时间在幕后完成工作。

    然后,您所要做的就是创建表单的一个实例,Show() 它,并在启动初始化时保留对它的引用。加载主表单后,请关闭()它。

    如果您的初始屏幕上有动画图像,则窗口也需要“双缓冲”,并且您需要绝对确保所有初始化逻辑都发生在 GUI 线程之外(这意味着您不能在主窗体的 Load 处理程序中拥有您的主要加载逻辑;您必须创建一个 BackgroundWorker 或其他一些线程例程。

    【讨论】:

    • 启动画面也应该在一个单独的线程上运行,让一个进程在后台运行,这样您的应用程序就可以在显示的同时进行工作(例如,连接到您的数据库、加载文件)。它告诉用户您的应用程序正在运行,并且不会浪费他们的时间停留 X 秒。
    • +1 了解详情。我在这里使用新的应用程序循环 (Application.Run) 和单例方法创建了一个启动画面,因此我可以在一开始就创建启动画面并在显示登录表单之前关闭它。
    • @ALMMa - 我在构建初始屏幕实现时做了几乎相同的事情,包括通过命令或某些类型的窗口(例如 Windows 安全登录对话框)显式隐藏或“隐藏”它的能力被检测到。
    • 缺少一个工作脚本示例
    • @JinSnow - “我该怎么做”并不总是最好用“像这样:”后跟代码块来回答。举个例子,闪屏的最佳实现可能会根据它所插入的应用程序的加载行为而有很大差异。我们也不鼓励在这里“给我 t3h c0d3z”。我的回答(和许多其他人)为您提供了创建表单的步骤(包括要设置的特定属性),以及在将其插入初始化逻辑时需要注意的各种警告。寻求更具体帮助的问题预计会显示他们到目前为止所获得的信息。
    【解决方案3】:

    首先你应该创建一个带或不带边框的表单(这些东西首选无边框)

    public class SplashForm : Form
    {
        Form _Parent;
        BackgroundWorker worker;
        public SplashForm(Form parent)
        {
             InitializeComponent();
             BackgroundWorker worker = new BackgroundWorker();
             this.worker.DoWork += new System.ComponentModel.DoWorkEventHandler(this.worker _DoWork);
             backgroundWorker1.RunWorkerAsync();
             _Parent = parent;
        }
        private void worker _DoWork(object sender, DoWorkEventArgs e)
        {
             Thread.sleep(500);
             this.hide();
             _Parent.show();
        }     
    }
    

    在 Main 你应该使用它

       static class Program
            {
                [STAThread]
                static void Main()
                {
                    Application.EnableVisualStyles();
                    Application.SetCompatibleTextRenderingDefault(false);
                    Application.Run(new SplashForm());
                }
            }
    

    【讨论】:

    • 这很糟糕。这将使您的 SplashForm 成为您应用程序的主表单。这意味着当您关闭它时,应用程序将停止
    【解决方案4】:

    创建飞溅

    private void timer1_Tick(object sender, EventArgs e)
    {
        counter++;
        progressBar1.Value = counter *5;
        // label2.Text = (5*counter).ToString();
        if (counter ==20)
        {
            timer1.Stop();
            this.Close();
        }
    }
    this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
    this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
    this.BackColor = System.Drawing.SystemColors.GradientInactiveCaption;
    this.ClientSize = new System.Drawing.Size(397, 283);
    this.ControlBox = false;
    this.Controls.Add(this.label2);
    this.Controls.Add(this.progressBar1);
    this.Controls.Add(this.label1);
    this.ForeColor = System.Drawing.SystemColors.ControlLightLight;
    this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
    this.Name = "Splash";
    this.ShowIcon = false;
    this.ShowInTaskbar = false;
    this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
    this.ResumeLayout(false);
    this.PerformLayout();
    

    然后在你的应用程序中

    sp = new Splash();
    sp.ShowDialog();
    

    【讨论】:

      【解决方案5】:

      这里的其他答案很好地涵盖了这一点,但值得知道的是,Visual Studio 中内置了启动屏幕功能:如果您打开 Windows 窗体应用程序的项目属性并查看“应用程序”选项卡,则有一个底部的“启动画面:”选项。您只需在应用程序中选择要显示为启动屏幕的表单,它会在应用程序启动时显示它,并在显示主表单后将其隐藏。

      您仍然需要按上述方式设置表单(使用正确的边框、定位、大小等)

      【讨论】:

      • 这听起来很理想,但我看不到这个选项(在 VS 2013 中)。我可以在网上找到的唯一参考资料仅适用于 Visual Basic,所以也许它只支持那个?
      • 嗨@Giles,很可能是这样,我只在VB应用程序上使用过它
      【解决方案6】:

      创建启动画面的简单易用的解决方案

      1. 打开新表单使用名称“SPLASH”
      2. 随意更改背景图片
      3. 选择进度条
      4. 选择计时器

      现在在计时器中设置计时器滴答声:

      private void timer1_Tick(object sender, EventArgs e)
      {
          progressBar1.Increment(1);
          if (progressBar1.Value == 100) timer1.Stop();        
      }
      

      添加新表单使用名称“FORM-1”并在 FORM 1 中使用以下命令。

      注意:启动表单在打开您的表单之前有效1

      1. 添加这个库

        using System.Threading;
        
      2. 创建函数

        public void splash()
        {     
            Application.Run(new splash());
        }
        
      3. 在初始化中使用以下命令,如下所示。

        public partial class login : Form
        {     
            public login()
            {
                Thread t = new Thread(new ThreadStart(splash));
                t.Start();
                Thread.Sleep(15625);
        
                InitializeComponent();
        
                enter code here
        
                t.Abort();
            }
        }
        

      http://solutions.musanitech.com/c-create-splash-screen/

      【讨论】:

      • 这是我找到的最简单最好的例子。完美运行。
      • @TylerDurden,最简单最佳?请改用this one(我自己会小心地称它足够好)。
      • 感谢@Sinatr,下次我可能会使用该实现,它看起来更加灵活。这个解决方案在我拿起 c# 的时候比较简单,我最初是在寻找一个简单的解决方案,它不需要委托和过多的线程。我还了解到,当您学习新方法时,最佳解决方案会发生变化;)感谢分享
      • 如何摆脱 t.abort() 抛出的异常?
      【解决方案7】:

      试试这个代码

      public partial class ssplashscreen : Form
          {
              public ssplashscreen()
              {                
                  InitializeComponent();    
              }
      
              private void timer1_Tick(object sender, EventArgs e)
              {
                  progressBar1.Increment(1);
                  if (progressBar1.Value == 100)
                  {
                      timer1.Stop();
                      this.Hide();
                      Form frm = new login();
                      frm.Show();
                  }
              }
          }
      

      【讨论】:

      • 这个答案可能需要一些解释。
      【解决方案8】:

      试试这个:

      namespace SplashScreen
      {
          public partial class frmSplashScreen : Form
          {
              public frmSplashScreen()
              {
                  InitializeComponent();
              }
      
              public int LeftTime { get; set; }
      
              private void frmSplashScreen_Load(object sender, EventArgs e)
              {
                  LeftTime = 20;
                  timer1.Start();
              }
      
              private void timer1_Tick(object sender, EventArgs e)
              {
                  if (LeftTime > 0)
                  {
                      LeftTime--;
                  }
                  else
                  {
                      timer1.Stop();
                      new frmHomeScreen().Show();
                      this.Hide();
                  }
              }
          }
      }
      

      【讨论】:

        【解决方案9】:

        我想要一个在主程序窗体准备好显示之前一直显示的启动画面,所以计时器等对我来说没用。我也想让它尽可能简单。 我的申请以(缩写)开头:

        static void Main()
        {
            Splash frmSplash = new Splash();
            frmSplash.Show();
            Application.Run(new ReportExplorer(frmSplash));
        }
        

        那么,ReportExplorer 有以下内容:

        public ReportExplorer(Splash frmSplash)
        {
            this.frmSplash = frmSplash;
            InitializeComponent();
        }
        

        最后,在所有初始化完成后:

        if (frmSplash != null) 
        {
             frmSplash.Close();
             frmSplash = null;
        }
        

        也许我遗漏了一些东西,但这似乎比处理线程和计时器要容易得多。

        【讨论】:

        • 适用于线程不重要或不需要的简单情况。非常适合初始表单是登录表单,后续表单是“主”表单但在登录表单和主表单之间存在大量数据负载的场景
        • 唯一的问题是当你在启动画面上有一些动画时。例如,带有 Marquee 样式的进度条不会动画。
        【解决方案10】:

        也许回答有点晚,但我想分享我的方式。 我发现了一种在 winform 应用程序的主程序中使用线程的简单方法。

        假设您的表单“启动画面”带有动画,而“主”表单包含您的所有应用程序代码。

         [STAThread]
            static void Main()
            {
                Application.EnableVisualStyles();
                Application.SetCompatibleTextRenderingDefault(false);
                Thread mythread;
                mythread = new Thread(new ThreadStart(ThreadLoop));
                mythread.Start();
                Application.Run(new MainForm(mythread));           
            }
        
            public static void ThreadLoop()
            {
                Application.Run(new SplashScreenForm());           
            }
        

        在构造函数的主窗体中:

         public MainForm(Thread splashscreenthread)
            {
                InitializeComponent();
        
                //add your constructor code
        
                splashscreenthread.Abort();            
            }
        

        这样,启动画面将持续到主窗体加载的时间。

        您的启动画面表单应该有自己的方式来动画/显示信息。 在我的项目中,我的启动画面启动了一个新线程,每隔 x 毫秒就会将他的主画面更改为另一个稍微不同的齿轮,从而产生旋转的错觉。

        我的启动画面示例:

        int status = 0;
        private bool IsRunning = false;
            public Form1()
            {
                InitializeComponent();
                StartAnimation();
            }
        
            public void StartAnimation()
            {
                backgroundWorker1.WorkerReportsProgress = false;
                backgroundWorker1.WorkerSupportsCancellation = true;
                IsRunning = true;
                backgroundWorker1.RunWorkerAsync();
            }
        
        
            public void StopAnimation()
            {
                backgroundWorker1.CancelAsync();
            }
        
            delegate void UpdatingThreadAnimation();
            public void UpdateAnimationFromThread()
            {
        
                try
                {
                    if (label1.InvokeRequired == false)
                    {
                        UpdateAnimation();
                    }
                    else
                    {
                        UpdatingThreadAnimation d = new UpdatingThreadAnimation(UpdateAnimationFromThread);
                        this.Invoke(d, new object[] { });
                    }
                }
                catch(Exception e)
                {
        
                }
            }
        
         private void UpdateAnimation()
            {
            if(status ==0) 
            {
            // mypicture.image = image1
             }else if(status ==1)
             {
            // mypicture.image = image2
             }
            //doing as much as needed
        
              status++;
                if(status>1) //change here if you have more image, the idea is to set a cycle of images
                {
                    status = 0;
                }
                this.Refresh();
            }
        
        private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
            {
                BackgroundWorker worker = sender as BackgroundWorker;
                while (IsRunning == true)
                {
                    System.Threading.Thread.Sleep(100);
                    UpdateAnimationFromThread();
                }
            }
        

        希望这会帮助一些人。 对不起,如果我犯了一些错误。英语不是我的第一语言。

        【讨论】:

        • "splashscreenthread.Abort();" 似乎并不总是中止线程。
        【解决方案11】:

        我最终做了些许不同,因为我对其他解决方案不满意。基本上,出于某种原因,我没有让它们按预期工作。

        我不希望启动画面显示一段固定的时间,而是只要 MainForm 正在加载,加载时间会根据与数据库的连接速度而有所不同。

        简而言之,我的MainForm 构造函数生成了一个显示我的SplashForm 的线程,并且在OnShown 事件中,启动线程被中止。起初这不起作用,SplashForm 有时会挂起(隐藏),并且在关闭 MainForm 时它会等待启动线程退出。解决方案是捕获ThreadAbortException 并在表单上调用Dispose

        示例代码

        private readonly Thread _splashThread = null;
        
        public MainForm() {
            InitializeComponent();
            _splashThread = new Thread(new ThreadStart(DoSplash));
            _splashThread.Start();
        }
        
        private void DoSplash()
        {
            var splashForm = new SplashForm();
            try
            {
                splashForm.ShowDialog();
            }
            catch (ThreadAbortException)
            {
                splashForm.Dispose();
            }
        }
        
        protected override void OnShown(EventArgs e)
        {
            if (_splashThread != null && _splashThread.IsAlive)
            {
               _splashThread.Abort();
            }
            base.OnShown(e);
        }
        

        【讨论】:

          【解决方案12】:

          这是创建启动画面的最简单方法:

          首先,在Form1.cs代码的命名空间前添加如下代码:

          使用 System.Threading;

          现在,请按照以下步骤操作:

          1. 在您的应用程序中添加新表单

          2. 将此新表单命名为 FormSplashScreen

          3. 在 BackgroundImage 属性中,从您的一个文件夹中选择一张图片

          4. 添加进度条

          5. 在 Dock 属性中,将其设置为底部

          6. 在 MarksAnimationSpeed 属性中,设置为 50

          7. 在默认命名为 Form1.cs 的主窗体中,创建以下方法:

             private void StartSplashScreen()
             {
                 Application.Run(new Forms.FormSplashScreen());
             }
            
          8. 在Form1.cs的构造方法中,添加如下代码:

             public Form1()
             {
                 Thread t = new Thread(new ThreadStart(StartSplashScreen));
                 t.Start();
                 Thread.Sleep(5000);
            
                 InitializeComponent();//This code is automatically generated by Visual Studio
            
                 t.Abort();
             }
            
          9. 现在,只需运行应用程序,它就会完美运行。

          【讨论】:

            【解决方案13】:

            其他答案都没有给出我正在寻找的东西。继续阅读我的解决方案。

            我希望启动屏幕在启动时从 0% 的不透明度淡入到 100% 的不透明度,最短显示时间为 2000 毫秒(以允许显示完整的淡入效果)。一切准备就绪后,我希望启动屏幕再显示 500 毫秒,而主屏幕显示在启动屏幕后面。然后我希望闪屏消失,让主屏幕继续运行。

            请注意,我将 MVP 模式用于 winforms。如果您不使用 MVP,则需要稍微简化以下示例。

            长话短说,您需要创建一个继承自ApplicationContextAppContext 类。我把它放在我的Program.cs 中,如下所示:

            static class Program
            {
                /// <summary>
                ///  The main entry point for the application.
                /// </summary>
                [STAThread]
                static void Main()
                {
                    Application.SetHighDpiMode(HighDpiMode.SystemAware);
                    Application.EnableVisualStyles();
                    Application.SetCompatibleTextRenderingDefault(false);
                    Application.Run(new AppContext());
                }
            }
            
            public class AppContext : ApplicationContext
            {
                private IMainPresenter _mainPresenter;
                private bool _ready;
            
                public AppContext()
                {
                    _ready = false;
            
                    using (ISplashPresenter splashPresenter = new SplashPresenter(new SplashView()))
                    {
                        Stopwatch sw = Stopwatch.StartNew();
            
                        _mainPresenter = new MainPresenter(new MainView());
                        _mainPresenter.Closed += MainPresenter_Closed;
            
                        new Thread(() =>
                            {
                                // !!! Do work here !!!
            
                                if (sw.ElapsedMilliseconds < 2000)
                                    Thread.Sleep(2000 - (int)sw.ElapsedMilliseconds);
            
                                _ready = true;
                            })
                            .Start();
            
                        while (!_ready)
                        {
                            Application.DoEvents();
                            Thread.Sleep(1);
                        }
            
                        _mainPresenter.Show();
            
                        _ready = false;
            
                        new Thread(() =>
                            {
                                Thread.Sleep(500);
            
                                _ready = true;
                            })
                            .Start();
            
                        while (!_ready)
                        {
                            Application.DoEvents();
                            Thread.Sleep(1);
                        }
                    }
                }
            
                private void MainPresenter_Closed(object sender, EventArgs e)
                {
                    ExitThread();
                }
            }
            

            这里有几个具体的实现细节我没有讲到,比如ISplashPresenter 实现IDisposable 以及淡入的具体管理方式;如果有足够多的人提出要求,我将编辑此答案以包含一个完整的示例。

            【讨论】:

              猜你喜欢
              • 2011-07-01
              • 2011-12-19
              • 2010-09-20
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2015-12-01
              • 1970-01-01
              相关资源
              最近更新 更多