連載:Windowsフォーム開発入門【Visual Studio 2010対応】

Windowsフォーム間連携の基礎

初音 玲
2010/12/07
Page1 Page2

子フォームに値を渡す方法

 親フォームから子フォームに値を渡す方法はいろいろあるが、フォームがFormクラス(System.Windows.Window名前空間)を継承したクラスであることに気付けば、どのような方法で値を渡すのがよいのか見えてくる。

 あるクラスをオブジェクト化して使う場合、そのオブジェクトに対する操作は「メソッド」または「プロパティ」を使う。通常、オブジェクト化されたクラス(=子オブジェクト)の中から、オブジェクト作成元のクラス(=親オブジェクト)の内容を直接参照するようなことはしないと思う。つまり、子オブジェクトが必要とする値は、親オブジェクトから渡してあげるのが、一般的な手法だ。これと同じことが、複数のWindowsフォーム間でもいえる。

 親フォームから子フォームに値を渡すには、メソッドまたはプロパティを子フォームに追加する。親フォームから子フォームを表示する際に、そのメソッドやプロパティに渡したい値を設定すればよい。今回のサンプルでは、ShowメソッドとShowDialogメソッドを拡張することで、子フォームに値を渡してみたい。

 リスト5では、Showメソッドにパラメータ「title」を1つ追加した(厳密には、Showメソッドをオーバーロードして、別バージョンのShowメソッドを作成した)。なお、このtitleパラメータは、フォーム・タイトルを指定するための文字列値を渡すためのものだ。

Public Class Method_Form

  Private _title As String

  Public Overloads Sub Show(ByVal owner As IWin32Window, _
                            ByVal title As String)
    _title = title
    MyBase.Show(owner)
  End Sub

  Private Sub Me_Shown(ByVal sender As Object,
                       ByVal e As EventArgs) _
           Handles Me.Shown
    Me.Text = _title
  End Sub
 End Class
namespace WindowsMultiFormCs
{
  public partial class Method_Form : Form
  {
    private String _title;

    public Method_Form()
    {
      InitializeComponent();
    }

    public void Show(IWin32Window owner, String title)
    {
      _title = title;
      base.Show(owner);
    }

    private void Me_Load(object sender, EventArgs e)
    {
      this.Text = _title;
    }
  }
}
リスト5 Showメソッドにパラメータを追加したコード例(上:VB、下:C#)

 親フォームから子フォーム(=リスト5の例では、Method_Formクラスのオブジェクト)を生成して、そのShowメソッドのパラメータにタイトル(=文字列値)を指定して、拡張(=オーバーロード)したShowメソッドを呼び出す。これにより、リスト5のShowメソッドが呼び出される。

 そのShowメソッド内では、まず、パラメータに渡された値をフォーム共通の変数(この例では「_title」)に設定している。

 次に、Mybase.Showメソッド(C#の場合はbase.Showメソッド)を呼び出している。これによって、本来のShowメソッドの内容が実行され、フォームのLoadイベントやフォームのShownイベントが発生する。

 先ほどフォーム共通変数に設定した値は、Shownイベントの中でフォームのタイトルに設定される。Loadイベントではなく、Shownイベントで設定しているのは、Showイベントはフォームが表示された直後に発生するが、Loadイベントは表示前に発生するため、Loadイベント・プロシージャに時間のかかる重たい処理を記述すると、処理が終わるまでフォームが表示されないからだ。よってLoadイベントでは、フォーム画面の表示内容をクリア(=初期化)するくらいの軽めの処理を記述する程度にとどめるのがよい(今回のようにタイトル設定くらいであればLoadイベントでも問題はない)。

 時間のかかる処理を記述したのがShownイベントであれば、フォームが表示された後に処理が行われるため、エンド・ユーザーがフォームを表示する操作を行うとすぐにフォームが表示されてから処理待ちとなるため、操作に対する反応がよいUIが実現できる。

 以上、Showメソッドの拡張例を説明した。ShowDialogメソッドも、ほぼ同様の実装が可能なので、説明は割愛する。

 次の画面は、リスト5のプログラムを実行した例だ。[ShowDialogで値を渡す]ボタンや[Showで値を渡す]ボタンをクリックすると、子フォームが表示され、そのタイトルに(親フォームから渡された)文字列値(この例では「2010/11/11 1:36:41」)が設定される。

[Showで値を渡す]ボタン
図2 子ウィンドウに値を渡す

拡張していない本来のメソッドを使わないようにするには

 ShowメソッドやShowDialogメソッドを拡張して値を渡す場合、拡張していない本来のメソッドを使えないようにするか、コンストラクタなどで(その値の)デフォルト値を設定しておくのがよい。そのためにはObsolete属性(System名前空間)を使って、次のようなコードを書いてみるのもいいだろう。このコードでは、本来のShowDialogメソッドを、それと同じシグネチャ(=パラメータ群)を持つメソッドで(VBではOverloadsキーワード、C#ではnewキーワードにより)置き換えている。

<Obsolete("ShowDialog(title As String)を使用してください。", True)> _
Public Overloads Function ShowDialog() As DialogResult
  _title = "" ' デフォルト値を設定
  Return MyBase.ShowDialog
End Function
[Obsolete("ShowDialog(title As String)を使用してください。", true)]
public new DialogResult ShowDialog(IWin32Window owner)
{
  _title = ""; // デフォルト値を設定
  return base.ShowDialog(owner);
}
リスト5 Obsolete属性の使用例(上:VB、下:C#)

 Obsolete属性を指定した形式のShowDialogメソッドを記述しようとしても、「旧形式」という判定でコンパイル・エラーとなり、コーディング時点で意図しない使用方法を除外できる。

図3 Obsolete属性によるコンパイル・エラー(上:VB、下:C#)

子フォームから値を取得する方法

 子フォームに値を渡す場合は、ShowメソッドやShowDialogメソッドを拡張すればよい。しかし子フォームから値を取得したいときには、(ShowメソッドやShowDialogメソッドの戻り値では)値を1つだけしか返却できないため、使いづらい面がある。それを解決するのが独自プロパティの実装だ。

 次のコードは、「Message」という独自プロパティを実装した子フォーム「Property_Form」のコード例である。

Public Class Property_Form

  Public Property Message As String

  Private Sub OK_Button_Click(ByVal sender As Object, _
                              ByVal e As EventArgs) _
                  Handles OK_Button.Click
    Message = Me.Message_TextBox.Text
    Me.DialogResult = DialogResult.OK
    Me.Close()
  End Sub

  Private Sub Cancel_Button_Click(ByVal sender As Object, _
                                  ByVal e As EventArgs) _
                   Handles Cancel_Button.Click
    Me.DialogResult = DialogResult.Cancel
    Me.Close()
  End Sub

  Private Sub Me_Shown(ByVal sender As Object, _
                       ByVal e As EventArgs) _
                   Handles Me.Shown
    Me.Message_TextBox.Text = Message
    Debug.Print("Shown")
  End Sub
End Class
namespace WindowsFormParameterCs
{
  public partial class Property_Form : Form
  {
    public string Message { get; set; }

    public Property_Form()
    {
      InitializeComponent();
    }

    private void OK_Button_Click(object sender, EventArgs e)
    {
      Message = this.Message_TextBox.Text;
      this.DialogResult = DialogResult.OK;
      this.Close();
    }

    private void Cancel_Button_Click(object sender, EventArgs e)
    {
      this.DialogResult = DialogResult.Cancel;
      this.Close();
    }

    private void Me_Shown(object sender, EventArgs e)
    {
      this.Message_TextBox.Text = Message;
    }
  }
}
リスト7 子フォームに独自プロパティを記述した例(上:VB、下:C#)

 Messageプロパティは、自動プロパティ機能によりSetとGetが省略されている。親フォームからMessageプロパティに値を設定すると、内部的に値を保持して、その値をShownイベント・プロシージャの中でTextBoxコントロールに設定・表示している。

 この独自プロパティを実装したWindowsフォーム・クラスを使って子フォームを表示するには、次のように記述する。

Private Sub PropShowDialog_Button_Click(ByVal sender As Object, _
                                        ByVal e As EventArgs) _
                  Handles PropShowDialog_Button.Click
  Using _form As New Property_Form
    _form.Message = "Test"
    If _form.ShowDialog(Me) = DialogResult.OK Then
      MessageBox.Show(_form.Message)
    End If
  End Using
End Sub
private void PropShowDialog_Button_Click(object sender, EventArgs e)
{
  using (Property_Form _form = new Property_Form())
  {
    _form.Message = "Test";
    if (_form.ShowDialog(this) == DialogResult.OK)
    {
      MessageBox.Show(_form.Message);
    }
  }
}
リスト8 独自プロパティを実装した子フォームを表示するコード例(上:VB、下:C#)

 ShowDialogメソッドを実行した親フォームは、子フォームが閉じられるまでは次の行に処理が移らない。よって、子フォームでMessageプロパティ値を変更してからフォームを閉じれば、「親フォームでそのMessageプロパティ値を取得して、子フォームでの操作結果を取得する」という流れが簡単に作れる。

親フォームに値を設定する方法

 先ほどのリスト8はShowDialogメソッドを使っていたので、メソッド実行後に子フォームのMessageプロパティを取得すれば、子フォームでの値の変更を取得できた。

 もし、Showメソッドを使ったらどうだろうか。その場合、Showメソッドの実行は子フォームが表示されたらすぐに完了して次の行の処理を行うため、Showメソッドの次の行でMessageプロパティを参照しても、子フォーム側の値の変更と同期が取れない。

 そこで、子フォームに独自イベントを追加し、子フォームでの値の変更が完了したところでイベントが発生するように拡張する必要が出てくる。

 次のコードは、独自イベントを実装した子フォーム「Property_Form」のコード例である。

Public Class Property_Form

  Public Property Message As String
  Public Event FormCloseEvent(ByVal sender As Object, ByVal e As EventArgs)

  Private Sub OK_Button_Click(ByVal sender As Object, _
                              ByVal e As EventArgs) _
                    Handles OK_Button.Click
    Message = Me.Message_TextBox.Text
    Me.DialogResult = DialogResult.OK
    Me.Close()
  End Sub

  Private Sub Cancel_Button_Click(ByVal sender As Object, _
                                  ByVal e As EventArgs) _
                    Handles Cancel_Button.Click
    Me.DialogResult = DialogResult.Cancel
    Me.Close()
  End Sub

  Private Sub Me_FormClosed(ByVal sender As Object, _
                            ByVal e As FormClosedEventArgs) _
                    Handles Me.FormClosed
    RaiseEvent FormCloseEvent(Me, Nothing)
  End Sub
End Class
namespace WindowsFormEventCs
{
  public partial class Property_Form : Form
  {
    public string Message { get; set; }
    public delegate void FormCloseHandler(Property_Form sender, EventArgs e);
    public event FormCloseHandler FormCloseEvent;

    public Property_Form()
    {
      InitializeComponent();
    }

    private void OK_Button_Click(object sender, EventArgs e)
    {
      Message = this.Message_TextBox.Text;
      this.DialogResult = DialogResult.OK;
      this.Close();
    }

    private void Cancel_Button_Click(object sender, EventArgs e)
    {
      this.DialogResult = DialogResult.Cancel;
      this.Close();
    }

    private void Me_FormClosed(object sender, EventArgs e)
    {
      FormCloseEvent(this, null);
    }
  }
}
リスト9 フォームに独自イベントを記述した例(上:VB、下:C#)

 Visual BasicとC#で独自イベントの実装形式に多少の違いはあるが、今回のサンプルでは「FormCloseEvent」というイベントを新たに作成している。このイベントは第1パラメータとして自分自身のオブジェクトをイベント発生時に設定している。第2パラメータはほかのイベントと形を合わせるために定義しているが、イベント発生時には何も設定しない。

 この独自イベントを実装したWindowsフォーム・クラスを使って子フォームを表示するには、次のように記述する。

Private Sub PropShowList_Button_Click(ByVal sender As Object, _
                                      ByVal e As EventArgs) _
                  Handles PropShowList_Button.Click
  Dim _form As New Property_Form
  _form.Message = "Test"
  AddHandler _form.FormCloseEvent, AddressOf FormCloseEvent
  _form.Show(Me)
  PropFormList.Add(_form)
End Sub

Private Sub FormCloseEvent(ByVal sender As Property_Form, _
                           ByVal e As EventArgs)
  If sender.DialogResult = DialogResult.OK Then
    MessageBox.Show(sender.Message)
  End If
  PropFormList.Remove(sender)
End Sub
private void PropShowList_Button_Click(object sender, EventArgs e)
{
  Property_Form _form = new Property_Form();
  _form.Message = "Test";
  _form.FormCloseEvent +=
    new Property_Form.FormCloseHandler(this.FormCloseEvent);
  _form.Show(this);
  PropFormList.Add(_form);
}

private void FormCloseEvent(Property_Form sender, EventArgs e)
{
  if (sender.DialogResult == DialogResult.OK)
  {
    MessageBox.Show(sender.Message);
  }
  PropFormList.Remove(sender);
}
リスト10 独自イベントを追加した子フォームを表示するコード例(上:VB、下:C#)

 イベントを使うためには、独自イベントに対するイベント・プロシージャを定義してイベントと関連付ける。今回はListジェネリック・クラスでフォームを管理しているので、「AddHandler」ステートメント(C#の場合は「+=」演算子)で動的にイベントと関連付けしてから、Listジェネリック・クラスに「Add」していくとよい。

 これで子フォームを閉じれば、親フォームのイベント・プロシージャが自動的に呼び出される。そこで、子フォームの独自プロパティの値を参照すればよい。この手順により、Showメソッドを使った場合でも、子フォームから親フォームに値を返却できる。

まとめ

 複数フォーム間の情報共有は、「メイン・フォーム画面(=親フォーム)を表示したときに、自動的に認証用ログイン画面(=子フォーム)を開いて、そこで(エンド・ユーザーから)入力された認証情報をメイン画面で使う場合」など、特に業務アプリケーションを組むときに必要になってくる。しかし、Windowsフォーム・クラスには情報共有用のメンバが存在しないので、独自の対応が必要になる。

 今回の記事では、ShowメソッドやShowDialogメソッドを拡張(=オーバーロード)して子フォームへの入力パラメータを実装し、独自プロパティにより出力パラメータを実装しているが、ほかにもいろいろ方法があると思う。もし、既存のコードに対して今回のサンプルで取り上げた方法を追加するのが難しいようであれば、情報共有のキーポイントは画面に配置したコントロール上の値を直接操作しないのが情報共有のキーポイントであるという点を踏まえたうえで、独自に実装するのもよいだろう。end of article


 INDEX
  [連載]Windowsフォーム開発入門【Visual Studio 2010対応】
  Windowsフォーム間連携の基礎
    1.Windowsフォーム表示の基本
  2.子フォームに値を渡す方法/子フォームから値を取得する方法/親フォームに値を設定する方法

インデックス・ページヘ  「Windowsフォーム開発入門」


Insider.NET フォーラム 新着記事
  • 第2回 簡潔なコーディングのために (2017/7/26)
     ラムダ式で記述できるメンバの増加、throw式、out変数、タプルなど、C# 7には以前よりもコードを簡潔に記述できるような機能が導入されている
  • 第1回 Visual Studio Codeデバッグの基礎知識 (2017/7/21)
     Node.jsプログラムをデバッグしながら、Visual Studio Codeに統合されているデバッグ機能の基本の「キ」をマスターしよう
  • 第1回 明瞭なコーディングのために (2017/7/19)
     C# 7で追加された新機能の中から、「数値リテラル構文の改善」と「ローカル関数」を紹介する。これらは分かりやすいコードを記述するのに使える
  • Presentation Translator (2017/7/18)
     Presentation TranslatorはPowerPoint用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)
- PR -

注目のテーマ

業務アプリInsider 記事ランキング

本日 月間
ソリューションFLASH