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

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

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.87/5 (27投票s)

2008 年 3 月 2 日

MPL

3分钟阅读

viewsIcon

243548

downloadIcon

11240

用于向任何窗体添加丰富异步进度动画的基类。

asyncdialog-src

引言

我的公司开发了许多富客户端应用程序,我们一直希望有一个好的“流畅”方式来向用户指示后台正在进行的活动。我们很少知道一个操作需要多长时间(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 日 - 初始发布
© . All rights reserved.