コントロールを描画した画像でフタをする方法
バックグラウンドで時間のかかる処理を行った場合、前述の方法だと、フォームの移動時にPaintイベントが発生しないため、コントロールがフォームから消え去る現象が生じます。
アニメ「キャッツ・アイ」では、監視カメラの真ん前に無人状態の風景写真を貼り付け、監視員がモニターに映ったその写真を実際の映像だと勘違いしている間に、カメラの前を横切るというシーンがありました。ここではそんな偽の画像を用いることで、コントロール処理を隠す方法を解説します。
.NET Framework 2.0にはフォームのスクリーンショットを取得するためのメソッドControl.DrawToBitmapが用意されていますが、このメソッドはWebBrowserコントロールなどのActiveXコントロールを反映されないため、ここではWindows XPから追加されたAPI「PrintWindow」を用い、この関数で取得した画像をフォームの最前面に貼り付けています。
[DllImport("user32")]
private static extern bool PrintWindow(
IntPtr hwnd, // イメージを取得する対象ウィンドウ(HWND)
IntPtr hdcBlt, // イメージの保存先(HDC)
uint nFlags // オプションフラッグ:
// PW_CLIENTONLY(=0x1)でクライアント領域のみ取得
);
private Bitmap screen = null;
private PictureBox pctbox = null;
private void button1_Click(object sender, EventArgs e)
{
// 有効状態であるクライアント領域のイメージをキャプチャ
// ※Windows XPではPrintWindowの第3引数に
// PW_CLIENTONLYを指定しても正しくキャプチャされないため、
// 引数0x0でウィンドウ全体を取得した後で、
// クライアント領域部分をコピーするようにしている
Bitmap winimg = new Bitmap(Width, Height);
Graphics g = Graphics.FromImage(winimg);
IntPtr hDC = g.GetHdc();
PrintWindow(this.Handle, hDC, 0x0);
g.ReleaseHdc(hDC);
g.Dispose();
// クライアント領域の相対位置を取得
Rectangle r1 = this.Bounds; r1.X = r1.Y = 0;
Rectangle r2 = this.ClientRectangle;
Rectangle r3 = new Rectangle(
(r1.Width - r2.Width) / 2,
(r1.Height - r2.Height - SystemInformation.CaptionHeight) / 2 + SystemInformation.CaptionHeight,
r2.Width, r2.Height);
// クライアント部分をコピーする
screen = new Bitmap(ClientSize.Width, ClientSize.Height);
g = Graphics.FromImage(screen);
g.DrawImage(winimg, ClientRectangle, r3, GraphicsUnit.Pixel);
g.Dispose();
winimg.Dispose();
// 「フタ」を作成
pctbox = new PictureBox();
pctbox.Paint += new PaintEventHandler(pctbox_Paint);
pctbox.Bounds = this.ClientRectangle;
pctbox.BackgroundImage = screen;
this.Controls.Add(pctbox);
pctbox.BringToFront();
panel1.Enabled = false;
timer1.Start();
// バックグラウンド処理の開始
backgroundWorker1.RunWorkerAsync();
}
private void backgroundWorker1_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e)
{
// 編集可能な状態に復帰する
panel1.Enabled = true;
// 偽装フォームを廃棄して本来のフォームを表示
if (pctbox != null) pctbox.Dispose();
}
PictureBox.OnPaintイベントに描画処理を追加すれば、Web 2.0でよく見られるようなウェイト処理を再現することも可能です(サンプルファイルを参照)。

最後に
「フタ」テクニックは、フォームの中央に半透明で「お待ち下さい」といったメッセージを簡単に描画できるので、アイデア次第で多彩なウェイト処理を実装できるでしょう。更新中にコントロールにアクセスされては困るようなタイプのソフトウェア――特にデータベースソフトウェアにおすすめします。
