当前位置:  开发笔记 > 编程语言 > 正文

C#中的多线程闪屏?

如何解决《C#中的多线程闪屏?》经验,为你挑选了7个好方法。

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

我一直在阅读很多关于线程的内容,但是我对它应该控制的地方感到迷茫(main()方法?).我也想知道它是如何main()工作的,这是应该创建的线程吗?现在,如果带有系统托盘控件的表单是"生命"表单,那么它应该来自那里吗?在形式完成之前不会加载吗?

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



1> aku..:

诀窍是创建单独的线程负责启动屏幕显示.
运行时,app .net会创建主线程并加载指定的(主)表单.为了隐藏辛勤工作,您可以隐藏主要表单,直到加载完成.

假设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();
}


@aku:在应用程序中使用Application.DoEvents()是个好主意吗?我读过很多关于不在互联网上使用这个功能的内容,如http://blogs.msdn.com/jfoscoding/archive/2005/08/06/448560.aspx
在Application.DoEvents()上旋转循环是对CPU时间的严重浪费。不应使用此答案,但很可惜,它的投票率很高。[此答案](/sf/ask/17360801/)是一个更好的选择(即使用`Application.Run()处理消息泵送)。

2> Guy Starbuck..:

好吧,对于我过去部署的ClickOnce应用程序,我们使用Microsoft.VisualBasic命名空间来处理启动画面线程.您可以在.NET 2.0中引用和使用C#中的Microsoft.VisualBasic程序集,它提供了许多不错的服务.

    主表单是否继承自Microsoft.VisualBasic.WindowsFormsApplicationBase

    像这样覆盖"OnCreateSplashScreen"方法:

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

非常简单,它会在加载过程中显示您的SplashForm(您需要创建),然后在主窗体完成加载后自动关闭它.

这真的让事情变得简单,VisualBasic.WindowsFormsApplicationBase当然经过了微软的测试,并且有很多功能可以让你在Winforms中的生活变得更轻松,即使在100%C#的应用程序中也是如此.

在一天结束时,无论如何都是IL和字节码,为什么不使用呢?


这是最优雅的解决方案,因为它使用了Visual Basic库非常好地抽象的内置功能.我不知道如何创建一个VB ApplicationBase程序,所以需要更多的研究......我将添加更多信息以保证完整性.感谢所有的好评!

3> Adam Nofsing..:

在查看Google和SO之后的解决方案时,这是我最喜欢的:http: //bytes.com/topic/c-sharp/answers/277446-winform-startup-splash-screen

FormSplash.cs:

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

    public FormSplash() {
        InitializeComponent();
    }

    /// 
    /// Show the Splash Screen (Loading...)
    /// 
    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);
    }

    /// 
    /// Close the splash (Loading...) screen
    /// 
    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中:

static class Program
{
    /// 
    /// The main entry point for the application.
    /// 
    [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

    ...

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

我曾与问题Microsoft.VisualBasic的背景上XP任职的发现,但在Windows 2003终端服务器,主要应用形式会出现(闪屏后),并在任务栏会闪烁-解决方案.并且在窗口中显示前景/焦点代码是另一种可以使用Google/SO的蠕虫.



4> Jay..:

这是一个老问题,但在尝试为WPF找到可能包含动画的线程闪屏解决方案时,我一直遇到它.

这是我最终拼凑在一起的内容:

App.xaml中:



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



5> DelftRed..:

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

引用MSDN:

如果这是活动应用程序,则激活表单会将其显示在前面,如果这不是活动应用程序,则会闪烁窗口标题.该表单必须可见才能使此方法产生任何效果.

如果您没有激活主表单,它可能会显示任何其他打开的窗口后面,使它看起来有点傻.



6> Peter Meyer..:

我认为使用像aku或Guy这样的方法是要走的路,但是从具体的例子中可以看出几件事:

    基本前提是尽快在单独的线程上显示您的启动.这就是我倾向的方式,类似于aku的说明,因为这是我最熟悉的方式.我不知道Guy提到的VB函数.而且,即使认为它是一个VB库,他也是对的 - 最终它都是IL.所以,即使它感觉很脏,也不是那么糟糕!:)我认为你要确保VB提供一个单独的线程,或者你自己创建一个 - 绝对是研究.

    假设您创建另一个线程来显示此启动,您将需要小心跨线程UI更新.我提出这个问题是因为你提到了更新进度.基本上,为了安全起见,您需要使用委托在splash表单上调用更新函数(您创建的).您将该委托传递到初始屏幕的表单对象上的Invoke函数.实际上,如果您直接调用splash表单来更新其上的progress/UI元素,只要您在.Net 2.0 CLR上运行,就会遇到异常.根据经验,表单上的任何UI元素都必须由创建它的线程更新 - 这就是Form.Invoke所保证的.

最后,我可能会选择在代码的main方法中创建启动(如果不使用VB重载).对我来说,这比主表单执行对象的创建并且与它紧密绑定更好.如果采用这种方法,我建议创建一个简单的界面,即启动画面实现 - 类似于IStartupProgressListener - 它通过成员函数接收启动进度更新.这将允许您根据需要轻松地交换/输出任一类,并很好地解耦代码.如果在启动完成时通知,则启动表单还可以知道何时关闭自身.



7> McKenzieG1..:

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

 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

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

    使用已执行main()作为GUI线程的线程启动Windows'消息泵'.

    将"主表单"指定为应用程序的"关闭表单".如果用户关闭该表单,则Application.Run()终止并控制返回到main(),您可以在其中执行任何所需的关闭.

没有必要产生一个线程来处理启动窗口,事实上这是一个坏主意,因为那时你必须使用线程安全技术来更新main()中的启动内容.

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

推荐阅读
围脖上的博博_771
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有