.NET ROOM.NET ROOMトップへCOMRADE総合トップへ
「2:イベントハンドラの登録と独自イベントの作成」へ

独自イベントの作成

 独自イベントの説明には、NumericUpDownというコントロールを使います。
はじめにNumericUpDownコントロールの特徴と問題点についてみておきましょう。

NumericUPDownの特徴と問題点


 このコントロールの外見を(図2)に、特徴を以下に簡単に挙げておきます。

図2:NumericUpDownコントロール



特徴1: 数字のみの入力が可能。

特徴2: 数字の値を上下ボタンで変更できる。

特徴3: Up/Downボタンによる変更に関しては、Maximum/Minimumプロパティにより、
上限と下限を設定できる。

特徴4: ReadOnlyプロパティをTrueに設定することにより、キーでの入力を禁止し、
Up/Downボタンのみで値を設定させるようにすることができる


 上記のように、NumericUpDownは便利なのですが、以下のような問題点があります。


問題点1: ReadOnlyプロパティをTrueに設定しても、フォーカスが移動すると
カレットが表示される。

問題点2: Maximum/Minimumプロパティがキーを入力した段階では検証されず、
[Enter]キーを押下してはじめて検証される。

問題点3: ValueChangedイベントは存在するが、Up/Downボタンが押されたことを
通知するイベントが存在しない。


  問題点1はプログラマが対処できる問題ではありません。Microsoftが修正してくれることを祈ります。

  問題点2に関しては、ValueChangedイベントの中で、値が検証されているのでこのような動作になっています。
そのため、たとえばKeyPressイベントで検証することで、キーが入力されるたびに値を検証し、
無効な入力を防ぐことが可能です。

  問題点3に関しては、独自にイベントを作成することで対処可能です。

独自イベント作成手順


 では、NumericUpDownに対して、独自のイベントを追加する手順を説明してゆきましょう。

 まずはじめに、これまで同様、「新しいプロジェクト」ダイアログで「Visual C#プロジェクト」のテンプレートから
「Windowsアプリケーション」を選択し、プロジェクト名を付けて[OK]ボタンを押し、新しいプロジェクトを作成します。

 Form1.csのデザイン画面が現われるので、ツールボックスから「NumericUpDown」を選択して、
フォームにドラッグします。

 NumericUpDownの機能拡張版の派生クラスは、Form1.csのForm1クラスの内部に、
プライベートクラスとして実装することも可能ですが、ここでは汎用的なコントロールを作成することが目的なので、
独自のパブリッククラスとして作成します。
また、クラスは、Form1.csのForm1クラスの後に記述しても問題ありませんが、
後でコントロールだけ切り離せるようにするために、ここでは別ファイルとして実装することにします。

 では早速ファイルを追加してみます。
新しいファイルの追加は、図3のように、ファイルを追加したいプロジェクトを選択し、
右クリックすると表示されるショートカットメニューから[追加]-[新しい項目の追加]を選択します。
すでに存在するファイルをプロジェクトに追加する場合は、[既存項目の追加]を選択します。

図3:新しい項目の追加
新しい項目の追加


 なお、ここで[継承コントロールの追加]を選択すればいいような気がしますが、[継承コントロールの追加]で
ファイルを作成しようとすると、図4のような画面になってしまいます。
標準コントロールの継承用テンプレートも用意してほしいものです。
仕方がないので、ここでは新しいファイルを追加して、実装はすべてコーディングすることにしました。

図4:継承コントロールの追加ではない
継承コントロールの追加ではない


 先の図3で[新しい項目の追加]を選択すると「新しい項目の追加」ダイアログが現われるので、
「コード」カテゴリの「クラス」テンプレートを選択し、名前を入力して[開く]ボタンを押します。

  生成されたコードはそのままでは意味をなさないので、コードエディタでリスト3のように記述します。

リスト3:イベントを拡張したNumericUpDownの派生クラス
using System;
using System.Windows.Forms;
public class ExtendedNumericUpDown : NumericUpDown {
 public class UpDownEventArgs : EventArgs {
  private bool handled = false;
  private readonly bool up;
  internal UpDownEventArgs(bool up) {
   this.up = up;
  }
  public bool Handled {
   get {return handled;}
   set {handled = value;}
  }
  public bool Up {
   get {return up;}
  }
 }
 public delegate void UpDownEventHandler(
  object sender, UpDownEventArgs e);
 public event UpDownEventHandler UpDown;
 public override void DownButton() {
  UpDownEventArgs e = new UpDownEventArgs(false);
  if (UpDown != null)
   UpDown(this, e);
  if (!e.Handled)
   base.DownButton();
 }
 public override void UpButton() {
  UpDownEventArgs e = new UpDownEventArgs(true);
  if (UpDown != null)
   UpDown(this, e);
  if (!e.Handled)
   base.UpButton();
 }
}

派生クラスの実装コード


 次にリスト3の内容を簡単に説明しましょう。

 最初に、UpDownイベントがどのようなイベントであるかを決めなければなりません。
UpDownイベントは、NumericUpDownコントロールのUp/Downボタンが押されたときに発生し、
イベントハンドラで処理をキャンセルすることも可能なイベントとして実装しています。

 まず、イベントの元になるデリゲートを宣言します。

   public delegate void UpDownEventHandler(
    object sender, UpDownEventArgs e);

 今回は、UpDownEventHandlerという名前で、イベントの発生元を通知するためのobject型のsenderと、
イベントの詳細情報を含めたUpDownEventArgs型の引数を持ったデリゲートにしました。

 続いて、UpDownEventHandler型のイベントである、UpDownイベントを宣言します。

   public event UpDownEventHandler UpDown;

 なお、イベントの利用に関するドキュメントには、


「規約により、.NET Frameworkのイベントデリゲートは、そのイベントの発生元と、
そのイベントのデータという2つのパラメータを持ちます。
この例でのイベントデータクラスは、System.EventArgsから派生します。
イベントがデータを生成しない場合は、イベントデータ型としてEventArgsが使われます」



という記述[注1]がありますが、自作のイベントではどのようなデリゲートでもイベントとして定義できます。
そのため、以下のように、引数のないデリゲートをイベントとして設定することも可能です。

   public delegate void UpDownEventHandler();
   public event UpDownEventHandler UpDown;

 ただし、.NET Frameworkの規約に則った仕様のイベントを作成したほうが、誤解が少なくていいかもしれません。

 次にUpDownEventArgsクラスですが、このクラスにはEventArgsクラスから派生させた、
以下の2つのプロパティが追加してあります。


Up: 押されたボタンが、UpボタンとDownボタンのどちらであるかを示す

Handled: イベントハンドラ内でUpDownイベントを処理し終わり
デフォルトのUp/DownButtonの処理をキャンセルしたい場合にTrueを指定する


 次はUpDownイベントの呼び出し部分です。NumericUpDownクラスには、もともとUpButtonメソッドと
DownButtonメソッドという、Up/Downボタンが押されたときに呼ばれるパブリックメソッドがあります。
そこで、今回のUpDownイベントの呼び出しは、UpButtonメソッドとDownButtonメソッドの中で行なうことにしました。

 ここでイベントに関して注意が必要な点について説明しておきましょう。
まず、イベントは何も登録されていない場合は、NULLになるという点です。
そのため、リスト3で、

    if (UpDown != null)
    UpDown(…);

となっている箇所を、

   UpDown(…);

のように、Nullチェックをせずに使用すると、UpButtonメソッドが呼ばれたタイミングで、
NullReferenceExceptionがThrowされてしまいます。

 また、リスト3の、

   if (!e.Handled)
    base.UpButton();

という箇所は、イベントハンドラでUpDownEventArgs.HandledプロパティをTrueにした際、
UpButtonをキャンセルできるようにするための記述です。


---------------------------------------------------------------------------
注1)
http://www.microsoft.com/japan/msdn/library/default.asp?url=/japan/msdn/library/ja/cpguide/html/cpconeventsoverview.asp
より一部抜粋。



前へ | ページトップへ | 次へ