Joh*_*ino 60 .net c# multithreading splash-screen winforms
我想在应用程序加载时显示启动画面.我有一个带有系统托盘控件的表单.我希望在加载此表单时显示启动画面,这需要一些时间,因为它正在访问Web服务API以填充一些下拉菜单.我还想在加载之前对依赖项进行一些基本测试(也就是说,Web服务可用,配置文件是可读的).随着启动的每个阶段的进行,我想要通过进度更新启动屏幕.
我一直在阅读很多关于线程的内容,但是我对它应该控制的地方感到迷茫(main()方法?).我也想知道它是如何main()工作的,这是应该创建的线程吗?现在,如果带有系统托盘控件的表单是"生命"表单,那么它应该来自那里吗?在形式完成之前不会加载吗?
我不是在寻找代码讲义,更多的是算法/方法,所以我可以一劳永逸地解决这个问题:)
aku*_*aku 46
诀窍是创建单独的线程负责启动屏幕显示.
运行时,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();
}
Run Code Online (Sandbox Code Playgroud)
Guy*_*uck 46
好吧,对于我过去部署的ClickOnce应用程序,我们使用Microsoft.VisualBasic命名空间来处理启动画面线程.您可以在.NET 2.0中引用和使用C#中的Microsoft.VisualBasic程序集,它提供了许多不错的服务.
像这样覆盖"OnCreateSplashScreen"方法:
protected override void OnCreateSplashScreen()
{
this.SplashScreen = new SplashForm();
this.SplashScreen.TopMost = true;
}
Run Code Online (Sandbox Code Playgroud)非常简单,它会在加载过程中显示您的SplashForm(您需要创建),然后在主窗体完成加载后自动关闭它.
这真的让事情变得简单,VisualBasic.WindowsFormsApplicationBase当然经过了微软的测试,并且有很多功能可以让你在Winforms中的生活变得更轻松,即使在100%C#的应用程序中也是如此.
在一天结束时,无论如何都是IL和字节码,为什么不使用呢?
Ada*_*ger 13
在查看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();
}
/// <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();
}
}
Run Code Online (Sandbox Code Playgroud)
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());
}
}
Run Code Online (Sandbox Code Playgroud)
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();
}
Run Code Online (Sandbox Code Playgroud)
我曾与问题Microsoft.VisualBasic的背景上XP任职的发现,但在Windows 2003终端服务器,主要应用形式会出现(闪屏后),并在任务栏会闪烁-解决方案.并且在窗口中显示前景/焦点代码是另一种可以使用Google/SO的蠕虫.
这是一个老问题,但在尝试为WPF找到可能包含动画的线程闪屏解决方案时,我一直遇到它.
这是我最终拼凑在一起的内容:
App.xaml中:
<Application Startup="ApplicationStart" …
Run Code Online (Sandbox Code Playgroud)
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();
}
Run Code Online (Sandbox Code Playgroud)
我建议在aku提供的答案中Activate();的最后一个之后直接打电话Show();.
引用MSDN:
如果这是活动应用程序,则激活表单会将其显示在前面,如果这不是活动应用程序,则会闪烁窗口标题.该表单必须可见才能使此方法产生任何效果.
如果您没有激活主表单,它可能会显示在任何其他打开的窗口后面,使它看起来有点傻.
我认为使用像aku或Guy这样的方法是要走的路,但是从具体的例子中可以看出几件事:
基本前提是尽快在单独的线程上显示您的启动.这就是我倾向的方式,类似于aku的说明,因为这是我最熟悉的方式.我不知道Guy提到的VB函数.而且,即使认为它是一个VB库,他也是对的 - 最终它都是IL.所以,即使它感觉很脏,也不是那么糟糕!:)我认为你要确保VB提供一个单独的线程,或者你自己创建一个 - 绝对是研究.
假设您创建另一个线程来显示此启动,您将需要小心跨线程UI更新.我提出这个问题是因为你提到了更新进度.基本上,为了安全起见,您需要使用委托在splash表单上调用更新函数(您创建的).您将该委托传递到初始屏幕的表单对象上的Invoke函数.实际上,如果您直接调用splash表单来更新其上的progress/UI元素,只要您在.Net 2.0 CLR上运行,就会遇到异常.根据经验,表单上的任何UI元素都必须由创建它的线程更新 - 这就是Form.Invoke所保证的.
最后,我可能会选择在代码的main方法中创建启动(如果不使用VB重载).对我来说,这比主表单执行对象的创建并且与它紧密绑定更好.如果采用这种方法,我建议创建一个简单的界面,即启动画面实现 - 类似于IStartupProgressListener - 它通过成员函数接收启动进度更新.这将允许您根据需要轻松地交换/输出任一类,并很好地解耦代码.如果在启动完成时通知,则启动表单还可以知道何时关闭自身.
一种简单的方法是使用像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
Run Code Online (Sandbox Code Playgroud)
当.NET CLR启动您的应用程序时,它会创建一个"主"线程并开始在该线程上执行main().Application.Run(myMainForm)最后做了两件事:
没有必要产生一个线程来处理启动窗口,事实上这是一个坏主意,因为那时你必须使用线程安全技术来更新main()中的启动内容.
如果您需要其他线程在您的应用程序中执行后台操作,您可以从main()生成它们.只需记住将Thread.IsBackground设置为True,这样它们就会在主/ GUI线程终止时死掉.否则,您将不得不安排自己终止所有其他线程,或者当主线程终止时,它们将使您的应用程序保持活动状态(但没有GUI).
| 归档时间: |
|
| 查看次数: |
41175 次 |
| 最近记录: |