タスク・モーダルなダイアログボックスでは、通常「OK」ボタンが押されたときに、ダイアログボックスに入力された内容を読み取り、親に連絡するのが普通です。これに対し、モードレス・ダイアログボックスでは、オーナーとダイアログボックス間で、もっと密接に通信しあう必要があります。
この章では、オーナーのフォームとモードレス・ダイアログボックスに同じチェックボックスを持ち、どちらかのチェック内容が変更になったら、他方にそれが反映されるようにします。
また、前章では、無制限にモードレス・ダイアログボックスを出現させることができましたが、ここでは、1つに制限します。
このようなプログラムで、威力を発揮するのがダイアログボックス側のOwnerプロパティです。また、オーナーフォーム側ではOwnedFormsプロパティです。
Ownerプロパティはすでに、前章で使っていますね。
OwnedFormsプロパティはSystem.Windows.Forms名前空間で定義されており、Formクラスのプロパティです。
public Form[] OwnedForms { get; }
プロパティ値は、フォームが所有しているすべてのフォームを表すForm配列を取得します。あるフォームを所有する場合は、AddOwnedFormメソッドを使います。RemoveOwnedFormメソッドで所有を放棄します(この章のプログラムでは使用していません)。OwnedFormsプロパティは、配列なのでLengthを使って所有しているフォームの数を知ることができます。
では、サンプルのプログラムを見てみましょう。
// modeless02.cs
using System;
using System.Drawing;
using System.Windows.Forms;
class modeless02 : Form
{
public static void Main()
{
modeless02 form = new modeless02();
Application.Run(form);
}
public modeless02()
{
Text = "猫でもわかるC#プログラミング";
BackColor = SystemColors.Window;
MainMenu mm = new MainMenu();
MenuItem miFile = new MenuItem("ファイル(&F)");
mm.MenuItems.Add(miFile);
MenuItem miExit = new MenuItem("終了(&X)");
miExit.Click += new EventHandler(miExit_Click);
miFile.MenuItems.Add(miExit);
MenuItem miOption = new MenuItem("オプション(&O)");
mm.MenuItems.Add(miOption);
MenuItem miDlg = new MenuItem("ダイアログを出す(&D)");
miDlg.Click += new EventHandler(miDlg_Click);
miOption.MenuItems.Add(miDlg);
Menu = mm;
CheckBox cb0 = new CheckBox();
cb0.Text = "チェックボックス0";
cb0.Location = new Point(10, 10);
cb0.Parent = this;
cb0.CheckedChanged += new EventHandler(cb_CheckedChanged);
CheckBox cb1 = new CheckBox();
cb1.Text = "チェックボックス1";
cb1.Location = new Point(10, 15 + cb0.Height);
cb1.Parent = this;
cb1.CheckedChanged += new EventHandler(cb_CheckedChanged);
}
void miExit_Click(object sender, EventArgs e)
{
Close();
}
void miDlg_Click(object sender, EventArgs e)
{
if (OwnedForms.Length >= 1)
{
MessageBox.Show("すでにダイアログは出ています",
"猫C#", MessageBoxButtons.OK,
MessageBoxIcon.Information);
return;
}
MyModeless modeless = new MyModeless();
modeless.Owner = this;
modeless.Show();
}
void cb_CheckedChanged(object sender, EventArgs e)
{
if (OwnedForms.Length == 0)
return;
for (int i = 0; i < 2; i++)
{
if (sender == Controls[i])
{
((CheckBox)OwnedForms[0].Controls[i]).Checked =
((CheckBox)sender).Checked;
break;
}
}
}
}
Mainメソッドでは、いつものようにメイン・フォームを作成しています。コンストラクタでは、メニューとチェックボックスを作成しています。
チェックボックスのチェックが変更されたら、cb_CheckedChangedメソッドが呼ばれるようにしておきます。
メニューの「終了」が選択されると、miExit_Clickメソッドが呼ばれ、フォームをCloseしてプログラムを終了します。
メニューの「ダイアログを出す」が選択されると、miDlg_Clickメソッドが呼ばれます。
もし、OwnedForms.Lengthが1以上であれば(実際には0か1のいずれかで2以上にはならないが・・・)、すでにダイアログが出ているので、その旨をメッセージボックスで表示しreturnします。
ダイアログが、出ていないときは、モードレス・ダイアログボックスを作成します。
チェックボックスのチェック状態が、変更になったときはcb_CheckedChangedメソッドが呼ばれます。
OwnedForms.Lengthが0の場合は、ダイアログボックスが出ていないので、何もせずにreturnします。
ダイアログボックスが出ている場合は、まず自分のどのチェックボックスに変化があったかを調べます。
引数のsenderとControls[i]が一致しているかどうかを調べます。一致していれば、そのときのiを利用して、モードレス・ダイアログボックス側のチェックボックスのチェック状態を一致させます。
((CheckBox)OwnedForms[0].Controls[i]).Checked = ((CheckBox)sender).Checked;ダイアログボックスは、1つしかないのでOwnedForms[0]です。このフォームのControls[i]が対応したチェックボックスです。
このチェックボックスのCheckedプロパティの値を、フォームのチェックボックス(sender)のCheckedプロパティの値に一致させればよいですね。
では、モードレス・ダイアログボックス側を見てみましょう。
class MyModeless : Form
{
public MyModeless()
{
Text = "モードレス・ダイアログボックス";
ControlBox = false;
MinimizeBox = false;
MaximizeBox = false;
ShowInTaskbar = false;
FormBorderStyle = FormBorderStyle.FixedDialog;
CheckBox cb0 = new CheckBox();
cb0.Text = "チェックボックス0";
cb0.Location = new Point(10, 10);
cb0.Parent = this;
cb0.CheckedChanged += new EventHandler(cb_CheckedChanged);
CheckBox cb1 = new CheckBox();
cb1.Text = "チェックボックス1";
cb1.Location = new Point(10, 15 + cb0.Height);
cb1.Parent = this;
cb1.CheckedChanged += new EventHandler(cb_CheckedChanged);
Button btnOK = new Button();
btnOK.Parent = this;
btnOK.Text = "OK";
btnOK.Location = new Point((ClientSize.Width - btnOK.Width) / 2,
ClientSize.Height - btnOK.Height - 5);
btnOK.Click += new EventHandler(btnOK_Click);
}
void btnOK_Click(object sender, EventArgs e)
{
Close();
}
void cb_CheckedChanged(object sender, EventArgs e)
{
for (int i = 0; i < 2; i++)
{
if (sender == Controls[i])
{
((CheckBox)Owner.Controls[i]).Checked =
((CheckBox)sender).Checked;
break;
}
}
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
for (int i = 0; i < 2; i++)
{
((CheckBox)Controls[i]).Checked =
((CheckBox)Owner.Controls[i]).Checked;
}
}
}
コンストラクタで、まずこのフォームのプロパティを設定しています。ControlBox, MinimizeBox, MaximizeBox, ShowInTaskbarプロパティをfalseにして、 ダイアログボックスとしての体裁を整えます。
また、境界をFormBorderStyle.FixedDialogにして、サイズ変更ができないようにしています。
次に、チェックボックスを作り、OKボタンを作っています。
チェックボックスのチェック状態が変更されたら、cb_CheckedChangedメソッドが呼ばれるようにしておきます。
最後にOKボタンを作ります。OKボタンを押したとき、btnOK_Clickメソッドが呼ばれるようにしておきます。
btnOK_Clickメソッドでは、単にCloseメソッドを呼んでダイアログを閉じます。
cb_CheckedChangedメソッドが呼ばれたら、senderとControls[i]とが等しくなるiを調べます。この時Owner.Controls[i]が対応する、オーナーのチェックボックスです。あとはsender.Checkedの値にあわせるだけです。
さて、ダイアログが出ていない状態で、オーナーのチェックボックスのチェック状態が変更されることも考えられます。したがって、ダイアログボックスが出現した時点で、オーナーのチェックボックスと、ダイアログボックスのチェックボックスのチェック状態を同じにしておくことが必要です。
これは、コンストラクタで行うより、Loadイベントを処理する方がよいでしょう。Loadイベントは、フォームが始めて表示される直前に発生します。
public event EventHandler LoadOnLoadメソッドを使用すると、派生クラスでイベントを処理できます。
protected virtual void OnLoad ( EventArgs e )ここでは、OnLoadメソッドを利用してLoadイベントの処理をしています。
では、実行結果を見てみましょう。
オーナー側、ダイアログ側どちらのチェックボックスのチェックを変更しても、他方に反映されます。
Update 27/Nov/2006 By Y.Kumei