連載:VB 6ユーザーのための
これならマスターできるVB 2005超入門

第5回 データベースはじめの一歩

羽山 博
2006/12/27
Page1 Page2 Page3 Page4

ユーザビリティ小考 〜 前回の反省から

Back Issue
1
VB 6の皆さん、これはもうVB 2005使うしかないでしょ
2 コントロール配列がなくても大丈夫!
3 VB 2005の“My”の便利さに脱帽!
4 トラブルは水際で防げ〜入力時のチェックとエラー処理

 前回は、入力や検索に使う簡単なフォームを使って、Validatingイベントを中心に入力時チェックやエラー処理の方法を見た。サンプルとしては極めて単純なものであるにもかかわらず、プログラミング技法やユーザビリティに直接かかわる問題が多く、読者の方からいくつかご質問やご指摘をいただいた。今回は、そのあたりから始め、後半はお約束していたデータベースの検索へと話を進めたい。

 データベースに一刻も早く取り組みたいという向きには、前半の話は飛ばしていただいて、先(次のページ)に進んでもらっても構わない。しかし、データベースといっても、その全容はあまりにも壮大かつ深遠なので「千里の道も足下よりはじまる」の古諺に沿って、欲張らずにはじめの一歩から着実に踏み出すこととしよう。

 さて、前回のプログラムは、[Tab]キーやマウスのクリックで入力フォーカスをほかの項目に移したときに、Validatingイベント・ハンドラを使って入力データが妥当かどうかを調べるものであった。画面は図1のようなもので、商品コードが8桁でない場合や、文字が含まれている場合にはエラー・メッセージが表示される。[キャンセル]ボタンをクリックしたり、[×]をクリックしたりしてウィンドウを閉じようとしたときの対処法も見た。

 読者からご指摘いただいたのは、[Enter]キーを押してフォームを閉じた場合の動作。その場合、フォームのデフォルト・ボタンである[OK]ボタンが選択されるのだが、Validatingイベントが発生せず、入力データのチェックが行われない。例えば、商品コードが8桁でなくても、[Enter]キーを押すと処理が先に進んでしまう。


図1 前回のプログラムの問題点
8桁の商品コードを入力するフォームを作成し、入力時チェックを行う。
  商品コードを入力し、[Tab]キーやマウスのクリックでほかの項目に進む。正しいコードが入力されると、商品名や在庫数が表示され、入力フォーカスが[払出数]テキストボックスに移る。
  商品コードは8桁固定となっているので、桁数が異なる入力に対してはエラー・メッセージを表示する。このとき、桁数の異なるデータがそのまま処理されてしまわないように、入力フォーカスの移動をキャンセルし、 で入力した内容が選択された状態になるようにする。ここまでは、問題はない。
  [OK]ボタンをクリックするのではなく、[Enter]キーを押してフォームのデフォルト・ボタンである[OK]を選択すると、テキストボックスのValidatingイベントが発生せず、入力チェックをすり抜けてしまい、 のエラー・メッセージが表示されない。

 フォームを閉じるときには、当然のことながら個々の入力データが互いに矛盾していないかどうかをチェックする必要がある。そこで再度チェックをかければエラー・データの混入は防げる。しかし、項目ごとの桁数チェックや不正な文字のチェックを素通りする場合があるのはどうも気持ちが悪い(前回はチェックが必要だとは言及したが、実際に例を示していなかったので、そういう場合があることを見落としていた)。

 これについては、できればフラグを使わず、美しく処理する方法を見つけようとしたのだが、残念ながらどうしてもフラグを使わざるを得なくなってしまった。といっても、トリッキーなことをやっているわけではないので、さほど違和感はないだろう。txtCode(商品コード・テキストボックス)のValidatingイベント・ハンドラと、btnOK([OK]ボタン)のClickイベント・ハンドラを以下のように書き換えるといい。

Dim flgCode As Boolean

' 商品コード・テキストボックスのValidatingイベント・ハンドラ
Private Sub lengthCheck(ByVal sender As System.Object, ByVal e As System.ComponentModel.CancelEventArgs) Handles txtCode.Validating

  Dim msg As String

  flgCode = False ' 最初はFalseにしておく
  lblAlert.Text = ""

  If ActiveControl Is txtCode Then
    Exit Sub
  End If

  Try
    If txtCode.TextLength <> 8 Then
      Throw New System.ApplicationException("商品コードは8桁です")
    Else
      Integer.Parse(txtCode.Text)
    End If
  Catch el As System.ApplicationException
    msg = el.Message
    ShowErr(e, msg)
  Catch ef As System.FormatException
    msg = "数字以外の文字が含まれています"
    ShowErr(e, msg)
  End Try

  flgCode = True ' Validationが成功したらTrueにする
End Sub

' [OK]ボタンのイベント・ハンドラ
Private Sub updateDB(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnOK.Click
  If flgCode = False Then
    txtCode.Select()
  Else
    Application.Exit()
  End If
End Sub

' エラーを表示するための共通の処理
Private Sub ShowErr(ByVal e As System.ComponentModel.CancelEventArgs, ByVal msg As String)
  e.Cancel = True
  lblAlert.Text = msg
  txtCode.Select(0, txtCode.TextLength)
End Sub
フォームで[Enter]キーを押して、デフォルト・ボタンが選択されたときにも対処できるコード
  フォームのスコープ内で変数flgCodeを宣言する。この変数を、入力時チェックが成功したかどうかを記憶しておくためのフラグとする。
  入力時チェックの開始時にはflgCodeをFalseにしておく。
  入力時チェックが成功したら、flgCodeをTrueにする。
  [OK]ボタンをクリックしたときに実行されるイベント・ハンドラで、flgCodeがFalseであれば、先に進まずに入力フォーカスを戻す。flgCodeがTrueであれば本来の処理をする(ただし、ここでは何もせずプログラムを終了するだけ)。

 ここではtxtCodeについてしか記述していないが、ほかのテキストボックスについても同様に記述すればよい。

 なお、デフォルト・ボタンをクリックしたときにはtxtCodeでValidatedイベントが発生するので、そちらを使うという手もあるかもしれないが、イベント・ハンドラが増えてしまうことや、ValidatingイベントとValidatedイベントが連続して発生したときの処理を記述するのも煩雑なので、その方法は使わなかった。

■小まめなチェックはかえって使いづらい

 ところで、話はわき道に入るが、対処法を考えて試行錯誤している間に、興味深い問題に遭遇したので、それについても簡単に記しておこう。

 私が最初に考えた方法は、すべてのテキストボックスの入力時チェックが成功して初めて、フォームのデフォルト・ボタンを表すAcceptButtonプロパティにbtnOKをセットするという方法だった。この方法であれば、入力時チェックが成功していないときにはフォームのデフォルト・ボタンがないので[Enter]キーを押しても先に進まない。入力時チェックと連動するので、なかなか気の利いたユーザー・インターフェイスだと思って作ってみた。

 だが、実際に使ってみると、ひどく操作性が悪い。[Enter]キーを押したときの動作が一貫していないからだ。入力にエラーがあるときには、何のフィードバックもなく、何が起こったかユーザーには分からない。もちろん、フィードバックを与えることもできるがコードがかなり複雑になる。

 一応、[OK]ボタンがデフォルト・ボタンであるか、そうでないかはボタンの外観を見れば分かるのだが、そこに注目して操作するユーザーはまずいないだろう。ここで必要なのは、エラーが起こった場合の対処であり、適切なフィードバックである。そういうわけで、[OK]ボタンはデフォルト・ボタンのままにしておき、項目ごとの入力時チェックに加え、ボタンがクリックされた時点でのチェックもきちんとやっておくという方法に立ち戻ったという次第である。

 ただし、項目ごとの入力時チェックも、厳密すぎるとかえって操作性を悪くする原因となる。例えば、ある項目の値の正当性がほかの複数の項目の値との関係で変わってくるような場合、入力時にいちいち複数の項目のチェックをすることは現実的ではない。処理があまりにも複雑になりすぎるし、ユーザーの立場から見ても、チェックが厳しすぎてなかなかほかの項目に進めないということにもなるからだ。

 従って、どうしてもその場で必要なチェック以外は項目の入力時ではなく、[OK]ボタンをクリックしたときにまとめてやった方がいいだろう。とりわけ、データ量が多く、定型的に入力を行う場合には、操作を中断する回数を少なくして、エラーがあればまとめて訂正するようにした方が効率的だし、入力時のフラストレーションもたまらない。

■何が何でも構造化例外処理というわけではない

 話が「Visual Basic 2005入門」からずいぶん遠く離れてしまったので、元の路線に戻ろう。

 もう1つのご指摘は、入力された文字列が数字のみかどうかを調べるのにParseメソッドを使っているが、TryParseメソッドを使った方がいいのではないかというもの。

 これはもっともな指摘だと思う。どちらでも同じことができるが、Parseメソッドでは数値に変換できない場合には例外がThrowされるが、TryParseメソッドではFalseが返される。つまり、Try〜Catch〜End Tryを使わなくてもIf文だけでエラー処理ができるというわけだ。対話型アプリケーションではまったく気にならないだろうが、TryParseメソッドを使った方が処理も速い*

*MSDN:Parse 対 TryParse アプリケーション サンプルに、両者の速度の差を測定するサンプル・プログラムがある。

 TryParseメソッドを使うとlengthCheckプロシージャのコードは次のようにスッキリしたものになる。

' 商品コード・テキストボックスのValidatingイベント・ハンドラ
Private Sub lengthCheck(ByVal sender As System.Object, ByVal e As System.ComponentModel.CancelEventArgs) Handles txtCode.Validating

  flgCode = False ' 最初はFalseにしておく

  lblAlert.Text = ""
  If ActiveControl Is txtCode Then
    Exit Sub
  End If

  Dim iCode As Integer    ' 作業用の整数

  If txtCode.TextLength <> 8 Then
    ShowErr(e, "商品コードは8桁です")
  ElseIf Not Integer.TryParse(txtCode.Text, iCode) Then
    ShowErr(e, "数字以外の文字が含まれています")
  End If

  flgCode = True  ' Validationが成功したらTrueにする
End Sub
Parseメソッドの代わりに、TryParseメソッドを使った例
Parseメソッドでは数値に変換できない場合には例外がThrowされるが、TryParseメソッドではFalseが返される。
  txtCode.Textの文字列を整数に変換し、iCodeに入れる。変換が成功しなかった場合はFalseが返されるので、ShowErrプロシージャを呼び出し、エラーを表示するとともに、入力フォーカスを元に戻す。

 Parseメソッドを使ったのは構造化例外処理を説明したかったためだが、前回のコードは、いわば「サンプルのためのサンプル」になりすぎてしまったきらいがある。「いついかなるときでも、可能な限りTry〜Catch〜End Tryを使うべし」と誤解されてしまったとしたら大変申し訳ない。If文で済むのであれば、無理をしてまで構造化例外処理を使う必要はない。

 サンプルのコードは機能の理解に重点を置いているので、カバーできない状況が出てくることもある。「こういう場合にはどう対処すればいい?」とか「こうすればもっと効率がいいのでは?」とか「こうした方が別の状況でコードを書くときにも有益では?」といったご指摘やご提案があれば、VB業務アプリケーション開発研究室 会議室やお便りでお知らせいただければありがたい(個別に返答するのは難しそうですが、ほかの人も困っているだろうと思われることや、私の言葉が足りず、誤解を招いたままにしておくとよくないと思われる場合には、今回のように取り上げたいと思います)。


 INDEX
  連載:VB 6ユーザーのためのこれならマスターできるVB 2005超入門
  第5回 データベースはじめの一歩
  1.ユーザビリティ小考 〜 前回の反省から
    2.サンプル・プログラム5 − コードなしのデータベース検索プログラム
    3.DataGridViewコントロールを配置する
    4.サンプル・プログラム6 − 検索のためのコードを記述する
 
インデックス・ページヘ  「これならマスターできるVB 2005超入門」


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