Copyright(c) 2005 Object Oriented XBASE Forum


FoxProライクなキーボード入力の実現について(製作途上につきご迷惑をおかけいたします)

 FoxPro等のXBASE言語ツールはネイティブなデータベースドライバーだけが特徴では無く、エンドユーザオリエンティドなマンマシンインターフェースが高度に実装されている事が揚げられます。Enterキー、カーソルキーによる入力オブジェクト間の移動や細かい入力エントリーフィードの制御、ファンクションキーの連携等、マウスレス・高速・確実な入力を行える環境が標準で利用できるようになっています。さらにグリッドのフィールドも同等に動作するようになっています。FoxProをご利用の方は、説明するまでもありませんが、これらの仕様はコードをいちいち書く必要は無くTextBoxやグリッドのプロパティ設定フィールドで指示するだけと、いたって簡単です。
 さて、これらのいたって手軽な入力方式がC#に標準でついているかと言うと、「ありません」の一言になります。そこでFoxProでのキーボード入力をシミュレートできるような手段を考えないといけないのであります。このページでは、実装するうえでのコードの実験過程や、失敗した経緯も含めて記述してあります。なをデータバインドについては考慮してありません。

 Visual Studio 2008 C#で ↑↓カーソルキー、Enterキー、数値入力書式制御のプログラミング方法については、こちらで詳述しています。ご参照ください。

ご理解いただきたい事項

 このページに書かれていますコードは、FoxPro互換入力を標榜した基本的な動作原理についての考え方を提案するものであり、システムに組み込んだ場合に動作を保証するものではありません。さらに、コードが妥当であるか否かというレベルでもこのページの製作者は、なんら責任を負うものではありません予めご了承の上ご参照いただきますようお願い致します。
 注:) このページにてダウンロードできるサンプルフォームは、ZIPで圧縮してあります。ウイルスチェックはトレンドマイクロ社のウイルスバスターとコンピュータアソシエーツ社のeTrustにてチェックをおこなっております。入力参照のみのフォームなので実行したコンピュータへのアクセスは一切ありません。


FoxProライクなキー動作としてなにが必要か?

1.↑↓矢印でテキストボックスを抜ける
2.エンターキーでテキストボックスを抜ける
3.(1)(2)はタブ循環でも次の入力オブジェクトへ移動しないといけない
4.数値入力等 Picture句関連の互換性
5.入力文字数の制御

 Unicodeとshift-JISの問題解決 Unicodeは半角も2バイトで表現、Lenが半角1バイト全角2バイトというように取得できない  全角/半角かかわらずLen関数は文字数(DBCS->1文字 katakana Half->1文字)  となってしまう 上記はどうしても押さえておかないといけないと考えます。 入力にあたりValidationメソッドがありますが、これは入力について最終的に検証が 行われる方法で、キーの入力時点から動作を制御できません。 1つ1つキーの動作を拾わざる得ないというのが結論です。 TextBoxのイベントには Enter Leave Press Down Up等がありますのでこのイベントに 記述することになります。 入力フィールドから抜けた時にTab循環で次のオブジェクトにフォーカスを当てないといけない。これらはSystem.Window.FormクラスのメソッドのselectedNextControlを使用する。 ※Object.GetNextControl.Focus()はTab循環が先頭[ TabIndex = 0 ]から最後尾のTabへ 戻ることが出来ない(wrapが不可能)。よってこのメソッドは使用しない。


リターンキーおよび↓キーで次のオブジェクトへフォーカスが移動し ↑キーで前のオブジェクトへ移動する方法を考えます。

キーボードのキー値をtextBoxのKeyDownメソッドで取得するようにします。 FormにツールボックスからTextBoxオブジェクトを選択し貼り付けます。 textBox1 という名称のものが生成されます。 textBox1のプロパティダイアログでイベントを指定して、その中の KeyDownメソッド名をダブルクリックします。自動的にtextBox1_KeyDownメソッドが 作成されます。

private void textBox1_KeyDown(object sender,System.Windows.Forms.KeyEventArgs e)

この関数のパラメータのうちKeyEventArgs eが 押されたキー情報を持っています。
tring sKey; sKey =
e.KeyCode.ToString(); sKeyには、キーコードが文字型に変換された値がストアされます。
リターンキー : "Enter"
↓キー  : "Down"
↑キー  : "Up"
さらに、Tab循環にて前後のオブジェクトへ移動するために、TabIndexから次の循環オブジェクトに対して SetFocusを行う仕組みを考えないといけません。 ここでは、SelectNextControl( ・・・)を採用します。GetNextControl().Focus()と いう方法も実験してみましたが、Tab循環のIndex=0から循環をさかのぼり最後尾のオブジェクトへの 移動が行えないことがわかりました。(tab循環のWrapが出来ない、実行すると致命的なエラーとなる)

[資料]---------------------------------------------------------------
public function SelectNextControl
(ctl : Control, forward : Boolean, tabStopOnly : Boolean, nested : Boolean, wrap : Boolean   ) :
Boolean; パラメータ
ctl 検索を開始する位置にある Control 。
forward タブ オーダー内を前方に移動する場合は true 。後方に移動する場合は false 。
tabStopOnly TabStop プロパティが false に設定されているコントロールを無視する場合は true 。  それ以外の場合は false 。 nested 入れ子になった (子コントロールの子) 子コントロールを含める場合は true 。  それ以外の場合は false 。
wrap タブ オーダーの最後のコントロールに到達した後、タブ オーダーの最初のコントロールから  検索を続行する場合は true 。それ以外の場合は false 。
戻り値
コントロールがアクティブにされた場合は true 。それ以外の場合は false 。

〆-------------------------------------------------------------------

これらを総合して KeyDownメソッドに記述するコード例です

[コード例]-----------------------------------------------------------

private void textBox1_KeyDown(object sender, System.Windows.Forms.KeyEventArgs e)
{
        string sKey;
        sKey = e.KeyCode.ToString();
        if(sKey=="Enter" || sKey =="Down"){
                this.SelectNextControl(this.textBox1,true,true,false,true);}
        else if(sKey=="Up"){
                this.SelectNextControl(this.textBox1,false,true,false,true);}
        }
}
※ thisは省略できます。(thisを入れるとインテリセンスで書くときに手間がかからず便利です)

〆------------------------------------------------------------------

 これでとりあえずリターンキーとカーソル上下キーでテキストフィールドを抜けることが出来るように なりました。しかし、お気づきのようにいちいちそれぞれのオブジェクトのKeyDownイベントにコードを 書くというような面倒なことは出来ません。一気にクラス化する手もありますが、判り易く共通の メソッドを作成してみます。
SelectNextControlの第1パラメータにタブ循環の起点オブジェクトを書かないといけません。つまり 共通メソッドを構成するうえで呼び出し元のオブジェクトが"誰"なのかを取得しないといけません。 Foxですと単に this でオブジェクト自分自身を表現出来ましたが、C#は、ちょっと簡単ではありません (この部分は私が知らないだけで、目から鱗の簡単な記述方法があるのかもしれません・・)
KeyDown( object sender,・・) の senderがその情報を保持しています。 senderからオブジェクトが"誰"なのかを引き出すためには TextBox tx = (TextBox)sender; と記述します。( txは自由に命名して構いません) めんどくさぁ〜 って言ってしまいます。我慢我慢・・

[コード例]----------------------------------------------------------

private void extEntKS(object sender, System.Windows.Forms.KeyEventArgs e)
{
        string sKey;
        sKey = e.KeyCode.ToString();
        TextBox tx = (TextBox)sender;
        if(sKey=="Enter" || sKey =="Down"){
                this.SelectNextControl(tx,true,true,false,true);}
        else if(sKey=="Up"){
                this.SelectNextControl(tx,false,true,false,true);}
        }
}

[実装例]----------------------------------------------------------
テキストボックス textBox1 textBox2 がFormに設置されているとします。

private void textBox1_KeyDown(object sender, System.Windows.Forms.KeyEventArgs e)
{
        this.extEntKS(sender, e);
}
private void textBox2_KeyDown(object sender, System.Windows.Forms.KeyEventArgs e)
{
        this.extEntKS(sender, e);
}
※ なをこのように個々の入力オブジェクトにてKey値を取得せず。FormのKeyDownEventで取得設定すると総てのコントロールに自動的に処理が行き渡ります。工夫してみてください。

〆------------------------------------------------------------------


 


Unicode-ShiftJIS変換による文字数(LEN)の取得方法

テキストボックスに入力された文字列(Unicode)がShift-JIS文字列の何文字に あたるかを検証する方法について考えます。

Shift-JISとして文字数を数えるタイミングは入力が終了し、フォーカスが離れるときをトリガーとして数えます。
textBox1のプロパティウインドウでLeaveイベントをダブルクリックします。
private void textBox1_Leave(object sender, System.EventArgs e)が 作成されます。
Shift-JISへの変換の一方法として byte [] sjis = System.Text.Encoding.GetEncoding("shift-jis").GetBytes(this.textBox1.Text);
配列に変換値を代入し、その配列のバイト数を求めます。 配列のバイト数の求め方は 配列変数.Lengthで求められるので sjis.Lengthを利用するとShift-JISでの文字バイト(半角1バイト/全角2バイト相当)のものが取得できます。

UnicodeとShift-JISの文字数の違いを確認してみる

private void textBox1_Leave(object sender, System.EventArgs e)メソッドに sjisへの変換処理を作成しました。実際にこの結果を表示して文字数のカウントの違いを 確認してみます。 数値型を文字列に変換するのは、ToString()を利用します。 Unicodeでの文字数を得るには、textBox1.TextLength(または〜.Text.Length)とします。 そして数値型を文字型に変換するのは、textBox1.TextLength.ToString()と書きます。 Shift-JISは、前述のsjis.Lengthを利用してsjis.Length.ToString()と書きます。 これをメッセージボックスにて表示して見えるようにLeaveメソッドを作成してみます。
[コード例]--------------------------------------------------------------

private void textBox1_Leave(object sender, System.EventArgs e)
{
        string cText = this.textBox1.Text;
        byte [] sjis = System.Text.Encoding.GetEncoding("shift-jis").GetBytes(this.textBox1.Text);
        MessageBox.Show("Shift-JISでの文字数 =" + sjis.Length.ToString()
            +"\r\n Unicodeでの文字数 =" + this.textBox1.TextLength.ToString());
}

※ thisは書略できます。
〆----------------------------------------------------------------------

このコードをコンパイルして実行してみてください。半角全角取り混ぜて入力を 行ってみて、実際の文字数がどのようにカウントされるのか確認してください。 次に、textBox1のプロパティにMaxLengthがあります。ここに適当に 数値(たとえば10)というように設定します。コンパイルした後に

1. 全角のみで入力
2. 半角のみで入力
3. 全角半角取り混ぜて入力 というように試してみてください。

データベースが固定長のShift-JISで設計されている場合、データのセットに注意を 払わないといけないことが理解できると思います。 データベーステーブルがShift-JISで10バイトであったとします。MaxLengthの値を10に 設定すると10バイトではなく10文字となります。これでは桁あふれを起こしてしまいます。 エラーチェックをどのようにするか、個々のプログラムで変わると思いますが、Unicode 対応のデータベースフィールドであればなんら問題がありませんが、固定長テキストで の設計ですと、ここは重要なポイントになります。



数値入力テキストボックスの作成

入力キー制御の基本として整数値の入力方法を考えます。

1. 半角数字 0 - 9 のみ入力
2. マイナス符号の入力
3. 書式制御表示

コードの記述より前にIME プロパティの設定を忘れないこと TextBoxObjectのIMEコントロールプロパティをDisableにしてFEPが起動しないようにします。 また、入力桁を設定します。桁数はMaxLengthに設定します。 キーの入力値を取得するためにTextBoxオブジェクトのKeyPressイベントを使用します。
private void textBox1_KeyPress(object sender, System.Windows.Forms.KeyPressEventArgs e)
このイベントでは、キーボードのどのキーが押されたか KeyPressEventArgs e で取得できます。 半角数字のみのキーを通すには e.KeyCharで取得するとともに、そのキー値をTextBox.textに反映させるかどうかを e.Handledにセットする真偽値で指定します。
e.Handle = false;でキー値を反映 e.Handled = true;でキーは無視されます。
「-」マイナス符号の入力のため入力中のカーソル位置が先頭で且つe.KeyChar = '-'かどうかを取得するために this.textBox.SelectionStartを利用します。SelectionStartはカーソルキーの位置をint数値で返したり、また設定も 出来ます。文字列の先頭に位置する場合は 0 になります。.Text.StartsWith("検索文字")は、文字列の先頭に"検索文字"が有るかを調べます。 ここまでのところをまとめてメソッドを書いてみます。
※ CTRL+C CTRL+Vによるカットアンドペーストはこのコードでは動作が制限されます。KeyDownメソッドで別途CTRL+CとSelectedTextがヌルでないかを判断してCopy()を発行する必要があります。
[コード例]---------------------------------------------------------------------------

private void textBox1_KeyPress(object sender, System.Windows.Forms.KeyPressEventArgs e)
{
        if( (e.KeyChar >= '0' && e.KeyChar <= '9') || e.KeyChar=='\b'){
                e.Handled = false;}
        else if (e.KeyChar == '-' && this.textBox1.SelectionStart==0 && !this.textBox1.Text.StartsWith("-")){
                e.Handled = false;}
        else{
                e.Handled = true;}
}

e.KeyChar== '\b' は[BackSpace]キーでキーが動くようにするためです。
〆----------------------------------------------------------------------------------

 


ただ今製作中につきご迷惑をおかけいたします。[VFPToolkit利用方法メインページへ戻る]