背景
掲示板にて、Windows アプリケーションにおいて、親子関係にある複数のフォームを表示したり、親子のフォーム間でデータを受け渡しするためにはどうすればよいか、という質問を散見するため、サンプルを提供します。
ここで提示する方法は、解決方法の一案であり、絶対的な方法ではありません。また、私自身、今回提供するサンプル コードに「これはまずい」と思っていることがあります。しかし、その場所については触れません。どこが、なぜまずいのか、考えてみてください。
前提条件
- .NET Framework 2.0
- C#
- Windows Application
キーワード
- 複数のフォーム 複数のウインドウ
- 親子のウインドウ
- データを渡す
提供するサンプル
- 子フォームが、1つだけ表示されるパターン
- 複数の子フォームが表示できるパターン1(すべて制御する)
コンボボックスにインスタンスを表示し、制御します。また、子フォームのテキストボックスに入力した値が、親フォームに表示されます。
- 複数の子フォームが表示できるパターン2(制御しない)
- 上記3つのパターンで、親フォームのテキストボックスの値を、子フォームで表示します
コードと説明
まず、子フォームをひとつだけ表示するパターンです。
このパターンを実現するためには、「1アプリケーションで、ひとつだけ new する」ことを実現できれば、目的を達成できます。しかし、ここで止まってはいけません。そのたったひとつの new したフォームが、閉じてしまったらどうしましょう?つまり、「閉じた」ことを知る必要があります。
インスタンスにスコープを持つ変数に、たったひとつのインスタンスを保存します。その変数が null でなければ new し、null であればフォーカスを移します。
また、インスタンスを作成したときに、FormClosed イベントを受けるようにします。これによって、フォームが閉じたことを知り、変数を null にして、次の回にインスタンスを作成できるようにします。
#region 一つだけ表示し、消去する
/// <summary>
/// 一つだけ表示するためのインスタンスを憶える
/// </summary>
private FrmMonoInstance oneFormeInstance = null;
/// <summary>
/// 一つだけ表示
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button1_Click(object sender, EventArgs e) {
if (this.oneFormeInstance == null) {
// インスタンスがないなら作成する
this.oneFormeInstance = new FrmMonoInstance();
// 向こうで閉じられた時用に、FormClosed イベントを受け取る
this.oneFormeInstance.FormClosed += new FormClosedEventHandler(FrmMonoInstance_FormClosed);
// 表示する
this.oneFormeInstance.Show(this.textBox1.Text);
} else {
this.oneFormeInstance.Focus();
}
}
/// <summary>
/// フォーム側で閉じられたときは、こちらで記録しているインスタンス情報を削除する
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void FrmMonoInstance_FormClosed(object sender, FormClosedEventArgs e) {
this.oneFormeInstance = null;
}
/// <summary>
/// 一つだけ表示したものを消去
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button3_Click(object sender, EventArgs e) {
if (this.oneFormeInstance != null) {
this.oneFormeInstance.Close();
}
}
#endregion
親から子へ、データを引き継ぐ方法です。
今回は、一番単純な方法をとりました。引数をひとつ受け取る Show メソッドのオーバーロードを作成して、ここで文字列を渡します。
/// <summary>
/// 表示する文字を指定して、フォームを表示する
/// </summary>
/// <param name="text"></param>
public void Show(string text) {
this.label2.Text = text;
this.Show();
}
次に、複数表示する場合です。この場合、開くことが出来るフォームの数に上限を決めるなら配列、上限を設けない(リソース上の上限まで)ならリストに、インスタンスを格納します。
同じように、FormClosed イベントを処理して、配列/リスト中から該当するインスタンスを削除します。
今回は、「1秒間に複数枚開くことは出来ないだろう」という甘い予想の元、DateTime.Now の値を識別子として利用します。
まず、データバインド用のクラスを作成ます。このクラスは、単体と、リストの2つを用意します。
class MultiInstanceData : IComparable<MultiInstanceData> {
private FrmMultiInstance instance;
/// <summary>
/// 格納している FrmMultiInstance クラスのインスタンス
/// </summary>
public FrmMultiInstance Instance {
get { return this.instance; }
}
/// <summary>
/// 格納しているインスタンスの識別子
/// </summary>
public string Identifier {
get {
if (this.instance != null) {
return this.instance.Identifier.ToString("G");
} else {
return string.Empty;
}
}
}
public MultiInstanceData() {
this.instance = null;
}
public MultiInstanceData(FrmMultiInstance form) {
this.instance = form;
}
public override bool Equals(object obj) {
MultiInstanceData dist = obj as MultiInstanceData;
if (dist == null) {
return base.Equals(obj);
} else {
return (this.CompareTo(dist) == 0 ? true : false);
}
}
public override int GetHashCode() {
return this.Identifier.GetHashCode();
}
#region IComparable<MultiInstanceData> メンバ
public int CompareTo(MultiInstanceData other) {
return this.Identifier.CompareTo(other.Identifier);
}
#endregion
}
/// <summary>
/// MultiInstanceData のリスト
/// </summary>
/// <remarks>オブジェクト データ ソースとして登録できるように、殻だけ宣言</remarks>
class MultiInstanceDataList : List<MultiInstanceData> {
public MultiInstanceDataList()
: base() {
}
}
さて、次にちょっとやっかいなのが、データバインドです。バインドしているデータソースに、勝手にデータを追加してはいけません。BindingSource.AddNew によって、追加します。そして、追加するデータソースを変更するには、AddingNew イベントにて、e.NewObject を操作します。
そのためには、追加するデータ クラスは、引数のない規定のコンストラクタが公開されてなければなりません。そして、様々なプロパティは、あとから変更可能でなければなりません。
という制約を無視するため、ここでは e.NewObject のインスタンスを直接置き換えるということをしています。
サンプル プロジェクト
このサンプルは、あくまで「この様な感じでコーディングすれば、目的のことが出来る」という指針です。コードの1行1行が何をしているのか、なぜ目的の動作になるのか、理解しようと努めない人が使用することを禁じます。
サンプル プロジェクトをダウンロードする
投稿日時 : 2006年12月31日 21:25