成績データ入力処理

DialogBox の成績処理で、データの一括入力に適したアプリケーションを作成します。
成績データの一括入力は、何百件ものデータをキーボードからタイプ入力します。
このようなときに何度もマウスに持ち替えたり、一件ごとに Button をクリックしなければならないようでは話になりません。
そこで成績データの一括入力に適したアプリケーションを作成してみましょう。
これから説明することは 「プログラムを完成させて、自分で実行する」 を行わないとその意味が解らないかも知れません。

前田稔(Maeda Minoru)の超初心者のプログラム入門

アプリケーション作成のポイントは、操作性を考慮したキーの使い方にあります。
ファンクションキーを使うのも有力な方法ですが、今回は「↑,↓,←,→,PageUp,Down」を使います。
  1. → キー
    次の EditBox に移ります。
    最後の EditBox(英語)のとき、現在画面表示されているデータをテーブルに追加します。
  2. PageUp または ↑ キー
    一件前のレコードを表示します。
  3. PageDown または ↓ キー
    次のレコードを表示します。
  4. ← キー
    前の EditBox に戻ります。
  5. TAB キー
    規定値として設定されている機能で、貼り付けられている EditBox や Button が順番にフォーカスされます。
    フォーカスしたくない Box は、プロパティでタブストップのチェックを外して下さい。

このような仕様にすると、一括入力作業中は「データ入力キーと制御キー」以外に触れる必要が無くなり効率良く入力できるようになります。
→ キーで「名前,番号,数学,国語,英語」と順番に移って行って、英語になると自動的にデータをテーブルに追加するにはどうすれば良いのでしょう。
その方法は EditBox の CALLBACK 関数を定義する「ウィンドウのサブクラス化」を使います。
サブクラス化するのは良いとして EditBox は五個もあります。
実務では、数十個の EditBox を使うこともあるでしょう。
EditBox の数だけ MySubProc() を作成して GetWindowLong() と SetWindowLong() を長々と並べなければならないのでしょうか?
もっとうまい方法を考えてみましょう。
ウィンドウのサブクラス化は成績処理で PageUp,Down を使うを参照して下さい。

五個の EditBox を制御する領域を配列で定義します。
EditID[5] は、EditBox のIDです。
DialogBox に貼り付けられた EditBox はそれぞれ固有の Handle を持っています。
g_hEdit[5] は EditBox 固有の Handle を格納する領域です。
Org_EWnd[5] は、元の CALLBACK 関数のエントリーポイントをセーブする領域です。
int         EditID[5]= { IDC_EDIT1,IDC_EDIT2,IDC_EDIT3,IDC_EDIT4,IDC_EDIT5 };
HWND        g_hEdit[5];         //EditBox Handle
WNDPROC     Org_EWnd[5];        //オリジナルプロシージャ

WM_INITDIALOG: でアプリケーションの初期化関数を呼びます。
LRESULT CALLBACK DlgProc(HWND hDlg, UINT msg, WPARAM wp, LPARAM lp)
{   switch(msg)
    {   case WM_INITDIALOG:
             InitApp(hDlg);
             break;

アプリケーションの初期化関数では、データファイルを入力してテーブルに格納します。
初期入力ではデータファイルが作成されていないので、テーブルは空で始めます。
EditBox の Handle と元の CALLBACK 関数のエントリーポイントをセーブします。
サブクラスの CALLBACK 関数には全て MySubProc を設定します。
    for(i=0; i<5; i++)
    {   g_hEdit[i]= GetDlgItem(hDlg,EditID[i]);
        Org_EWnd[i]= (WNDPROC)GetWindowLong(g_hEdit[i],GWL_WNDPROC);
        SetWindowLong(g_hEdit[i],GWL_WNDPROC,(LONG)MySubProc);
    }

五個の EditBox の Handle をせっかく配列に格納したのですから、テキストの設定と取り込みにはこれを使いましょう。
    SetWindowText(g_hEdit[0], buf);
    GetWindowText(g_hEdit[0], buf, 24);

さて肝心の MySubProc() はどうなるでしょう。
五個の EditBox の全てからこの共通の関数が呼び出されます。
と言う事は どの EditBox から呼び出されたのかを識別する 方法を考えなくてはなりません。
MySubProc() の hWnd には、呼び出した EditBox のハンドルが渡されてきます。
つぎのコードを見てください。
focus には現在フォーカスが設定されている EditBox の番号がセットされることを解っていただけるでしょうか?
    for(focus=0; focus<5 && g_hEdit[focus]!=hWnd; focus++);

これで全てが解決しました。
→ キーの処理は次のようになります。
SetFocus(g_hEdit[focus]) は g_hEdit[focus] にフォーカスを移すコードです。
フォーカスが移ったときテキストが選択状態になっていないと新規入力が面倒ですよね。
SendMessage() は EditBox のテキストを選択状態にするコードです。
    case VK_RIGHT:      //右矢印が来たら次の項目
        if (focus==4)  Append();
        focus= (focus+1)%5;
        SetFocus(g_hEdit[focus]);
        SendMessage(g_hEdit[focus],EM_SETSEL,0,MAKELONG(20,1));
        return 0L;

全角/半角を自動的に設定

全角入力/半角入力のときに、毎回キー操作で切り替えるのは結構手間がかかります。
そこで項目ごとに IME の全角/半角を自動的に設定することを考えます。
imm で始まる関数が多いのですが、これは Imput Method Manager の略です。
  1. imm.h を取り込んで、imm32.lib をリンクして下さい。
        #include    <imm.h>
        #pragma     comment(lib,"imm32.lib")
        
  2. IME を ON, OFF に設定するソースコードです。
    TRUE で ON に、FALSE で OFF に設定します。
        HIMC    hImc;
        ImmSetOpenStatus(hImc,TRUE);
        ImmSetOpenStatus(hImc,FALSE);
        
  3. hImc は入力コンテキストのハンドルで、EditBox のハンドルから ImmGetContext() で取得します。
        HIMC    hImc;
        hImc= ImmGetContext(GetDlgItem(hDlg,IDC_EDIT1));
        
  4. ImmGetContext() で取得したハンドルは ImmReleaseContext() で開放して下さい。
        if (ImmReleaseContext(edit,hImc)==0)
            MessageBox(NULL,"ImmReleaseContext Error","Error",MB_OK);
        
  5. 現在の ON, OFF 状態を取得するソースコードです。
        if (ImmGetOpenStatus(hImc)==TRUE)   //ON の状態
        if (ImmGetOpenStatus(hImc)==FALSE)  //OFF の状態
        

Edit Box にフォーカスが設定

Edit Box にフォーカスが設定されたことを検出する方法です。
フォーカスが設定されると親ウインドウに WM_COMMAND: として通知されます。
LOWORD(wp) が EditBox のIDで、HIWORD(wp) が通知コードです。
case WM_COMMAND:
    switch(LOWORD(wp))
    {   case IDC_EDIT1:
            if (HIWORD(wp)==EN_SETFOCUS)    //IDC_EDIT1 にフォーカスが設定された時の処理
            return 0;

【MEMO】

サブクラスで TAB キーを検出しようとしたのですが出来ませんでした。
システムで制御しているらしく?、Tab キーを押すと EditBox や Button が順番にフォーカスされます。
//    case VK_TAB:        //TAB は検出不可
    case VK_DOWN:       //下矢印が来たら次のレコード


DialogBox を表示した直後にフォーカスを設定しようと次のコードを書いたのですが状況は変わりませんでした。
何かキーをタイプするとフォーカスが設定されます。
WM_INITDIALOG: では、まだ DialogBox が表示されていない? とすれば当然かも知れません。
void    InitApp(HWND hDlg)
{   DWORD   dwBytes;
            :
    Disp();
//    SetFocus(hDlg);
//    SetFocus(g_hEdit[0]);
}


WM_DESTROY: でサブクラスを元に戻したのですが、果たして実行されるのでしょうか。
制御が渡されたことを確認しました。
Windows プログラムの最後の始末は、ここで行うのが最も良いようです。
    case WM_DESTROY:
         for(i=0; i<5; i++)
            SetWindowLong(g_hEdit[i],GWL_WNDPROC,(LONG)Org_EWnd[i]);
         PostQuitMessage(0);
         return TRUE;
         break;

超初心者のプログラム入門(Win32API C++)