c#中的多线程启动屏幕?

时间:2021-07-24 21:01:03

I want a splash screen to show while the application is loading. I have a form with a system tray control tied to it. I want the splash screen to display while this form loads, which takes a bit of time since it's accessing a web service API to populate some drop-downs. I also want to do some basic testing for dependencies before loading (that is, the web service is available, the configuration file is readable). As each phase of the startup goes, I want to update the splash screen with progress.

我希望在应用程序加载时显示一个启动屏幕。我有一个带有系统托盘控件的表单。我希望在加载该表单时显示splash屏幕,这需要一些时间,因为它正在访问web服务API来填充一些下拉列表。我还希望在加载之前对依赖项做一些基本的测试(也就是说,web服务是可用的,配置文件是可读的)。随着启动的每个阶段的进行,我想要更新启动画面并取得进展。

I have been reading a lot on threading, but I am getting lost on where this should be controlled from (the main() method?). I am also missing how Application.Run() works, is this where the threads for this should be created from? Now, if the form with the system tray control is the "living" form, should the splash come from there? Wouldn't it not load until the form is completed anyway?

我已经阅读了很多关于线程的文章,但是我迷失在从(main()方法?)控制线程的位置上。我也不知道Application.Run()是如何工作的,它的线程应该从这里创建吗?现在,如果带有系统托盘控件的窗体是“活动”窗体,溅起的水花应该来自那里吗?在表单完成之前,它不是不会被加载吗?

I'm not looking for a code handout, more of an algorithm/approach so I can figure this out once and for all :)

我不是在找代码讲义,更多的是一种算法/方法,这样我就可以一劳永逸地解决这个问题:)

12 个解决方案

#1


47  

Well, for a ClickOnce app that I deployed in the past, we used the Microsoft.VisualBasic namespace to handle the splash screen threading. You can reference and use the Microsoft.VisualBasic assembly from C# in .NET 2.0 and it provides a lot of nice services.

对于我以前部署的ClickOnce应用,我们使用的是微软。VisualBasic命名空间来处理启动屏幕线程。你可以参考和使用微软。在。net 2.0中使用c#进行VisualBasic程序集,它提供了许多很好的服务。

  1. Have the main form inherit from Microsoft.VisualBasic.WindowsFormsApplicationBase
  2. 主要形式是否继承自Microsoft.VisualBasic.WindowsFormsApplicationBase
  3. Override the "OnCreateSplashScreen" method like so:

    像这样覆盖“OnCreateSplashScreen”方法:

    protected override void OnCreateSplashScreen()
    {
        this.SplashScreen = new SplashForm();
        this.SplashScreen.TopMost = true;
    }
    

Very straightforward, it shows your SplashForm (which you need to create) while loading is going on, then closes it automatically once the main form has completed loading.

非常简单,它在加载过程中显示了SplashForm(需要创建),然后在主表单完成加载后自动关闭它。

This really makes things simple, and the VisualBasic.WindowsFormsApplicationBase is of course well tested by Microsoft and has a lot of functionality that can make your life a lot easier in Winforms, even in an application that is 100% C#.

这真的让事情变得简单,而且直观。WindowsFormsApplicationBase当然经过了Microsoft的良好测试,它有许多功能,可以使Winforms中的您的生活变得更轻松,即使是在100% c#的应用程序中也是如此。

At the end of the day, it's all IL and bytecode anyway, so why not use it?

最后,它是所有的IL和字节码,所以为什么不使用它呢?

#2


47  

The trick is to to create separate thread responsible for splash screen showing.
When you run you app .net creates main thread and loads specified (main) form. To conceal hard work you can hide main form until loading is done.

诀窍是创建单独的线程,负责启动屏幕显示。当你运行app。net时,它会创建主线程并加载指定的(主)表单。要隐藏艰苦的工作,你可以隐藏主要形式,直到加载完成。

Assuming that Form1 - is your main form and SplashForm is top level, borderles nice splash form:

假设Form1 -是你的主窗体,SplashForm是*窗体,边界上有漂亮的飞溅窗体:

private void Form1_Load(object sender, EventArgs e)
{
    Hide();
    bool done = false;
    ThreadPool.QueueUserWorkItem((x) =>
    {
        using (var splashForm = new SplashForm())
        {
            splashForm.Show();
            while (!done)
                Application.DoEvents();
            splashForm.Close();
        }
    });

    Thread.Sleep(3000); // Emulate hardwork
    done = true;
    Show();
}

#3


14  

After looking all over Google and SO for solutions, this is my favorite: http://bytes.com/topic/c-sharp/answers/277446-winform-startup-splash-screen

在查看了谷歌和其他解决方案之后,我最喜欢的是:http://bytes.com/topic/c-sharp/answers/277446-winform-startup-splash-screen

FormSplash.cs:

FormSplash.cs:

public partial class FormSplash : Form
{
    private static Thread _splashThread;
    private static FormSplash _splashForm;

    public FormSplash() {
        InitializeComponent();
    }

    /// <summary>
    /// Show the Splash Screen (Loading...)
    /// </summary>
    public static void ShowSplash()
    {
        if (_splashThread == null)
        {
            // show the form in a new thread
            _splashThread = new Thread(new ThreadStart(DoShowSplash));
            _splashThread.IsBackground = true;
            _splashThread.Start();
        }
    }

    // called by the thread
    private static void DoShowSplash()
    {
        if (_splashForm == null)
            _splashForm = new FormSplash();

        // create a new message pump on this thread (started from ShowSplash)
        Application.Run(_splashForm);
    }

    /// <summary>
    /// Close the splash (Loading...) screen
    /// </summary>
    public static void CloseSplash()
    {
        // need to call on the thread that launched this splash
        if (_splashForm.InvokeRequired)
            _splashForm.Invoke(new MethodInvoker(CloseSplash));

        else
            Application.ExitThread();
    }
}

Program.cs:

Program.cs:

static class Program
{
    /// <summary>
    /// The main entry point for the application.
    /// </summary>
    [STAThread]
    static void Main(string[] args)
    {
        // splash screen, which is terminated in FormMain
        FormSplash.ShowSplash();

        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        // this is probably where your heavy lifting is:
        Application.Run(new FormMain());
    }
}

FormMain.cs

FormMain.cs

    ...

    public FormMain()
    {
        InitializeComponent();            

        // bunch of database access, form loading, etc
        // this is where you could do the heavy lifting of "loading" the app
        PullDataFromDatabase();
        DoLoadingWork();            

        // ready to go, now close the splash
        FormSplash.CloseSplash();
    }

I had issues with the Microsoft.VisualBasic solution -- Worked find on XP, but on Windows 2003 Terminal Server, the main application form would show up (after the splash screen) in the background, and the taskbar would blink. And bringing a window to foreground/focus in code is a whole other can of worms you can Google/SO for.

我和微软有矛盾。VisualBasic解决方案——在XP上使用过find,但是在Windows 2003终端服务器上,主应用程序表单会在后台显示(在启动屏幕之后),任务栏会闪烁。在代码中引入一个窗口到前台/焦点是另一个可以谷歌/SO的蠕虫。

#4


9  

I recommend calling Activate(); directly after the last Show(); in the answer provided by aku.

我建议打电话激活();直接在最后一个Show()之后;在aku提供的答案中。

Quoting MSDN:

引用MSDN:

Activating a form brings it to the front if this is the active application, or it flashes the window caption if this is not the active application. The form must be visible for this method to have any effect.

如果这是活动应用程序,激活一个表单会将它带到前台;如果不是活动应用程序,则会闪烁窗口标题。这个窗体必须是可见的,这样才能产生任何效果。

If you don't activate your main form, it may be displayed behind any other open windows, making it look a bit silly.

如果你不激活你的主窗体,它可能会显示在任何其他打开的窗口后面,使它看起来有点傻。

#5


9  

This is an old question, but I kept coming across it when trying to find a threaded splash screen solution for WPF that could include animation.

这是一个老问题,但是当我试图为WPF找到一个包含动画的线程启动屏幕解决方案时,我一直遇到这个问题。

Here is what I ultimately pieced together:

以下是我最后拼凑的:

App.XAML:

App.XAML:

<Application Startup="ApplicationStart" …

App.XAML.cs:

App.XAML.cs:

void ApplicationStart(object sender, StartupEventArgs e)
{
        var thread = new Thread(() =>
        {
            Dispatcher.CurrentDispatcher.BeginInvoke ((Action)(() => new MySplashForm().Show()));
            Dispatcher.Run();
        });
        thread.SetApartmentState(ApartmentState.STA);
        thread.IsBackground = true;
        thread.Start();

        // call synchronous configuration process
        // and declare/get reference to "main form"

        thread.Abort();

        mainForm.Show();
        mainForm.Activate();
  }

#6


7  

I think using some method like aku's or Guy's is the way to go, but a couple of things to take away from the specific examples:

我认为使用aku或Guy的方法是可行的,但要从具体的例子中得到一些东西:

  1. The basic premise would be to show your splash on a separate thread as soon as possible. That's the way I would lean, similar to what aku's illustrated, since it's the way I'm most familiar with. I was not aware of the VB function Guy mentioned. And, even thought it's a VB library, he is right -- it's all IL in the end. So, even if it feels dirty it's not all that bad! :) I think you'll want to be sure that either VB provides a separate thread for in that override or that you create one yourself -- definitely research that.

    基本的前提是尽快在一个单独的线程上显示您的溅泼。这是我的方式,类似于aku的插图,因为这是我最熟悉的方式。我不知道VB函数。尽管他认为这是一个VB库,但他是对的,它最终都是IL。所以,即使感觉脏了也没那么糟糕!:)我认为您应该确保VB为该覆盖提供一个单独的线程,或者您自己创建一个线程——一定要对此进行研究。

  2. Assuming you create another thread to display this splash, you will want to be careful of cross thread UI updates. I bring this up because you mentioned updating progress. Basically, to be safe, you need to call an update function (that you create) on the splash form using a delegate. You pass that delegate to the Invoke function on your splash screen's form object. In fact if you call the splash form directly to update progress/UI elements on it, you'll get an exception provided you are running on the .Net 2.0 CLR. As a rule of thumb, any UI element on a form must be updated by the thread that created it -- that's what Form.Invoke insures.

    假设您创建了另一个线程来显示这个splash,那么您将需要小心跨线程UI更新。我提到这个是因为你提到了更新进度。基本上,为了安全起见,您需要使用委托调用splash窗体上的update函数(您创建的)。将该委托传递给启动屏幕的窗体对象上的调用函数。实际上,如果您直接调用splash表单来更新它的进程/UI元素,那么在. net 2.0 CLR上运行时,您会得到一个异常。根据经验,表单上的任何UI元素都必须由创建它的线程更新——这就是表单。调用确保。

Finally, I would likely opt to create the splash (if not using the VB overload) in the main method of your code. To me this is better than having the main form perform creation of the object and to be so tightly bound to it. If you take that approach, I'd suggest creating a simple interface that the splash screen implements -- something like IStartupProgressListener -- which receives start-up progress updates via a member function. This will allow you to easily swap in/out either class as needed, and nicely decouples the code. The splash form can also know when to close itself if you notify when start-up is complete.

最后,我可能会选择在代码的主要方法中创建splash(如果不使用VB重载)。对我来说,这比让主窗体执行对象的创建和与它紧密绑定要好。如果您采用这种方法,我建议您创建一个简单的接口,让splash屏幕实现这个接口,比如IStartupProgressListener,它通过成员函数接收启动进度更新。这将允许您根据需要方便地交换或交换任何类,并很好地解耦代码。如果您在启动完成时通知您,启动表单还可以知道何时关闭自己。

#7


6  

One simple way is the use something like this as main():

一种简单的方法是使用像这样的main():

<STAThread()> Public Shared Sub Main()

    splash = New frmSplash
    splash.Show()

    ' Your startup code goes here...

    UpdateSplashAndLogMessage("Startup part 1 done...")

    ' ... and more as needed...

    splash.Hide()
    Application.Run(myMainForm)
End Sub

When the .NET CLR starts your application, it creates a 'main' thread and starts executing your main() on that thread. The Application.Run(myMainForm) at the end does two things:

当. net CLR启动应用程序时,它会创建一个“main”线程,并开始在该线程上执行main()。最后的Application.Run(myMainForm)做了两件事:

  1. Starts the Windows 'message pump', using the thread that has been executing main() as the GUI thread.
  2. 使用一直执行main()的线程作为GUI线程启动Windows的“消息泵”。
  3. Designates your 'main form' as the 'shutdown form' for the application. If the user closes that form, then the Application.Run() terminates and control returns to your main(), where you can do any shutdown you want.
  4. 将“主表单”指定为应用程序的“关机表单”。如果用户关闭该表单,那么Application.Run()将终止,控制将返回到main(),您可以在那里执行任何关闭操作。

There is no need to spawn a thread to take care of the splash window, and in fact this is a bad idea, because then you would have to use thread-safe techniques to update the splash contents from main().

不需要生成一个线程来处理splash窗口,实际上这是一个坏主意,因为您必须使用线程安全技术来更新main()中的splash内容。

If you need other threads to do background operations in your application, you can spawn them from main(). Just remember to set Thread.IsBackground to True, so that they will die when the main / GUI thread terminates. Otherwise you will have to arrange to terminate all your other threads yourself, or they will keep your application alive (but with no GUI) when the main thread terminates.

如果需要其他线程在应用程序中执行后台操作,可以从main()派生它们。记住要设置线程。IsBackground为True,因此当主/ GUI线程终止时,它们将死亡。否则,您将不得不自己安排终止所有其他线程,否则当主线程终止时,它们将使您的应用程序保持活动状态(但没有GUI)。

#8


5  

I posted an article on splash screen incorporation in the application at codeproject. It is multithreaded and might be of your interest

我在codeproject上发布了一篇关于“闪屏”的文章。它是多线程的,可能是您感兴趣的。

Yet Another Splash Screen in C#

c#中的另一个闪屏

#9


5  

private void MainForm_Load(object sender, EventArgs e)
{
     FormSplash splash = new FormSplash();
     splash.Show();
     splash.Update();
     System.Threading.Thread.Sleep(3000);
     splash.Hide();
}

I got this from the Internet somewhere but cannot seem to find it again. Simple but yet effective.

我是从网上找来的,但是好像找不到了。简单而又有效。

#10


4  

I like Aku's answer a lot, but the code is for C# 3.0 and up since it uses a lambda function. For people needing to use the code in C# 2.0, here's the code using anonymous delegate instead of the lambda function. You need a topmost winform called formSplash with FormBorderStyle = None. The TopMost = True parameter of the form is important because the splash screen might look like it appears then disappears quickly if it's not topmost. I also choose StartPosition=CenterScreen so it looks like what a professional app would do with a splash screen. If you want an even cooler effect, you can use the TrasparencyKey property to make an irregular shaped splash screen.

我很喜欢Aku的答案,但是代码是c# 3.0以上,因为它使用lambda函数。对于需要在c# 2.0中使用代码的人,这里的代码使用匿名委托而不是lambda函数。您需要一个名为FormBorderStyle = None的最顶层winform。窗体的TopMost = True参数非常重要,因为如果不是TopMost,溅泼屏幕看起来可能会很快消失。我还选择了StartPosition=CenterScreen,这样它看起来就像一个专业的应用程序在使用splash界面时所做的那样。如果你想要更冷的效果,你可以使用TrasparencyKey属性来制作一个不规则形状的飞溅屏幕。

private void formMain_Load(object sender, EventArgs e)
  {

     Hide();
     bool done = false;
     ThreadPool.QueueUserWorkItem(delegate
     {
       using (formSplash splashForm = new formSplash())
       {
           splashForm.Show();
           while (!done)
              Application.DoEvents();
           splashForm.Close();
       }
     }, null);

     Thread.Sleep(2000);
     done = true;
     Show();
  }

#11


3  

I disagree with the other answers recommending WindowsFormsApplicationBase. In my experience, it can slow your app. To be precise, while it runs your form's constructor in parallel with the splash screen, it postpone your form's Shown event.

我不同意其他推荐WindowsFormsApplicationBase的答案。在我的经验中,它可以减慢你的应用程序。准确地说,当它与启动屏幕并行运行窗体的构造函数时,它会延迟窗体的显示事件。

Consider an app (without splashs screen) with a constructor that takes 1 second and a event handler on Shown that takes 2 seconds. This app is usable after 3 seconds.

考虑一个应用程序(没有splashs屏幕),它的构造函数需要1秒,而一个事件处理程序需要2秒。3秒后可用。

But suppose you install a splash screen using WindowsFormsApplicationBase. You might think MinimumSplashScreenDisplayTime of 3 seconds is sensible and won't slow your app. But, try it, your app will now take 5 seconds to load.

但是,假设您使用WindowsFormsApplicationBase安装了一个启动屏幕。你可能会认为3秒的小屏幕显示时间是合理的,不会让你的应用慢下来。


class App : WindowsFormsApplicationBase
{
    protected override void OnCreateSplashScreen()
    {
        this.MinimumSplashScreenDisplayTime = 3000; // milliseconds
        this.SplashScreen = new Splash();
    }

    protected override void OnCreateMainForm()
    {
        this.MainForm = new Form1();
    }
}

and

public Form1()
{
    InitializeComponent();
    Shown += Form1_Shown;
    Thread.Sleep(TimeSpan.FromSeconds(1));
}

void Form1_Shown(object sender, EventArgs e)
{
    Thread.Sleep(TimeSpan.FromSeconds(2));
    Program.watch.Stop();
    this.textBox1.Text = Program.watch.ElapsedMilliseconds.ToString();
}

Conclusion: don't use WindowsFormsApplicationBase if your app has a handler on the Slown event. You can write better code that runs the splash in parallel to both the constructor and the Shown event.

结论:如果你的应用程序在Slown事件上有一个处理器,不要使用WindowsFormsApplicationBase。您可以编写更好的代码,将splash与构造函数和显示事件并行运行。

#12


0  

Actually mutlithreading here is not necessary.

实际上,这里没有必要进行突变。

Let your business logic generate an event whenever you want to update splash screen.

让您的业务逻辑在需要更新启动屏幕时生成事件。

Then let your form update the splash screen accordingly in the method hooked to eventhandler.

然后让您的表单在连接到eventhandler的方法中相应地更新启动屏幕。

To differentiate updates you can either fire different events or provide data in a class inherited from EventArgs.

为了区分更新,您可以触发不同的事件,也可以在从EventArgs继承的类中提供数据。

This way you can have nice changing splash screen without any multithreading headache.

通过这种方式,您可以有一个漂亮的改变飞溅屏幕,没有任何多线程头痛。

Actually with this you can even support, for example, gif image on a splash form. In order for it to work, call Application.DoEvents() in your handler:

实际上,你甚至可以支持,例如,在一个飞溅窗体上的gif图像。为了让它工作,在处理程序中调用Application.DoEvents():

private void SomethingChanged(object sender, MyEventArgs e)
{
    formSplash.Update(e);
    Application.DoEvents(); //this will update any animation
}

#1


47  

Well, for a ClickOnce app that I deployed in the past, we used the Microsoft.VisualBasic namespace to handle the splash screen threading. You can reference and use the Microsoft.VisualBasic assembly from C# in .NET 2.0 and it provides a lot of nice services.

对于我以前部署的ClickOnce应用,我们使用的是微软。VisualBasic命名空间来处理启动屏幕线程。你可以参考和使用微软。在。net 2.0中使用c#进行VisualBasic程序集,它提供了许多很好的服务。

  1. Have the main form inherit from Microsoft.VisualBasic.WindowsFormsApplicationBase
  2. 主要形式是否继承自Microsoft.VisualBasic.WindowsFormsApplicationBase
  3. Override the "OnCreateSplashScreen" method like so:

    像这样覆盖“OnCreateSplashScreen”方法:

    protected override void OnCreateSplashScreen()
    {
        this.SplashScreen = new SplashForm();
        this.SplashScreen.TopMost = true;
    }
    

Very straightforward, it shows your SplashForm (which you need to create) while loading is going on, then closes it automatically once the main form has completed loading.

非常简单,它在加载过程中显示了SplashForm(需要创建),然后在主表单完成加载后自动关闭它。

This really makes things simple, and the VisualBasic.WindowsFormsApplicationBase is of course well tested by Microsoft and has a lot of functionality that can make your life a lot easier in Winforms, even in an application that is 100% C#.

这真的让事情变得简单,而且直观。WindowsFormsApplicationBase当然经过了Microsoft的良好测试,它有许多功能,可以使Winforms中的您的生活变得更轻松,即使是在100% c#的应用程序中也是如此。

At the end of the day, it's all IL and bytecode anyway, so why not use it?

最后,它是所有的IL和字节码,所以为什么不使用它呢?

#2


47  

The trick is to to create separate thread responsible for splash screen showing.
When you run you app .net creates main thread and loads specified (main) form. To conceal hard work you can hide main form until loading is done.

诀窍是创建单独的线程,负责启动屏幕显示。当你运行app。net时,它会创建主线程并加载指定的(主)表单。要隐藏艰苦的工作,你可以隐藏主要形式,直到加载完成。

Assuming that Form1 - is your main form and SplashForm is top level, borderles nice splash form:

假设Form1 -是你的主窗体,SplashForm是*窗体,边界上有漂亮的飞溅窗体:

private void Form1_Load(object sender, EventArgs e)
{
    Hide();
    bool done = false;
    ThreadPool.QueueUserWorkItem((x) =>
    {
        using (var splashForm = new SplashForm())
        {
            splashForm.Show();
            while (!done)
                Application.DoEvents();
            splashForm.Close();
        }
    });

    Thread.Sleep(3000); // Emulate hardwork
    done = true;
    Show();
}

#3


14  

After looking all over Google and SO for solutions, this is my favorite: http://bytes.com/topic/c-sharp/answers/277446-winform-startup-splash-screen

在查看了谷歌和其他解决方案之后,我最喜欢的是:http://bytes.com/topic/c-sharp/answers/277446-winform-startup-splash-screen

FormSplash.cs:

FormSplash.cs:

public partial class FormSplash : Form
{
    private static Thread _splashThread;
    private static FormSplash _splashForm;

    public FormSplash() {
        InitializeComponent();
    }

    /// <summary>
    /// Show the Splash Screen (Loading...)
    /// </summary>
    public static void ShowSplash()
    {
        if (_splashThread == null)
        {
            // show the form in a new thread
            _splashThread = new Thread(new ThreadStart(DoShowSplash));
            _splashThread.IsBackground = true;
            _splashThread.Start();
        }
    }

    // called by the thread
    private static void DoShowSplash()
    {
        if (_splashForm == null)
            _splashForm = new FormSplash();

        // create a new message pump on this thread (started from ShowSplash)
        Application.Run(_splashForm);
    }

    /// <summary>
    /// Close the splash (Loading...) screen
    /// </summary>
    public static void CloseSplash()
    {
        // need to call on the thread that launched this splash
        if (_splashForm.InvokeRequired)
            _splashForm.Invoke(new MethodInvoker(CloseSplash));

        else
            Application.ExitThread();
    }
}

Program.cs:

Program.cs:

static class Program
{
    /// <summary>
    /// The main entry point for the application.
    /// </summary>
    [STAThread]
    static void Main(string[] args)
    {
        // splash screen, which is terminated in FormMain
        FormSplash.ShowSplash();

        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        // this is probably where your heavy lifting is:
        Application.Run(new FormMain());
    }
}

FormMain.cs

FormMain.cs

    ...

    public FormMain()
    {
        InitializeComponent();            

        // bunch of database access, form loading, etc
        // this is where you could do the heavy lifting of "loading" the app
        PullDataFromDatabase();
        DoLoadingWork();            

        // ready to go, now close the splash
        FormSplash.CloseSplash();
    }

I had issues with the Microsoft.VisualBasic solution -- Worked find on XP, but on Windows 2003 Terminal Server, the main application form would show up (after the splash screen) in the background, and the taskbar would blink. And bringing a window to foreground/focus in code is a whole other can of worms you can Google/SO for.

我和微软有矛盾。VisualBasic解决方案——在XP上使用过find,但是在Windows 2003终端服务器上,主应用程序表单会在后台显示(在启动屏幕之后),任务栏会闪烁。在代码中引入一个窗口到前台/焦点是另一个可以谷歌/SO的蠕虫。

#4


9  

I recommend calling Activate(); directly after the last Show(); in the answer provided by aku.

我建议打电话激活();直接在最后一个Show()之后;在aku提供的答案中。

Quoting MSDN:

引用MSDN:

Activating a form brings it to the front if this is the active application, or it flashes the window caption if this is not the active application. The form must be visible for this method to have any effect.

如果这是活动应用程序,激活一个表单会将它带到前台;如果不是活动应用程序,则会闪烁窗口标题。这个窗体必须是可见的,这样才能产生任何效果。

If you don't activate your main form, it may be displayed behind any other open windows, making it look a bit silly.

如果你不激活你的主窗体,它可能会显示在任何其他打开的窗口后面,使它看起来有点傻。

#5


9  

This is an old question, but I kept coming across it when trying to find a threaded splash screen solution for WPF that could include animation.

这是一个老问题,但是当我试图为WPF找到一个包含动画的线程启动屏幕解决方案时,我一直遇到这个问题。

Here is what I ultimately pieced together:

以下是我最后拼凑的:

App.XAML:

App.XAML:

<Application Startup="ApplicationStart" …

App.XAML.cs:

App.XAML.cs:

void ApplicationStart(object sender, StartupEventArgs e)
{
        var thread = new Thread(() =>
        {
            Dispatcher.CurrentDispatcher.BeginInvoke ((Action)(() => new MySplashForm().Show()));
            Dispatcher.Run();
        });
        thread.SetApartmentState(ApartmentState.STA);
        thread.IsBackground = true;
        thread.Start();

        // call synchronous configuration process
        // and declare/get reference to "main form"

        thread.Abort();

        mainForm.Show();
        mainForm.Activate();
  }

#6


7  

I think using some method like aku's or Guy's is the way to go, but a couple of things to take away from the specific examples:

我认为使用aku或Guy的方法是可行的,但要从具体的例子中得到一些东西:

  1. The basic premise would be to show your splash on a separate thread as soon as possible. That's the way I would lean, similar to what aku's illustrated, since it's the way I'm most familiar with. I was not aware of the VB function Guy mentioned. And, even thought it's a VB library, he is right -- it's all IL in the end. So, even if it feels dirty it's not all that bad! :) I think you'll want to be sure that either VB provides a separate thread for in that override or that you create one yourself -- definitely research that.

    基本的前提是尽快在一个单独的线程上显示您的溅泼。这是我的方式,类似于aku的插图,因为这是我最熟悉的方式。我不知道VB函数。尽管他认为这是一个VB库,但他是对的,它最终都是IL。所以,即使感觉脏了也没那么糟糕!:)我认为您应该确保VB为该覆盖提供一个单独的线程,或者您自己创建一个线程——一定要对此进行研究。

  2. Assuming you create another thread to display this splash, you will want to be careful of cross thread UI updates. I bring this up because you mentioned updating progress. Basically, to be safe, you need to call an update function (that you create) on the splash form using a delegate. You pass that delegate to the Invoke function on your splash screen's form object. In fact if you call the splash form directly to update progress/UI elements on it, you'll get an exception provided you are running on the .Net 2.0 CLR. As a rule of thumb, any UI element on a form must be updated by the thread that created it -- that's what Form.Invoke insures.

    假设您创建了另一个线程来显示这个splash,那么您将需要小心跨线程UI更新。我提到这个是因为你提到了更新进度。基本上,为了安全起见,您需要使用委托调用splash窗体上的update函数(您创建的)。将该委托传递给启动屏幕的窗体对象上的调用函数。实际上,如果您直接调用splash表单来更新它的进程/UI元素,那么在. net 2.0 CLR上运行时,您会得到一个异常。根据经验,表单上的任何UI元素都必须由创建它的线程更新——这就是表单。调用确保。

Finally, I would likely opt to create the splash (if not using the VB overload) in the main method of your code. To me this is better than having the main form perform creation of the object and to be so tightly bound to it. If you take that approach, I'd suggest creating a simple interface that the splash screen implements -- something like IStartupProgressListener -- which receives start-up progress updates via a member function. This will allow you to easily swap in/out either class as needed, and nicely decouples the code. The splash form can also know when to close itself if you notify when start-up is complete.

最后,我可能会选择在代码的主要方法中创建splash(如果不使用VB重载)。对我来说,这比让主窗体执行对象的创建和与它紧密绑定要好。如果您采用这种方法,我建议您创建一个简单的接口,让splash屏幕实现这个接口,比如IStartupProgressListener,它通过成员函数接收启动进度更新。这将允许您根据需要方便地交换或交换任何类,并很好地解耦代码。如果您在启动完成时通知您,启动表单还可以知道何时关闭自己。

#7


6  

One simple way is the use something like this as main():

一种简单的方法是使用像这样的main():

<STAThread()> Public Shared Sub Main()

    splash = New frmSplash
    splash.Show()

    ' Your startup code goes here...

    UpdateSplashAndLogMessage("Startup part 1 done...")

    ' ... and more as needed...

    splash.Hide()
    Application.Run(myMainForm)
End Sub

When the .NET CLR starts your application, it creates a 'main' thread and starts executing your main() on that thread. The Application.Run(myMainForm) at the end does two things:

当. net CLR启动应用程序时,它会创建一个“main”线程,并开始在该线程上执行main()。最后的Application.Run(myMainForm)做了两件事:

  1. Starts the Windows 'message pump', using the thread that has been executing main() as the GUI thread.
  2. 使用一直执行main()的线程作为GUI线程启动Windows的“消息泵”。
  3. Designates your 'main form' as the 'shutdown form' for the application. If the user closes that form, then the Application.Run() terminates and control returns to your main(), where you can do any shutdown you want.
  4. 将“主表单”指定为应用程序的“关机表单”。如果用户关闭该表单,那么Application.Run()将终止,控制将返回到main(),您可以在那里执行任何关闭操作。

There is no need to spawn a thread to take care of the splash window, and in fact this is a bad idea, because then you would have to use thread-safe techniques to update the splash contents from main().

不需要生成一个线程来处理splash窗口,实际上这是一个坏主意,因为您必须使用线程安全技术来更新main()中的splash内容。

If you need other threads to do background operations in your application, you can spawn them from main(). Just remember to set Thread.IsBackground to True, so that they will die when the main / GUI thread terminates. Otherwise you will have to arrange to terminate all your other threads yourself, or they will keep your application alive (but with no GUI) when the main thread terminates.

如果需要其他线程在应用程序中执行后台操作,可以从main()派生它们。记住要设置线程。IsBackground为True,因此当主/ GUI线程终止时,它们将死亡。否则,您将不得不自己安排终止所有其他线程,否则当主线程终止时,它们将使您的应用程序保持活动状态(但没有GUI)。

#8


5  

I posted an article on splash screen incorporation in the application at codeproject. It is multithreaded and might be of your interest

我在codeproject上发布了一篇关于“闪屏”的文章。它是多线程的,可能是您感兴趣的。

Yet Another Splash Screen in C#

c#中的另一个闪屏

#9


5  

private void MainForm_Load(object sender, EventArgs e)
{
     FormSplash splash = new FormSplash();
     splash.Show();
     splash.Update();
     System.Threading.Thread.Sleep(3000);
     splash.Hide();
}

I got this from the Internet somewhere but cannot seem to find it again. Simple but yet effective.

我是从网上找来的,但是好像找不到了。简单而又有效。

#10


4  

I like Aku's answer a lot, but the code is for C# 3.0 and up since it uses a lambda function. For people needing to use the code in C# 2.0, here's the code using anonymous delegate instead of the lambda function. You need a topmost winform called formSplash with FormBorderStyle = None. The TopMost = True parameter of the form is important because the splash screen might look like it appears then disappears quickly if it's not topmost. I also choose StartPosition=CenterScreen so it looks like what a professional app would do with a splash screen. If you want an even cooler effect, you can use the TrasparencyKey property to make an irregular shaped splash screen.

我很喜欢Aku的答案,但是代码是c# 3.0以上,因为它使用lambda函数。对于需要在c# 2.0中使用代码的人,这里的代码使用匿名委托而不是lambda函数。您需要一个名为FormBorderStyle = None的最顶层winform。窗体的TopMost = True参数非常重要,因为如果不是TopMost,溅泼屏幕看起来可能会很快消失。我还选择了StartPosition=CenterScreen,这样它看起来就像一个专业的应用程序在使用splash界面时所做的那样。如果你想要更冷的效果,你可以使用TrasparencyKey属性来制作一个不规则形状的飞溅屏幕。

private void formMain_Load(object sender, EventArgs e)
  {

     Hide();
     bool done = false;
     ThreadPool.QueueUserWorkItem(delegate
     {
       using (formSplash splashForm = new formSplash())
       {
           splashForm.Show();
           while (!done)
              Application.DoEvents();
           splashForm.Close();
       }
     }, null);

     Thread.Sleep(2000);
     done = true;
     Show();
  }

#11


3  

I disagree with the other answers recommending WindowsFormsApplicationBase. In my experience, it can slow your app. To be precise, while it runs your form's constructor in parallel with the splash screen, it postpone your form's Shown event.

我不同意其他推荐WindowsFormsApplicationBase的答案。在我的经验中,它可以减慢你的应用程序。准确地说,当它与启动屏幕并行运行窗体的构造函数时,它会延迟窗体的显示事件。

Consider an app (without splashs screen) with a constructor that takes 1 second and a event handler on Shown that takes 2 seconds. This app is usable after 3 seconds.

考虑一个应用程序(没有splashs屏幕),它的构造函数需要1秒,而一个事件处理程序需要2秒。3秒后可用。

But suppose you install a splash screen using WindowsFormsApplicationBase. You might think MinimumSplashScreenDisplayTime of 3 seconds is sensible and won't slow your app. But, try it, your app will now take 5 seconds to load.

但是,假设您使用WindowsFormsApplicationBase安装了一个启动屏幕。你可能会认为3秒的小屏幕显示时间是合理的,不会让你的应用慢下来。


class App : WindowsFormsApplicationBase
{
    protected override void OnCreateSplashScreen()
    {
        this.MinimumSplashScreenDisplayTime = 3000; // milliseconds
        this.SplashScreen = new Splash();
    }

    protected override void OnCreateMainForm()
    {
        this.MainForm = new Form1();
    }
}

and

public Form1()
{
    InitializeComponent();
    Shown += Form1_Shown;
    Thread.Sleep(TimeSpan.FromSeconds(1));
}

void Form1_Shown(object sender, EventArgs e)
{
    Thread.Sleep(TimeSpan.FromSeconds(2));
    Program.watch.Stop();
    this.textBox1.Text = Program.watch.ElapsedMilliseconds.ToString();
}

Conclusion: don't use WindowsFormsApplicationBase if your app has a handler on the Slown event. You can write better code that runs the splash in parallel to both the constructor and the Shown event.

结论:如果你的应用程序在Slown事件上有一个处理器,不要使用WindowsFormsApplicationBase。您可以编写更好的代码,将splash与构造函数和显示事件并行运行。

#12


0  

Actually mutlithreading here is not necessary.

实际上,这里没有必要进行突变。

Let your business logic generate an event whenever you want to update splash screen.

让您的业务逻辑在需要更新启动屏幕时生成事件。

Then let your form update the splash screen accordingly in the method hooked to eventhandler.

然后让您的表单在连接到eventhandler的方法中相应地更新启动屏幕。

To differentiate updates you can either fire different events or provide data in a class inherited from EventArgs.

为了区分更新,您可以触发不同的事件,也可以在从EventArgs继承的类中提供数据。

This way you can have nice changing splash screen without any multithreading headache.

通过这种方式,您可以有一个漂亮的改变飞溅屏幕,没有任何多线程头痛。

Actually with this you can even support, for example, gif image on a splash form. In order for it to work, call Application.DoEvents() in your handler:

实际上,你甚至可以支持,例如,在一个飞溅窗体上的gif图像。为了让它工作,在处理程序中调用Application.DoEvents():

private void SomethingChanged(object sender, MyEventArgs e)
{
    formSplash.Update(e);
    Application.DoEvents(); //this will update any animation
}