解説

実例で学ぶWindowsプログラミング

第1回 MDI型Windowsアプリケーションの基礎開発

デジタルアドバンテージ
2004/11/10
Page1 Page2 Page3 Page4

■メニュー項目の処理の実装

 メニュー項目の処理を実装するには、それぞれのメニュー項目のClickイベント・ハンドラを追加する必要がある。これには、先ほど作成したメニュー項目をそれぞれダブルクリックしていけばよい(ただし親メニュー項目である[ファイル]、[ウィンドウ]、[ヘルプ]は子ニュー項目をまとめるだけのものなので、これらに対するClickイベント・ハンドラは追加しなくてよい)。さらに、フォームをダブルクリックして、Loadイベント・ハンドラも同時に追加する。すべてのイベント・ハンドラの追加が終われば、各ハンドラに次のコードを実装しよう。

private void menuItemStartMenu_Click(object sender, System.EventArgs e)
{
  StartMenuWindow startMenu = new StartMenuWindow();
  startMenu.MdiParent = this;
  startMenu.WindowState = FormWindowState.Maximized;
  startMenu.Show();
  startMenu.Update();
}

private void menuItemClose_Click(object sender, System.EventArgs e)
{
  this.Close();
}

private void menuItemCascade_Click(object sender, System.EventArgs e)
{
  this.LayoutMdi(MdiLayout.Cascade);
}

private void menuItemTileHorizontal_Click(object sender, System.EventArgs e)
{
  this.LayoutMdi(MdiLayout.TileHorizontal);
}

private void menuItemTileVertical_Click(object sender, System.EventArgs e)
{
  this.LayoutMdi(MdiLayout.TileVertical);
}

private void menuItemAllClose_Click(object sender, System.EventArgs e)
{
  Form [] children = this.MdiChildren;
  foreach (Form child in children)
  {
    child.Close();
    child.Dispose();
  }
}

private void menuItemAboutBox_Click(object sender, System.EventArgs e)
{
  MessageBox.Show("顧客.NET\n  ver. 0.1");
}

private void MainWindow_Load(object sender, System.EventArgs e)
{
  StartMenuWindow startMenu = new StartMenuWindow();
  startMenu.MdiParent = this;
  startMenu.WindowState = FormWindowState.Maximized;
  startMenu.Show();
  startMenu.Update();

  Form1 winform1 = new Form1();
  winform1.MdiParent = this;
  winform1.WindowState = FormWindowState.Maximized;
  winform1.Show();
  winform1.Update();

  Form2 winform2 = new Form2();
  winform2.MdiParent = this;
  winform2.WindowState = FormWindowState.Maximized;
  winform2.Show();
  winform2.Update();
}
メニュー項目の処理を行うサンプル・コード(C#)
VB.NETのサンプル・コード(winexp01_01.vb

 これらのコードは非常に単純なので詳しく説明する必要はないだろう。よってここでは、ごく簡単に説明するにとどめる。

 menuItemStartMenuコントロール(メニュー項目の[スタート メニュー])のClickイベント・ハンドラと、MainWindow(メイン・ウィンドウ)のLoadイベント・ハンドラでは、それぞれの子ウィンドウのインスタンスを生成して表示する処理を実装している。ここで注意が必要なのは、そこで生成した子ウィンドウ・オブジェクトのMdiParentプロパティに、メイン・ウィンドウのオブジェクト(this)を設定しているところだ。これにより、そこで生成されたウィンドウがMDIアプリケーションの子ウィンドウとして機能する。

 menuItemCascadeコントロール([重ねて表示]メニュー項目)、menuItemTileHorizontalコントロール([上下に並べて表示]メニュー項目)、menuItemTileVerticalコントロール([左右に並べて表示]メニュー項目)のClickイベント・ハンドラでは、フォームのLayoutMdiメソッドによりウィンドウの整列を行っている。LayoutMdiメソッドについては、MSDNのドキュメントを当たってほしい。

 menuItemAllCloseコントロール(メニュー項目の[すべて閉じる])のClickイベント・ハンドラでは、すべてのMDIアプリケーションの子ウィンドウを1つずつ取り出して、それらを順に閉じていく処理を行っている。

 以上でメニュー項目の処理を実装できた。ここまでで実装してきたアプリケーションを実際に実行してみよう。

実装したMDIアプリケーションの実行画面

 MDIアプリケーションを実行すると、メニュー項目の処理はすべて正常に実行できることが確認できる。しかし、1点だけ、開発者の意図どおりに動いていない個所がある。それは、メニュー・バーの[スタート メニュー]を選択すると、StartMenuWindowウィンドウがいくつも表示できてしまうことだ。次に、この問題を修正するために、StartMenuWindowウィンドウに対して1つのウィンドウしか表示しない仕組みを追加することにしよう。

■シングルトンの実装

 オブジェクトが1つしか生成できないことを保証する設計の仕組みは、デザイン・パターン(=よく使う汎用的な設計パターンをまとめたもの)の世界では、「シングルトン・パターン」と呼ばれる。このシングルトン・パターンをStartMenuWindowフォームに適用すれば先ほどの問題を解決することができる。具体的には、「StartMenuWindow.cs」に次のコードを加筆・修正する(追加・変更するのは太字の部分のみ)。

// クラス外部からのnew演算子の使用を禁止
//public StartMenuWindow()
private StartMenuWindow()
{
  InitializeComponent();
}

protected override void Dispose( bool disposing )
{
  if( disposing )
  {
    if(components != null)
    {
      components.Dispose();
    }
  }
  base.Dispose( disposing );

  // ウィンドウ破棄の際にインスタンスを初期化する
  instance = null;
}

#region Windows フォームをシングルトン対応にするコード
// インスタンスを格納するフィールド変数
private static StartMenuWindow instance = null;

// インスタンスを取得するためのメソッド

public static StartMenuWindow GetInstance()
{
  if (instance == null)
  {
    instance = new StartMenuWindow();
  }
  return instance;

}
#endregion
Windowsフォームにシングルトンを適用するサンプル・コード(C#)
VB.NETのサンプル・コード(winexp01_02.vb

 このコードでは、StartMenuWindowクラスのインスタンスを生成する手段を静的メソッドの「GetInstanceメソッド」のみに限定し、さらにそのインスタンスを静的フィールド変数の「instanceフィールド変数」に1つだけ保持・管理するような仕組みとなっている。これにより、クラス構造のレベルで、StartMenuWindowオブジェクトが確実に1つしか存在しないことを保証する。

 たとえもし、クラス外部でnew演算子によりStartMenuWindowオブジェクトを生成しようとするコードを記述しても、StartMenuWindowクラスのコンストラクタのアクセス修飾子が「private」となっているために、クラス外部からStartMenuWindowクラスのコンストラクタにアクセスできず、コンパイル・エラーとなる。

 よって、new演算子を使ってStartMenuWindowオブジェクトを生成していた先ほどのコードは、StartMenuWindow.GetInstanceメソッドを使ったコードに置き換える必要がある。具体的には次のように修正する。

  • 修正前:
      StartMenuWindow startMenu = new StartMenuWindow();

  • 修正後:
      StartMenuWindow startMenu = StartMenuWindow.GetInstance();

 本稿でこの修正が必要な個所は次の2カ所である。

  • menuItemStartMenuコントロール([スタート メニュー]メニュー項目)のClickイベント・ハンドラ

  • MainWindow(メイン・ウィンドウ)のLoadイベント・ハンドラ

 これらの修正を行って再度プログラムを実行すると、StartMenuWindowウィンドウが常に1つしか起動しないのを確認できる。もちろん本稿のようにわざわざシングルトンを使わなくても、同様の処理は実現可能である。例えば、子ウィンドウのインスタンス(=この例では「StartMenuWindowウィンドウ」)が作成されたかどうかのフラグを、メイン・ウィンドウのクラス内に作成して、そのフラグを使って(メイン・ウィンドウ側で)子ウィンドウのインスタンスが1つしか生成されないように管理すればよい。しかし、それではメイン・ウィンドウと子ウィンドウの依存性が増してしまう。2つのクラスの依存性が増すと、一方のクラスの変更が、もう一方のクラスに(知らないうちに)影響を与える可能性が高くなる。よって、本稿のようにシングルトン・パターンを使ってクラス内で自身のインスタンス管理を完結させた方が、(ほかのクラスからの影響に対して)より頑健なクラスになるといえるだろう。

 以上で今回は終わりだ。ここまでの実装でMDI型Windowsアプリケーションの基礎部分が作成できたことになる。次回以降の解説で、さらにこのMDIアプリケーションを拡張していく予定だ。

 次回は、業務アプリケーションでは欠かせない「使いやすさを追求したスタート・メニュー画面の構築」について解説する。スタート・メニュー画面とは、業務処理画面に簡単にアクセスするためのメニュー画面である。次回もお楽しみに。End of Article


 INDEX
  解説:実例で学ぶWindowsプログラミング
  第1回 MDI型Windowsアプリケーションの基礎開発 
    1.本連載で開発するWindowsアプリケーションについて
    2.MDIアプリケーション開発の準備
    3.MDIアプリケーションへのコントロールの追加
  4.MDIアプリケーションのメニュー項目処理の実装
 
インデックス・ページヘ  「解説:実例で学ぶ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