AJAX风格的异步进度对话框,适用于WinForms






4.87/5 (27投票s)
用于向任何窗体添加丰富异步进度动画的基类。
引言
我的公司开发了许多富客户端应用程序,我们一直希望有一个好的“流畅”方式来向用户指示后台正在进行的活动。我们很少知道一个操作需要多长时间(Web 服务、远程调用等),所以我们过去总是只在主应用程序窗口的右上角放一个小的“理发店杆”类型的动画。但这从来不是一个完美的解决方案,因为我们仍然需要做所有令人讨厌的 Control
/Form
“锁定”,以确保它们不能排队另一个动作。而且,在许多 WinForms 控件上设置 Enabled = False
看起来很丑陋且不一致,特别是如果 Form
包含各种不同的控件。
近年来,网络上的 AJAX 实际上开创了一些有趣的 GUI 概念。我一直很喜欢当网站弹出一个中心窗口,使背景稍微变暗,然后要求您输入一些内容。然后您输入该输入并按下 OK 按钮,然后您会看到一个很棒的小理发店杆动画,指示它已经返回到服务器并正在等待下一步的回复(如果有)。
这基本上就是这个项目的目的。将这种“酷炫”的 AJAX 风格的异步指示行为带到 WinForms。
背景
该项目由几个基本概念组成
- 以可靠且一致的方式捕获/快照
Form
的当前外观。
注意:Control.DrawToBitmap()
未被使用,因为它对某些控件(如RichTextBox
)具有奇怪的行为。 - 以类似于大多数 AJAX 网站的方式操作捕获的位图,使其模糊或灰度化。
Form
中心的理发店杆类型动画。在这种情况下,我使用了 Martin Gagne 出色的“Loading Circle”控件 - 所以感谢 Martin :~)- 从一开始,我就确保我开发的东西可以在普通的
Form
和 MDI 子窗口上运行。这对我来说至关重要,因为我们的许多产品都使用 MDI 用户界面。其次,这排除了使用 Win2000 及更高版本的合成分层半透明窗口的可能性(我最初尝试过)。
Using the Code
要使用基类,只需修改您的 Form
以从我的 AsyncBaseDialog
派生,而不是默认的 System.Windows.Forms.Form
。然后您只需调用 RunAsyncOperation()
并将您的委托方法作为其参数传入。此方法处理在后台线程上调度您的工作的所有繁琐工作。
或者,如果您希望更好地控制事物,则可以使用 BeginAsyncIndication()
和 EndAsyncIndication()
。
在内部,Begin/EndAsyncIndication()
使用引用计数,以便您可以以堆栈的方式多次调用它们,并且仍然获得预期的行为。
public partial class MyForm : AsyncBaseDialog {
public ModalDlg() {
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e) {
AsyncProcessDelegate d = delegate() {
//
// Do your long-duration work here
// and remove the placeholder Sleep() below
//
System.Threading.Thread.Sleep(3000);
};
RunAsyncOperation(d);
}
private void button2_Click(object sender, EventArgs e) {
//
// Alternatively if you don't want to use the RunAsyncOperation() wrapper...
// You can use BeginAsyncIndication() and EndAsyncIndication() explicitly.
//
BeginAsyncIndication();
}
}//class
工作原理
通过打开其 DC(设备上下文)然后将其内容复制到 Bitmap
来获取 Form
的快照。然后使用 Martin Gagne 的方法操作此位图,使其灰度化。
Form
的快照可能是最难的部分,因为我已经多年没有使用 Win32 API 了!顺便说一句,如果您在使用 Control.DrawToBitmap()
时遇到问题,我建议您看看这个。就在这里
//
// Get DC of the form...
IntPtr srcDc = GetDC(this.Handle);
//
// Create bitmap to store image of form...
Bitmap bmp = new Bitmap(this.ClientRectangle.Width, this.ClientRectangle.Height);
//
// Create a GDI+ context from the created bitmap...
using (Graphics g = Graphics.FromImage(bmp)) {
//
// Copy image of form into bitmap...
IntPtr bmpDc = g.GetHdc();
BitBlt(bmpDc, 0, 0, bmp.Width, bmp.Height, srcDc, 0, 0, 0x00CC0020 /* SRCCOPY */);
//
// Release resources...
ReleaseDC(this.Handle, srcDc);
g.ReleaseHdc(bmpDc);
//
// Blur/grayscale it...
Grayscale(bmp);
//
// Apply translucent overlay... fillBrush has an alpha-channel.
g.FillRectangle(fillBrush, 0, 0, bmp.Width, bmp.Height);
}//using
我在用户调整大小、最大化、最小化、恢复或双击 Form
的标题栏时遇到了一些问题,同时异步指示处于活动状态。基本上,这些是重绘问题 - 尤其是在 Vista Aero Glass 之前的机器上。在权衡了可能的解决方案后,我决定用户希望在异步指示处于活动状态时调整/最小化/最大化 Form
的可能性很小,并且对他们的烦恼可能很小。因此,我编写了一些 WndProc
过滤器,如下所示
protected override void WndProc(ref Message m) {
if (IsAsyncBusy) {
if (m.Msg == 0x112 /* WM_SYSCOMMAND */) {
int w = m.WParam.ToInt32();
if (w == 0xf120 /* SC_RESTORE */ || w == 0xf030
/* SC_MAXIMIZE */ || w == 0xf020
/* SC_MINIMIZE */)
return; // short circuit
} else if (m.Msg == 0xa3 /* WM_NCLBUTTONDBLCLK */)
return; // short circuit
}
base.WndProc(ref m);
}
谢谢
感谢您的阅读,希望您喜欢这个控件。
如果您对此控件进行任何修改/错误修复/增强,请在评论部分发布您的源代码片段和/或想法。
历史
- 2008 年 3 月 2 日 - 初始发布