Visual Basic基礎と実践

カーソル移動の実装から
Windowsを透視する


初音 玲 HATSUNE, Akira



Visual Basicは、Windowsのさまざまな機能を上手にまとめることにより、それまで必要だった、“膨大な資料の読破”というインターフェイスデザインとは無縁の作業を行なわなくてもよいように工夫した言語だ。しかし、Visual BasicといえどもWindowsに備わっていない機能を実現するのは不可能である。不可能な仕様を追い求めないためには、Windowsのことを知らなければならない。今回は、Visual Basicで簡単に実現できそうでできない機能を紹介することで、Visual Basicプログラミングのツボを探ってみる。

WindowsとVisual Basicの関係

 Visual Basicは、Windowsの複数の機能をまとめて、自分の機能としてプログラマに公開している(図1)。たとえば、Visual Basic入門書の最初の最初に出てきそうなフォームオブジェクトだけの実行ファイルも、Windowsの提供している機能だけで実現しようとしたら、Visual Basicユーザーには想像できないくらいの試行錯誤が必要だ。

図1:Windowsメッセージの流れ
図1:Windowsメッセージの流れ

[Enter]キーによるカーソル移動を考える

 さて、Windowsの標準GUIに基づいていないと、どれくらい処理を記述しなければいけないか、[Enter]キーによるカーソル移動を例に考えてみる。ここで説明する手間を吸収できるだけの開発工数と期間があるのならば、システム仕様として実装しても良いだろう。しかし、Windowsの新しいバージョンでも問題なく動くかどうかは別問題だ。また、この仕様を採用するということは、そのシステムが他の同様のシステムに比べて、いつも半歩立ち後れてしまう。もしかしたら、いつも多く費用がかかるということまで甘んじて受けなければいけないということを肝に銘じて欲しい。それが、果たしてお客様のためになるかは、賢明な方ならお分かりになるだろう。

テキストボックスに対応するテキストボックスに対応する

  Sample:Text.vbp

 テキストボックスしか使わない画面であれば、[Enter]キーによるカーソル移動を実現するのは、簡単だ(図2)。
 フォームオブジェクトのKeyPreviewプロパティをTrueに設定して、キー入力をフォームオブジェクトでも認識できるようにしたあとに、Form_KeyDownプロシージャで、リスト1のようにコーディングすれば良いのだ。これは、フォーカスがあるコントロールを使用不可(EnabledプロパティをFalse)にすれば、フォーカス可能なコントロールでTabIndexプロパティが次の値のコントロールに自動的にフォーカスが移動することを利用している。
 たったこれだけのコーディングで問題は解決するのだ。

図2:テキストボックスの[Enter]キー移動
図2:テキストボックスの[Enter]キー移動

リスト1:[Enter]キーによるカーソル移動
Private Sub Form_KeyDown(KeyCode As Integer, Shift As Integer)
  Dim ctlFocusObject As Control

  If KeyCode = vbKeyReturn Then
  ' 現在のアクティブコントロールを取得
    Set ctlFocusObject = Me.ActiveControl
  ' アクティブコントロールを使用不可にして、
  ' 次のコントロールにフォーカスを移動させる
    ctlFocusObject.Enabled = False
  ' もともとフォーカスのあったコントロールの使用可に戻す
    ctlFocusObject.Enabled = True
  End If
End Sub

Caution! POINT-1
日本語入力中のときは、事前に文字を確定しなければならない。つまり【文字確定】→【カーソル移動】となるために、[Enter]キーを2回連続して押さなければならない、ということだ。もし、日本語確定と同時にカーソルを移動させたいのであれば、IME関係のAPIを駆使しなければいけないだろう。

コマンドボタンでも使うコマンドボタンでも使う

  Sample:Cbutton.vbp

 テキストボックスのみの画面では、簡単に[Enter]キーでカーソル移動が可能だった。しかし、テキストボックスのみの画面というのは実際にはありえないだろう。入力した値を眺めて楽しむのなら別だが、大抵は入力した値を使って検索したり更新したりするはずだ。そのような操作を行なうためのGUIは、いろいろあるが、今回はコマンドボタンで行なってみる(図3)。
 テキストボックスにフォーカスがあるときは、[Enter]キーによりカーソルが移動してゆくが、コマンドボタンにフォーカスが移ると、[Enter]キーを押しても次のコマンドボタンにフォーカスが移動しない。これは、コマンドボタンにとって、[Enter]キーとは、マウスで左クリックしたときと同等の意味があるからだ。
 拡張したサンプルでは、最後のテキストボックスで[Enter]キーを押せば、先頭のテキストボックスに戻ってくる。このように、TabStopプロパティを活用すれば[Enter]キーで動き回る(実際は[TAB]キーで動き回る)範囲を限定することが可能だ。そして、TabStopプロパティは、読み書き可能なプロパティなので、実行時の状態により、TabStopプロパティを動的に変化させることもできる。

図3:コマンドボタンの[Enter]キー移動
図3:コマンドボタンの[Enter]キー移動

How To Extend-1
[Enter]キーでコマンドボタンにカーソルが移動しないようにサンプルを変更。これは、コマンドボタンのTabStopプロパティをFalseにすることで実現可能だ。

Caution! POINT-2
コマンドボタンの上でも[Enter]キーで次のコントロールに移動したいという要望は、危険ポイントだ。この要望は、中途半端にWindowsを知っているときに出てきやすい。つまり[TAB]キーの代わりに[Enter]キーを使ってしまおうという発想だ。テンキー部分のみですべてをまかなおうとすれば、[TAB]キーの代わりに[Enter]キーをということになりかねない。
しかし、コマンドボタンで[Enter]キーを特別に認識することはできないのだ。サンプルプログラムのcmdEnter_KeyDownおよびcmdEnter_KeyPressプロシージャにForm_KeyPressプロシージャのロジックをコピーして確認して欲しい。そもそも[Enter]キーを押したところで、KeyDownイベントもKeyPressイベントも発生してくれないのだ。もちろん、他のキーならば、きちんとイベントは発生する。
[TAB]キーの代わりを[Enter]キーでまかなうことは、Visual Basicでの開発を前提とするならば、制限付きの仕様ということになる。

複数行テキストボックスを使う複数行テキストボックスを使う

  Sample:Mline.vbp

 テキストボックスは、MultiLineプロパティをTrueにすることにより、複数行のテキストを入力することができる(図4)。普通、テキストを改行するには[Enter]キーを使う。そう[Enter]キーでカーソル移動という仕様と相反する仕様がはじめから実装されているわけだ。
 テキストボックス内の改行を[CTRL]+[Enter]というような仕様にすれば大丈夫だと思えるが、入力したキーを無視することができるKeyPressプロシージャで[CTRL]キーの状態は取れない。よって[Enter]キーと[CTRL]+[Enter]を区別して、前者のときは、キーを無視することはできないのだ。
 拡張したサンプルでは、テキストボックスで、“/(半角のスラッシュ)”を押すと改行する。もちろん、その他の文字でも良い。

図4:[Enter]キーで複数行を移動
図4:[Enter]キーで複数行を移動

How To Extend-2
テキストボックスである特定の文字を入力すると改行するという仕様にする。
テキストボックスのKeyPressプロシージャで、[Enter]キーの無視と特定文字の改行化を記述する(リスト2)。

リスト2:
Private Sub txtEnter_KeyPress(Index As Integer, KeyAscii As Integer)
  If KeyAscii = vbKeyReturn Then
    KeyAscii = 0
  ElseIf KeyAscii = Asc("/") Then
    KeyAscii = vbKeyReturn
  End If
End Sub

Caution! POINT-3
[Enter]キー以外に改行する方法があると自然に気がつく人がどれくらいいるだろうか。ヘルプに記述したりツールチップで表示したりする以外に、フォームのあいている部分に、ずばり「テキストボックス内での改行は、“/(半角スラッシュ)”です」と記述するなどの工夫が必要だろう。

Caution! POINT-4
テキストボックスに入力したい文字以外を特定文字にするが、もし、その特定文字をテキストボックスに入力したくなった場合には、プログラムの変更が生じる。

[PF]キー対応を考える

 もうひとつの例は[PF]キーの活用だ。[Enter]キーの処理は、キーを入力しているフォーム内に限った話だが、[PF]キーでは、キーが押されたときに[PF]キーを受け取る対象が、必ずしもアクティブなフォームではないという難しさがある。実現方法は似ているが、難しさのポイントが違うのだ。

[PF]キーを検出する[PF]キーを検出する

  Sample:Pf.vbp

[PF]キーをフォームオブジェクトで検出するのは、[Enter]キーでカーソル移動をする処理の応用になる(図5)。
 フォームオブジェクトのKeyPreviewプロパティをTrueに設定して、キー入力をフォームオブジェクトでも認識できるようにしたあとに、Form_KeyDownプロシージャで、リスト3のようにコーディングすれば良いのだ。
 この仕様は、ごくごく自然に動作してくれる。ただし、図5にあるように、ショートカットキーがあらかじめ割り当てられているときは、注意が必要だ。必ずメニュー項目と折り合いをつけるか、Windowsの標準で決まっている[PF]キー(表1)はそのまま使うようにするのが良いだろう。
 また、コマンドボタンを左クリックしたときと同じ動作をさせるのであれば、コマンドボタンへのSetFocusメソッドの実行も忘れてはいけない。コマンドボタン自身にフォーカスを移すこと自体にはあまり意味がないかもしれないが、もともとのコントロールからフォーカスが外れる(LostFocusイベントが起こる)ことが重要なときがある。

図5:[PF]サポートキー
図5:[PF]サポートキー

リスト3:
Private Sub Form_KeyDown(KeyCode As Integer, Shift As Integer)
  If KeyCode = vbKeyF1 Then
  ' 実行
    Call subExecute
  ElseIf KeyCode = vbKeyF3 Then
  ' 取消
    Call subCancel
  ElseIf KeyCode = vbKeyF7 Then
  ' 前頁
    Call subPrev
  ElseIf KeyCode = vbKeyF8 Then
  ' 後頁
    Call subNext
  ElseIf KeyCode = vbKeyF10 Then
  ' 確定
    Call subCommit
  End If
End Sub

Caution! POINT-5
コマンドボタンを左クリックしたとき、モーダルウィンドウなどを表示さえしていなければ、そのフォームがフォーカスをもっていなくてもフォームをアクティブにし、かつ、コマンドボタンにもフォーカスが移されるだろう。
 この動作を[PF]キーで代替することは、大変困難だ。それは、Visual Basicの機能を使っている限り、どうがんばってもキー入力を受け取るのは、アクティブなウィンドウだからだ。その制限を打ち破るには、Windowsの“キーが押された”メッセージを直接横取りして、無理矢理所定のウィンドウに投げつけてやる必要がある。VB5以降では、Address Ofなどを使って、ロジックの一部をサブクラス化して、Windowsメッセージをフックすることが可能(図6)だが、デバグの手間及び処理効率を考えると、文化オリエント株式会社から発売されているSpyWorksProのDWSHK32.ocxなどを使うのが良いだろう。

図6:Windowsメッセージの流れ
図6:Windowsメッセージの流れ

アクセスキーで代用するアクセスキーで代用する

  Sample:Akey.vbp

 コマンドボタンには、アクセスキーを設定することができる。アクセスキーは、[ALT]キーと一緒に押すことで、マウスを使わずにダイレクトにフォーカスを移動させる方法だ。

 アクセスキーの設定は、コマンドボタンのCaptionプロパティに、“&(アンパサンド)”と一緒に[実行(&1)]のように指定すれば良い。このように指定すると、画面上には、[実行( 1)]のように表示される(図7)。
 また、[PF]キーを横取りするのとは異なり、Windowsが予約済みの[PF]キーもそのまま動作する。図6では、[PF10]キーを押したときの姿だが、[ALT]キーを押したときと同じ動作が実現できているのが分かる。
 また、Captionプロパティの設定だけで、キーボード対応のためのコードを1行も書いていないことも注目に値する。ただしCaution!のPOINT-5のことも忘れてはならない。

図7:アクセスキーによる実装
図7:アクセスキーによる実装

ショートカットを利用するショートカットを利用する

  Sample:Skey.vbp

 アクセスキーを使えば、キーボードから手を放さずに、素早くコマンドボタンをクリックすることが可能だ。しかし[ALT]キーを一緒に押すことに抵抗を感じる人もいるだろう。そのようなときは、メニューを使って、ショートカットを利用するのが良いだろう(図8)。
 ショートカットを使ったときは、[PF]キーを検出するときと同じようにロジックの記述が必要(リスト4)だが、メニューそのものの機能として[PF10]を選べないように工夫されていたり(図9)、同一のショートカットを設定できないようにチェック(図10)してくれるので、プログラム時の気苦労はかなり軽減される。ただしCaution!のPOINT-5の内容も忘れてはならない。

図8:ショートカットによる代用
図8:ショートカットによる代用

図9:ショートカットの種類
図9:ショートカットの種類
図10:同一ショートカット設定字のエラーメッセージ
図10:同一ショートカット設定字のエラーメッセージ

リスト4:
Private Sub mnuPcKey_Click(Index As Integer)
  If Index = 0 Then
  ' 実行
    Call subExecute
  ElseIf Index = 1 Then
  ' 取消
    Call subCancel
  ElseIf Index = 2 Then
  ' 前頁
    Call subPrev
  ElseIf Index = 3 Then
  ' 後頁
    Call subNext
  ElseIf Index = 4 Then
  ' 確定
    Call subCommit
  End If

さいごに

 さて、具体的にWindowsの標準的なGUI以外の仕様を満足する手法を紹介してきた。
「これくらいの手間ですむのか」または「面倒だな」と人によって感想はさまざまだろう。しかし、標準GUIよりも確実にコードの記述量が増えることは、揺るぎない事実だ。コードの記述量が増えるということは、それだけコーディングおよびテストに時間も必要だし、他の人が見たときに理解する時間もかかるということである。そして、Windowsに新しい操作性が付加されたときに、自分で作ったGUIがそのWindowsの仕様とぶつからないという保証はどこにもないということも考慮し、「手間暇かけて、お客様のためにがんばって実現しました」ということを理解してもらうのが重要なのだ。


[動作確認環境]-----------------------------------------------
 Windows 95 OSR2.1(Non ActiveDesktop)
 Visual Basic 6.0(SP2)


VB Magazine ライブラリ| Visual Basic WorkGroup
int21 ホームページ| PCDN ホームページ

PCDN
Copyright (c) 1998 int21 CorporationAll Rights Reserved.
For questions or comments, please send mail to: pcdn@int21.co.jp