成績処理で PageUp,Down を使う

DialogBox の成績処理で「PageUp,Down(↑キー,↓キー)」を使えるようにします。
成績データの入力や修正は、キーボードからタイプして行います。
一括入力処理では、何百件ものデータをまとめて入力することもあり、このようなときに 何度もマウスに持ち替えなければならないようでは、大変な効率の低下をもたらします。
そこでマウスに頼らなくてもキーボードから制御する手段を考えます。

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

番号の EditBox にフォーカスがある時、↓キー(↑キー)が押されると次のレコード(前のレコード)を表示することを考えます。
キーを検出するために DlgProc() に次のコードを書いてみましょう。
LRESULT CALLBACK DlgProc(HWND hDlg, UINT msg, WPARAM wp, LPARAM lp)
{   switch(msg)
    {   case WM_KEYDOWN:
             制御が渡されたことを確認するコード
             break;

ところが幾らキーを押しても WM_KEYDOWN が確認されません。
EditBox は親(DialogBox)とは独立した子供ウィンドウ(?)で、自分のウィンドウハンドルを持っています。
このウィンドウのプロシージャで WM_KEYDOWN を捕まえなくてはいけません。
それがウィンドウのサブクラス化と呼ばれる方法です。
ウィンドウのサブクラス化は別名「メッセージの横取り」とも呼ばれます。

サブクラス化の方法を説明します。

  1. GetWindowLong() で元々のプロシージャのアドレスを取得する。
  2. サブクラス化の実行。
    SetWindowLong() で元のプロシージャに代わるプロシージャを設定する。
  3. 新しいプロシージャでは、自分で処理しないものは CallWindowProc() で元のプロシージャに押しつける。
    自分で処理したものはゼロを返す。
  4. 必要がなくなったら元のプロシージャに戻す。

それでは詳しく説明しましょう。
DialogBox を操作するために、ハンドルをグローバル領域に保存します。
Org_EWnd は元々のプロシージャのアドレスを保存する領域です。
HWND        g_hDlg;
WNDPROC     Org_EWnd;

WM_INITDIALOG: で元々のプロシージャのアドレスを保存して、新しいプロシージャを設定します。
ReadData() は前と同じ成績ファイルを入力してテーブルに格納する関数です。
LRESULT CALLBACK DlgProc(HWND hDlg, UINT msg, WPARAM wp, LPARAM lp)
{
    switch(msg)
    {   case WM_INITDIALOG:
             g_hDlg= hDlg;
             ReadData(hDlg);
             Org_EWnd= (WNDPROC)GetWindowLong(GetDlgItem(hDlg,IDC_EDIT1),GWL_WNDPROC);
             SetWindowLong(GetDlgItem(hDlg,IDC_EDIT1),GWL_WNDPROC,(LONG)MySubProc);
             break;

一番目の Edit Box(番号) に設定する新しいプロシージャです。
VK_DOWN のとき次のレコードを、VK_UP のとき前のレコードを表示します。
それ以外のキーのときは元のプロシージャに任せます。
LRESULT CALLBACK MySubProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp)
{
    switch (msg)
    {   case WM_KEYDOWN:
             if ((int)wp == VK_DOWN)    //下矢印が来たら次のレコード
             {  g_RNO++;
                Disp(g_hDlg);
                return 0L;              //メッセージの処理をしたら0を返す
             }
             if ((int)wp == VK_UP)      //上矢印が来たら前のレコード
             {  g_RNO--;
                Disp(g_hDlg);
                return 0L;
             }
             break;
        default:
             break;
    }
    //自分で処理しないものは元のプロシージャにやってもらう
    return(CallWindowProc(Org_EWnd,hWnd,msg,wp,lp));
}

プログラムの終了時に(必要がなくなったら)元のプロシージャに戻します。
    case WM_DESTROY:
         SetWindowLong(GetDlgItem(hDlg,IDC_EDIT1),GWL_WNDPROC,(LONG)Org_EWnd);
         PostQuitMessage(0);
         return TRUE;

【演習】

数百件のデータを連続して追加入力する事を考えて見ましょう。
一件入力するごとにマウスに持ち替えて「追加」ボタンをクリックするようでは話になりません。
五番目の Edit Box(英語) で↓が押されたとき、自動的にレコードをテーブルに追加してから、 表示をクリアして次の入力に移ります。
ただし、入力データにエラーがあるときは、ブザーを鳴らして現在のレコード編集を続けます。

参考のために、私のプログラムのプロトタイプ宣言を示します。
LRESULT     CALLBACK DlgProc(HWND, UINT, WPARAM, LPARAM);
LRESULT     CALLBACK MySubProc(HWND, UINT, WPARAM, LPARAM);
void        ReadData(HWND hDlg);
void        Disp(HWND hDlg);
HRESULT     Update(HWND hDlg);
HRESULT     Delete(HWND hDlg);
HRESULT     Append(HWND hDlg);
HRESULT     Check(HWND hDlg);

五番目の Edit Box(英語) にもサブクラスを設定するには、一番目のときと同じように IDC_EDIT5 にも 新しいプロシージャを設定します。
    Org_EWnd2= (WNDPROC)GetWindowLong(GetDlgItem(hDlg,IDC_EDIT5),GWL_WNDPROC);
    SetWindowLong(GetDlgItem(hDlg,IDC_EDIT5),GWL_WNDPROC,(LONG)MySubProc2);

MySubProc() と同じ要領で作成して下さい。
LRESULT CALLBACK MySubProc2(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp)
{
    switch (msg)
    {   case WM_KEYDOWN:
               :
               :
    }
    //自分で処理しないものは元のプロシージャにやってもらう
    return(CallWindowProc(Org_EWnd2,hWnd,msg,wp,lp));
}

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