65.9K
CodeProject 正在变化。 阅读更多。
Home

一个简单的等待窗口

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.50/5 (20投票s)

2010年3月5日

CPOL

4分钟阅读

viewsIcon

186513

downloadIcon

15468

在长时间运行的过程中显示一个等待窗口。

引言

在我使用FoxPro的年代,我们运行任何耗时操作时都会弹出一个等待窗口。到了.NET,没有内置的等待窗口,所以你必须自己实现一个。随着我们调用可能快速也可能缓慢的Web服务,这个问题变得越来越突出。在此期间,用户需要知道应用程序没有崩溃。他们也应该无法点击应用程序的其他任何部分,直到进程完成,这与旧的FoxPro等待窗口方式相同。

waitwindow.png

背景

要实现一个好的等待窗口,需要覆盖几个细节。

在我们的处理过程中,我们希望在等待窗口上提供一个动画,以确保用户相信应用程序没有崩溃。在本篇文章中,我使用了ProgressBar,但你也可以同样使用动画GIF,或者你自己设计的自定义动画方案。其副作用是,工作方法必须在辅助线程上执行,否则它将阻塞GUI更新动画。

另外,我们不能为此使用BackgroundWorker,因为我们希望执行是内联的。例如,我们想调用等待窗口并指定要执行的方法,然后期望在我们碰到下一行代码时它已经完成。因此,我们将不得不自己编写线程代码。

Using the Code

我将示例代码构建在一个单独的程序集中,这样你就可以直接引用该程序集。不过,我预计大多数人会将类复制到自己的项目结构中,这也可以。

在任何人提问之前,示例代码包含C#和VB.NET的项目。虽然我针对3.5框架编译了它,但针对任何.NET Framework版本构建应该都可以。不过,如果你想使用旧的解决方案格式,你需要手动将代码复制到Visual Studio 2005项目中。

首先,你需要编写要执行的方法。请记住,它将在单独的线程上运行,所以传入所有内容并返回结果,或者确保你进行的任何调用都保证是线程安全的!

为了使事情更容易,方法的消息参数包含一个可以传入的参数集合,以及对WaitWindow对象的引用,以便在处理过程中可以更新显示的消息。下面的代码显示了你可能想要做的绝大多数事情。更多示例请参阅演示项目。

private void WorkerMethod(object sender, Jacksonsoft.WaitWindowEventArgs e){
	// Do something
	for (int progress = 1; progress <= 100; progress++){
		System.Threading.Thread.Sleep(20);
		
		// Update the wait window message
		e.Window.Message = string.Format
		("Please wait ... {0}%", progress.ToString().PadLeft(3));
	}
	
	// Use the arguments sent in
	if (e.Arguments.Count> 0){
		// Set the result to return
		e.Result = e.Arguments[0].ToString();
	} else {
		// Set the result to return
		e.Result = "Hello World";
	}
}

接下来,你需要调用WaitWindow并传入你创建的方法。下面的示例仅使用默认的“请稍候...”消息,但你也可以传入要显示的消息以及工作方法的任何参数。同样,演示项目中还有更多示例。

object result = WaitWindow.Show(this.WorkerMethod);

WaitWindow对象暴露了两个方法,可以从工作方法内部调用。这两个方法是Cancel()和属性Message。前者立即取消执行,后者分别更新WaitWindow中显示的消息。

关注点

在第一个版本中,我做了一些巧妙的事情,让一个非模态窗口显示为模态,但后来发现我实际上根本不需要这样做。

如果你确实出于其他原因想这样做,你必须捕获鼠标,然后捕获所有窗口消息,并确保只有你想要的消息才能传递给应用程序的其他部分。

我们将Form传递给AddMessageFilter方法,以便窗体首先接收所有窗口消息。

System.Windows.Forms.Application.AddMessageFilter(this._GUI);

在窗口的代码内部,我们现在可以实现IMessageFilter接口,从而得到一个PreFilterMessage方法。剩下的就是为鼠标和键盘事件返回true,而不是默认的false,这样窗口就会表现得好像是非模态的,而实际上它是模态的。

可惜我放弃了那段相当不错但毫无意义的代码,这就是开发,一个不断学习的过程。 

历史

  • 第一个版本 - 2010年3月
  • 版本2 - 更新为更简单的线程模型(非常感谢John Brett的贡献)
  • 版本3 - 更新以支持取消和更好的异常处理
© . All rights reserved.