Windowsフォームの入力チェックを効率化するはじめに入力チェックはどのプログラムにも必要なものであり、どのプログラマにとっても悩みの種です。大抵の場合は、「電話番号が7桁または10桁である」「IPアドレスが4つのオクテット型から成り立つ」「国名が193ある選択肢の1つと一致している」といったことを確認するプログラムロジックをハードコーディングすることになります。.NET 2.0では、入力チェックを合理化するためのサポートがいくつか取り入れられましたが、これは入力チェックプロセスの自動化や単純化を少しばかり手助けするものにすぎません。入力チェックに関してMSDNを検索しても、見つかる情報の大半は、検証コードをハードコーディングする方法を紹介しています。しかし、検証を必要とする個々の要素に手書きのカスタムコードを用意することは、保守という観点から見れば悪夢としか言いようがありません。 また、検証のステップをプログラミングのステップと同様に「AならばBする」といった形で概念化することは不自然です。それよりも、検証するデータの「質」に焦点を当てた方がずっと分かりやすいはずです。例えば、入力データを説明するときには、「5〜8文字にする」「負でない数字にする」「必ず最後は『5』で終わる」などというのが普通でしょう。 本稿で紹介する検証エンジンは、検証プロセスよりもデータの質に焦点を当てるという考え方から生まれています。モジュール形式でデータ主導の検証エンジンアプローチを採用することで、より自然なインターフェイスが実現され、柔軟性が向上し、保守も簡単になりました。Visual Studioでは、デザイン時にさまざまなコンポーネントにプロパティ値を指定することができます。このアプローチを検証属性の定義に使用することは自然の流れであり、これによって、開発者は堅牢な検証メカニズムをもっと簡単に導入できるようになります。また、このアプローチでは検証の設定を構成ファイルに格納するという柔軟な仕組みを採用しているため、検証の変更も簡単になり、開発者や管理者はコンパイルやデプロイをやり直したり、ソースコードを書いたりせずに、検証プロパティの修正やカスタマイズを行うことができます。本稿で紹介する検証エンジンを使えば、わずか数行のコードで幅広い検証基準を活用することができます。 目標とするソリューションフォームの入力チェックを実行し、ユーザーにフィードバックし、できればその条件を満たすために必要なその他のアクションを実行できるプログラムを実装します。さらに、できるだけ単純かつ簡単な実装にすることを目指します。 本稿のソリューションの制限本稿で紹介するソリューションは、有益ではありますが、どのケースにも適した完全なソリューションではありません。むしろ、80対20の法則を採用しています。つまり、この検証エンジンは、対処する必要があるユースケースの約80%に対して役立ちます。さらに、これまた80対20の法則に従い、残りの20%のユースケースをカバーするには労力の80%が必要になると考えられます(つまり、これまで費やしてきた労力の5倍が必要になります)。 そのため、今回の検証エンジンでは、例えば複数のフィールド間の関係に基づく検証は実装していません。その代わりに、単一フィールドの検証に焦点を当て、基本的な仕組みを見ていくことにします。 基本となるソリューション:ErrorProviderコンポーネントまず、ErrorProviderという便利なMicrosoftコンポーネントを知る必要があります。
ErrorProviderをビジュアルデザイナにドラッグすると(またはコード内でそのインスタンスを作成すると)、検証メッセージを表示するために必要な基本コンポーネントが作成されます。ErrorProviderの代表的な使用例を次に示します(MSDNライブラリの『方法 : WindowsフォームErrorProviderコンポーネントを使用してフォーム妥当性検査でエラーアイコンを表示する』より引用)。 protected void textBox1_Validating(object sender, CancelEventArgs e) { try { int x = Int32.Parse(textBox1.Text); errorProvider1.SetError(textBox1, ""); } catch (Exception e) { errorProvider1.SetError(textBox1, "Not an integer value."); } } このコードはTextBoxへの入力を検証し、入力値が整数かどうかを確認します。入力値をテストするため、このコードでは このメソッドを、ビジュアルデザイナを使うか、次のコードをプログラミングすることで、textBox1という特定のTextBoxコントロールの this.textBox1.Validating += new CancelEventHandler(this.textBox1_Validating); ここに、MSDNライブラリの情報からはまったくわからない重要な事実があります。 この例の ErrorProviderには、他のエラー表示テクニックにはない次のような長所があります。
別のコントロールへの移動を制限する
e.Cancel = true;
このコード行により、通常は ErrorProviderの出力次に、ErrorProviderが実際にどのような出力をするのかを見てみましょう。図1のシンプルなフォームを見てください。このシンプルなフォームには、バインドされていないDataGridView、2つのTextBoxコントロール、OKボタン、Cancelボタンが含まれています。以降ではこのフォームを中心に話を進めていきます。 先ほど説明した お分かりのように、ErrorProviderを使えば、フォーム入力チェック用のユーザーインターフェイスを簡単に作成することができます。とはいえ、これまでのことをすべて実現するには、フォーム上の個々のコントロールに 「エネルギーの総支出を減らすために、多大な努力をするように、あなたをかりたてる性質。こうして労力を省くために書いたプログラムは他人も使うようになり、そのプログラムに関する質問にいちいち答えずに済ますためにドキュメントを書くようになる。」 つまり、この場合で言えば、もっと良い方法があるはずだ、ということです。そして、実際にあるのです。そのためには、 より優れたソリューション今度は、ErrorProviderを利用しつつ、柔軟性も高い検証エンジンの実装方法を考えてみます。具体的には、エラーメッセージをLabelコントロールに表示するようにします(マウスでポイントするのが嫌いな方向けです)。開発者が限界を感じることがないよう、このエンジンでは、カスタムの検証機能と自動的な検証機能を(統合させないまでも)共存させることができなければなりません。 これを踏まえて、検証の性質とビジネスルールの概念を考えてみましょう。例えば、米国内の電話番号を入力するフォームフィールドがあるとします。この要件の影にあるのが、電話番号が何で構成されているかについての多くの前提条件です。ビジネスルールとしては、次のようなものが挙げられます。
一般に、ビジネスルールとは、すべてのビジネスに当てはまるものではなく、個別のビジネスに当てはまるものと考えられていますが、ここで挙げたルールも、データの意味を問うという広い意味でのビジネスルールです。今度は、米国内の電話番号という汎用的な特徴ではなく、このサンプルビジネスのみに当てはまるビジネスルールをもう1つ追加します。
フォームの入力を処理する多くのソフトウェアアプリケーションは、次の仮想コードのようなロジックを使って、ビジネスルールのチェックをフォームの処理と一緒にハードコーディングしています。 read phoneNumber if (phoneNumber contains only digits and hyphens) and getAreaCode(phoneNumber) is on the California list of area codes then store phoneNumber else flag invalid phone number しかし、この会社がオレゴン州やワシントン州にも拡大するとしたらどうなるでしょうか。ビジネスルールの変更に伴い、コードの変更も必要になります。これは、9月のレイバーデイが過ぎてもまだ夏用の白い靴を履いているのと同じくらい格好悪いことであり、きちんとした人々――つまりここで言えばクリーンなコード――ならば絶対にしてはならないことです。データをプログラムの外部に置くソフトウェア開発者はたくさんいますが、ルールを外部化する人は意外に少数です。そしてここが、うまく設計された検証エンジンの力の見せどころになります。ビジネスルールをプログラムの外部に置くことによって、ソフトウェア設計者ではなくビジネスアナリストによる管理を実現することができます。 従って、本稿の検証エンジンが最終的に目指すところは次のようになります。
ビジネスルールを外部で保守するこれは本稿の検証エンジンの機能で最も重要な部分ですが、Visual Studio 2005のビジュアルデザイナを使えば非常に簡単に実現できます。とはいえ、学ぶことがないわけではありません。.NETでの永続的な設定については、MSDNライブラリの『方法 : アプリケーション設定を追加または削除する』などを参照してください。 簡単に言えば、.NETは永続的な設定をローカルシステム上の Visual Studio 2005のビジュアルデザイナでは、コードから独立した形でアプリケーションのユーザーインターフェイス層にアクセスすることができます。ビジュアルデザイナ上での操作はコードに自動的に反映されますが、形としては別々に保持されています。フォーム検証用のビジネスルールは、当然、ユーザーインターフェイスに密接に結び付けられます。つまり、フォーム上のコントロールのほとんどに1つ以上の固有のフォーム検証用のビジネスルールが割り当てられます。では、ユーザーインターフェイスを指定するビジュアルデザイナで検証用のビジネスルールを指定してはどうでしょうか。 ビジュアルデザイナには、フォーム上のコントロールごとにプロパティペインが用意されています。ここで必要なことは、プロパティとしてビジネスルールを追加することだけです。しかし、プロパティペインにはプロパティを追加することができないので、そこが問題です。この問題を簡単に回避する1つの方法は、ユーザー定義可能なプロパティとして提供されている ビジネスルールに関連付けるには、検証対象の各コントロールに、次のような形式の一連の属性から構成される <attr1>=<value1>;<attr2>=<value2>;...;<attrn>=<valuen> さらに高度なソリューションとして、ExtenderProviderコントロールを使って、利用可能な属性に対応する実際のプロパティを定義し、 表1に、利用可能な属性とその型を示します。 表1 検証属性:利用可能な検証属性とその型および簡単な説明
最初の2つ( 表2 一般的な検証属性の組み合わせ:よく使われる一般的な属性の組み合わせ
Visual Studioのビジュアルデザイナでフォームをデザインしながら、各コントロールのプロパティペインにビジネスルールを入力することができます。次に、そのプロパティをアプリケーション設定に関連付け、アプリケーションが実行されたときに外部化されるようにする必要があります。ステップは次のとおりです。 まず、コントロールの 図3 アプリケーション設定 [(PropertyBinding)]の省略記号ボタンをクリックします。選択したコントロールに関する[Application Settings]ダイアログボックスが開きます(図4を参照)。このダイアログボックスを使って、プロパティ値をコントロールの個々のアプリケーション設定に割り当てます。 図4 新規のApplication Settingsダイアログボックス 図5 New Application Settingダイアログボックス:新しい設定に名前を割り当てる [OK]をクリックして[New Application Setting]ダイアログボックスを閉じ、再度[OK]をクリックして[Application Settings]ダイアログボックスを閉じます。これでプロパティペインは図6のようになります。先ほど入力した 図6 設定の完了 理論上は、これでプロパティのバインディング作業は終わりですが、実際にはまだ終わっていません。実行しなければならないステップがもう1つあります。プロジェクトプロパティウィンドウの[Settings]タブを開いてください。 これを表示するのは簡単です。設定の型を 実装 本稿で作成した検証エンジンの名前はValidatorです。これは私のオープンソースのWebサイトからダウンロードできます(Products >> Documentation >> C# と選択してください)。このエンジンは、前述の コントロールを検証するには、そのコントロールをValidatorインスタンスに追加し、コントロールの 検証エンジンは、指定されたコントロールのすべての検証属性(ビジネスルール)を評価しますが、1つのコントロールにつき一度に1つのエラーしか報告しません。例えば、1つの入力が最大長テストと最大値テストの両方に不合格だったとしても、エンジンがユーザーに報告するのはそのうちの一方のエラーだけです。そのため、ユーザーがそのエラーを修正した後で、もう一方のエラー条件が表示される可能性があります。 サンプルのコーディングでは、実際のコーディングについて説明しましょうょう。これまで使ってきたシンプルなサンプルフォームのユーザーコードは次のとおりです(デザイナで生成したコードとは異なります)。個々の短いメソッドはイベントハンドラなので、適切なイベントに関連付ける必要があります(どのイベントに関連付けるかは、メソッドの名前から分かるはずです)。 using System; using System.ComponentModel; using System.Windows.Forms; using CleanCode.Forms; namespace ValidationDemo { public partial class Form1 : Form { private Validator validator; public Form1() { InitializeComponent(); } private void Form1_Load(object sender, EventArgs e) { validator = new Validator(okButton, errorProvider); validator.Add(dataGridView); validator.Add(leftTextBox); validator.Add(rightTextBox); } private void okButton_Click(object sender, EventArgs e) { MessageBox.Show("OK clicked"); } private void cancelButton_Click(object sender, EventArgs e) { MessageBox.Show("cancel clicked"); } private void leftTextBox_Validating(object sender, CancelEventArgs e) { if (!validator.Validate(leftTextBox)) { e.Cancel = true; } } private void rightTextBox_TextChanged(object sender, EventArgs e) { validator.Validate(rightTextBox); } private void dataGridView_CellValidating( object sender, DataGridViewCellValidatingEventArgs e) { if (!validator.Validate((DataGridView)sender, e)) { // e.Cancel = true; // this really traps the user in there! } } } } これが、図1のサンプルアプリケーションに関してVisual Studio 2005内で記述する検証コードのすべてです。実際の検証コードはValidatorに含まれています。この他に必要なのはビジネスルールだけです。次に、これについて簡単に説明します。 前述のコードで、Form1_Loadイベントハンドラは検証エンジンのインスタンスを作成し、検証する各コントロールをそのインスタンスに追加しています。Validatorコンストラクタがフォームの[OK]ボタンの参照を受け取ることに注意してください。Validatorはその参照を使って、すべてのコントロールが入力チェックに合格した場合は[OK]ボタンを選択可能にし、いずれかのコントロールが入力チェックに合格しなかった場合は[OK]ボタンを選択不能(グレーアウト)にします。また、Validatorコンストラクタは、エラー表示用のErrorProviderも受け取ります。 DataGridViewコントロールのような複数のデータ値を含むコントロールの場合でも、メソッドを呼び出して検証を実行する処理は、単一値のコントロールの場合とほぼ同じです。ただし、ビジネスルールセットに指定する情報の量は多くなります(詳しくは後述)。DataGridView検証の処理は、特にDataGridViewがバインドされている場合にはかなり複雑になる可能性がありますが、本稿では説明を省きます。 サンプルアプリケーションのビジネスルール 前述のコードに加えて、 表3 サンプルアプリケーションのTagプロパティ値:サンプルアプリケーションの3つのTextBoxコントロールに対するTagの設定
これらのルールでは、正規表現を用いて、各コントロールに入力すべき値を記述しています。leftTextBoxには、電子メールアドレス形式の値を入力しなければなりません(「foo@bar.com」も「e@a.b.c.d」も有効です)。rightTextBoxには、5、-25、-3.9999、.81などの数値を入力しなければなりません。dataGridViewでは、列ごとにビジネスルールを割り当てることも可能ですが、必須ではありません。この場合、このルールは、「Column」という名前の列では文字または数字のみを受け付け、「MatchExpr」という列では入力のどこかに1組の始めかっこと終わりかっこが必要であることを表しています。 著者メモ
表3で、複数のビジネスルールセットを1つのプロパティに埋め込む場合の入力形式に注意してください。各列の名前を大かっこで囲んで指定し、その後ろにビジネスルールを記述しています。
サンプルを実行する場合の注意事項アプリケーションをビルドして実行し、どうなるか見てみましょう。入力チェックに不合格であるとボタンは選択不能になります。この場合、ValidatorはErrorProviderを使って検証エラーを表示します。今回のコードではErrorProviderの作成を分離化しているので、検証エンジンに実装されているビジネスルールカテゴリの範囲に含まれない独自の検証チェックをイベントハンドラに自由に追加することができます。 ここでは、ビジネスルールとコードをどのように切り離すか、なぜ切り離すのかを理解していただけるように、検証エンジンを利用しました。検証エンジンそのもののメカニズムについては、ソースコードを参照してください。これは、以前にWebサイト用にPerlとJavaScriptを使って実装した「PageValidator」という検証エンジンの簡易バージョンです。同じエンジンをクライアントとサーバの両方で実行することで、クライアントとサーバの両方で高速応答とセキュリティを実現できます。 著者紹介Michael Sorens(Michael Sorens)
フリーランスのソフトウェアエンジニア。フェニックス大学やコミュニティカレッジでの指導、2冊の本とさまざまな記事の執筆、さらにオープンソースのWebサイトを通して、優れたデザインの種をまき続けている。Fortune 500企業やベンチャー企業でJava、C#、Perl、C、Lisp、PostScriptなどのプログラミングに従事。お気に入りのプロジェクトは、シリコン製でプリンタヘッドにレーザーを使用した、独立宣言を文字どおりピンの頭に印字できる世界最小のワープロの設計と実装。
|