.NET ROOM.NET ROOMトップへCOMRADE総合トップへ
「1:COM/.NETコントロールの作成方法」へ

VC#.NETを使った.NETコントロールの実装

 次にVC#.NETを使って.NETコントロールを作成する手順を説明します。

 COM版と同様に、メニューから[ファイル]-[新規作成]-[プロジェクト]を選択すると、
「新しいプロジェクト」というダイアログが開きます。
そして、「プロジェクトの種類」から「Visual C#プロジェクト」を、「テンプレート」から
「Windowsコントロールライブラリ」を選択し、任意のプロジェクト名を付けてプロジェクトを作成する場所を選びます。

 [OK]ボタンを押すと、UserControlのデザイン画面になり、メニューから[表示]-[コード]を
選択するとコードが現われます。

 残念ながら、派生クラスを選択してクラスを生成するようなオプションがないため、生成されるクラスは
Sytem.Windows.Forms.UserControlからの派生で作成されます。
そこで、コード編集画面で以下のような修正を加えます。

 デフォルトで追加されているコードは、UserControlを使用する場合のコードなので、消してしまいましょう。
修正後のコードは以下のような、非常にシンプルなものになります。

   public class DigitText : TextBox {
   }

 続いて、OnKeyPressメソッドのオーバーライドの仕方です。

 メニューから[表示]-[クラスビュー]を選択して、クラスビューを表示します。
そして、クラスビュー上で目的のクラスを発見し、「ベースとインターフェイス」というところをクリックして、
基底クラスをたどってゆきます(図5)。
OnKeyPressメソッドはControlまでさかのぼらないと出てきませんでした。

図5:クラスビュー



  クラスビューでOnKeyPressメソッドを選択して、右クリックすると表示されるショートカットメニューから[追加]
[オーバーライド]という項目を選択すると、オーバーライドメソッドの雛形が自動生成されます。

 同様にTextプロパティのオーバーライドですが、先ほど同様にクラスビューでTextBoxクラスまで基底クラスを
たどってゆき(図6)、Textプロパティを選択して右クリックすると表示されるショートカットメニューから
[追加]-[オーバーライド]という項目を選択すると、オーバーライドメソッドの雛形が自動生成されます。

図6:Textプロパティのオーバーライド



ページトップへ


COMコントロールと.NETコントロールの簡単な比較

 COMコントロールと.NETコントロールの簡単な比較をしてみると、次のような違いがあります。
(詳細は付録のサンプルプログラムを参照してください)


違い@ COMコントロールのソースの構成ファイルは .NETコントロールよりも多い。

違いA プロジェクトファイルを除いた、純粋なソース部分の書式は、COMコントロールの場合
VC++.NET、IDLファイル、DEFファイルなどに分かれているが、
.NETコントロールの場合は基本的にVC#.NETのみで記述可能である。


 これはVC#.NETが、プロパティやイベントを含んだ言語仕様になっており、VC#.NETのみですべてを
記述できるようになっているからです。

 今回の解説では、Visual Studio .NETを使用してコントロールを作成していましたが、
.NET Framework SDKを利用し、コマンドラインでコンパイルして実験を行なうということも簡単にできます。
COMコントロールを作成する場合には、前述のとおり、さまざまなファイルを必要とするため、
C++のソースコードだけでは表現しきれません。
このため、Visual Studio .NETの助けなしにコントロールを作成することは
非常に難しいと言えるのではないでしょうか?

 テストが簡単に行なえるので、仕様などの確認でリファレンスを参照する以外にも、テストで細かい動作や
リファレンスではわからない点などを気軽に確認できるということは非常に大きなメリットだと思います。


ページトップへ


.NETコントロールの実装の詳細

 続いて、コントロールの実装の詳細に関して解説してゆきましょう。

Key入力制御


 はじめに、Key入力を制御する部分です。Key入力制御部分のソースコードをリスト1に示します。

リスト1:.NET版のKey入力制御部分(DigitText.csの6〜14行目)
protected override void OnKeyPress(KeyPressEventArgs e) {
 char c = e.KeyChar;
 const char BACKSPACE = '\b';
 if (!char.IsDigit(c) && c != BACKSPACE) {
  e.Handled = true;
  return;
 }
 base.OnKeyPress(e);
}
※COM版のコードはDigitTextCtrl.cppの221〜226行目。
ソースコードの詳細は付録に収録しているサンプルプログラムを参照してください。


 この部分は、DigitTextクラスが、基底クラスのTextBoxのOnKeyPressの挙動をオーバーライドする部分です。
最後の1行、

   base.OnKeyPress(e);

で、基底クラスの処理も行なうようにします。実験してみたところ、この行をコメントアウトしても、

   e.Handled = true;

にしない限り、基底クラスのOnKeyPressイベントの挙動は動作しているようにも思われます。
ただし、DigitTextからさらに派生してクラスを作った場合に、
この部分を省くと、DigitTextで定義した動作が活きません。
派生クラスで挙動をオーバーライドする場合には、基本的に基底クラスの処理を呼び出すようにしましょう。

 また、注意点としては、この部分を誤って、

   OnKeyPress(e);

というように「base.」を抜いて記述してしまうと、永久に“再帰呼び出し”が続き、やがてスタックが足りなくなり、
System.StackOverflowExceptionというExceptionをThrowしてしまうので注意が必要です。

Text入力制御


 上記の処理で、Key入力によるデータは数字のみになったはずですが、
コードから値を代入した場合は数字以外の文字が代入できてしまいます。
そこで、いかにその部分を制御するかについて解説します。
Text入力制御部分のソースコードをリスト2に示します。

リスト2:.NET版のText入力制御部分(DigitText.csの15〜28行目)
static bool isNumericString(string text) {
 foreach (char c in text)
  if (!char.IsDigit(c))
   return false;
 return true;
}
public override string Text {
 get {
  return base.Text;
 }
 set {
  if (isNumericString(value))
   base.Text = value;
 }
}
※COM版のコードはDigitTextCtrl.cppの228〜236行目。
ソースコードの詳細は付録に収録しているサンプルプログラムを参照してください。


  この例では、Textへの代入値に対して、それが数字のみで構成されている文字列かを
判定して処理を分けています。
文字列の要素にアクセスする部分で、foreachを使ってみました。

 foreachはVisual Basicで使用可能でしたが、C#でもIEnumerableインターフェイスを実装している
クラスに対して使用することができ、コレクションの要素ひとつひとつに対してアクセスできます。
StringはCharのコレクションであり、IEnumerableインターフェイスを実装しているため、
Stringの中のCharに対してforeachでアクセス可能です。

 Property のオーバーライドに関しては、残念ながらget/setアクセサのどちらか片方だけを
オーバーライドすることはできないので、まとめてオーバーライドする必要があります。
その場合も、基底クラスのメソッドを呼ぶことを忘れないようにしましょう。

 なお、For文での同様の表現は以下のようになります。

   // For文を使用した同等表現
   for (int i = 0; i < text.Length; i++)
    if (!char.IsDigit(text[i]))
     return false;

 このStringクラスの中身に対する配列演算子でのアクセスは、インデクサアクセスといいます。
ただし、Stringクラスのインデクサは、getのみが定義されている“ReadOnlyな”アクセサであるため、
以下のコードはコンパイルエラーになります。

   // Stringクラスのインデクサへの
   // 代入(コンパイルエラー)
   string str = "abc";
   str[1] = 'd';
   Console.WriteLine(str);

 上記のコードで意図した処理を実装したい場合には、
StringBuilderクラスを使用して以下のようなコードにします。

   // StringBuilderクラスを使用した、
   // 文字列の値の変更
   string str = "abc";
   StringBuilder sb = new StringBuilder(str);
   sb[1] = 'a';
   str = sb.ToString();
   Console.WriteLine(str);
   // 結果: aac

 また、Stringクラスは、コンストラクタで初期化した後、変更不可能なクラスです。
「それはおかしい。Replaceというメソッドがあるではないか」と思われるかもしれません。
しかし、Replaceは中身を変更するメソッドではなく、
実は変更が適用された“新しいStringのインスタンス”を返します。

 このように、Stringクラスのメソッドは新しいStringのインスタンスを返すようになっているため、
大幅な変更を加えるときはパフォーマンスに影響を与えてしまいます。
したがって、そのようなときにもStringBuilderクラスを使用するようにします(記事末コラムを参照)。


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