20章 ダイアログボックス(その3)

<html>

<head>
<meta http-equiv="Content-Type" content="text/html; charset=shift_jis"></meta>
<title>20章 ダイアログボックス(その3)</title>
</head>

<body bgcolor="WHITE">
<font size="5">20章 ダイアログボックス(その3)</font>
<hr>
<p>
 前回は、モーダルダイアログボックスの作成について学びました。今回は、モーダルダイアログボックスの復習と、前回説明できなかったサンプルプログラムのダイアログプロシージャについて学びます。実際は、コントロールの簡単な操作の学習です。
<hr>
<p>
<b>・モーダルダイアログボックスの復習</b>
<p>
 モーダルダイアログボックスと言うのは、それが表示されている間、親ウインドウは非アクティブになっていているダイアログボックスの事です。そして、ダイアログボックスとは、コントロールをより簡単にそして有効に使うために特化したウインドウの事です。
<p>
 モーダルダイアログボックスの作り方の概略は、前回の予習で示したように以下の通りでしたね。
<p align="CENTER">
<table border="1" bgcolor="#F0F0F0">
<tr><td>
・ダイアログテンプレートの作成<br>
・ダイアログボックスプロシージャの定義<br>
・専用の関数でモーダルダイアログボックスを作成する
</table>
<p>
 ダイアログテンプレートは、リソーススクリプトという言語でダイアログボックスやコントロールのレイアウトを定義するものです。そして、実際は、リソーススクリプトでダイアログボックステンプレートをテキストエディタで記述する必要は無く、ビジュアル環境で画面を見ながら作成する事が出来ます。まさに、RADツール感覚です。
<p>
 ダイアログボックスプロシージャは、ウインドウプロシージャとよく似ていて、ダイアログボックスに対するメッセージを処理するコールバック関数です。ただ、戻り値がBOOL型で、WM_INITDIALOGメッセージに対する場合を除いて、メッセージを処理した場合はTRUE、メッセージを処理しなかった場合はFALSEを返します。また、ウインドウプロシージャのように、DefWindowProc関数に処理しなかったメッセージを渡したりしません。そして、モーダルダイアログを閉じるときは、EndDialog関数を用います。
<p>
 そして、モーダルダイアログボックスは、DialogBoxParam関数を代表とする専用の関数やマクロで作成します。この関数やマクロの戻り値は、EndDialog関数に渡されたものです。
<p>
 以上、ここまではいいですよね。簡単ですよね。この簡単なのがモーダルダイアログボックスの良いところです。そして、簡単だから利用価値が少ないとかでなく、物凄く使い道があるのがモーダルダイアログボックスなのです。
<hr>
<p>
<b>・サンプルプログラムのダイアログプロシージャを綿密に調べてみよう</b>
<p>
 19章では、モーダルダイアログボックスのダイアログプロシージャのスケルトンは説明しましたが、実際のサンプルのダイアログプロシージャの説明をしませんでした。今回は、その説明をしましょう。
<p>
<p>
<table border="1" bgcolor="#F0F0F0">
<caption align="BOTTOM">リスト20-1(DlgProc1.c)
<tr><td>
<pre>
/*
  『C言語で始めるWindowsプログラミング』
   18章のサンプルプログラム
                   Programmed by Y.Kondo

/


#define STRICT

#include &lt;windows.h&gt;
#include &lt;stdio.h&gt;
#include "DlgProc1.h"
#include "chap18.h"
#include "resource.h"

/* このファイル内でのみ用いられる変数 */
static TLineData *gVarLineData;

/* このファイル内でのみ用いられる関数のプロトタイプ宣言 */
static BOOL Wm_InitDialogProc(HWND,HWND,TLineData*);
static BOOL Wm_CommandProc(HWND,WORD,WORD,HWND);
static BOOL IsNumber(HWND);

/* ダイアログプロシージャ */
BOOL CALLBACK ModalDialogBoxProc(HWND hwnd,UINT msg,WPARAM wparam,LPARAM lparam)
{
  switch(msg)
   {
       case    WM_INITDIALOG:
           return  Wm_InitDialogProc(hwnd,(HWND)wparam,(TLineData*)lparam);
       case    WM_COMMAND:
           return  Wm_CommandProc(hwnd,HIWORD(wparam),LOWORD(wparam),(HWND)lparam);
   }
   return  FALSE;
}

static BOOL Wm_InitDialogProc(HWND hwnd,HWND hwndFocus,TLineData* LineData )
{
  int         c;
   int         EditControl[]={IDC_XS,IDC_YS,IDC_XE,IDC_YE};
   char        strbuf[100];

  /*  ラインデータのアドレスを保存    */
   gVarLineData=LineData;

  /*  入力できる文字数を4に制限している  */
   for(c=0;c&lt;(sizeof(EditControl)/sizeof(EditControl[0]));c++)
           SendDlgItemMessage(hwnd,EditControl[c],EM_SETLIMITTEXT,4,0);

  /*  エディットコントロールの初期化  */
   sprintf(strbuf,"%d",LineData-&gt;xs);
   SetWindowText(GetDlgItem(hwnd,IDC_XS),strbuf);
   sprintf(strbuf,"%d",LineData-&gt;ys);
   SetWindowText(GetDlgItem(hwnd,IDC_YS),strbuf);
   sprintf(strbuf,"%d",LineData-&gt;xe);
   SetWindowText(GetDlgItem(hwnd,IDC_XE),strbuf);
   sprintf(strbuf,"%d",LineData-&gt;ye);
   SetWindowText(GetDlgItem(hwnd,IDC_YE),strbuf);

  SetFocus(GetDlgItem(hwnd,IDC_XS));
   return  FALSE;  
}

static BOOL Wm_CommandProc(HWND hwnd,WORD wNotifyCode,WORD wID,HWND hwndCtl)
{
  int     c;
   int     EditControl[]={IDC_XS,IDC_YS,IDC_XE,IDC_YE};
   char    strbuf[5];

  switch(wID)
   {
       case    IDOK:
           /*  ここで入力エラーを検出する  */
           for(c=0;c&lt;(sizeof(EditControl)/sizeof(EditControl[0]));c++)
           {
               if(!IsNumber(GetDlgItem(hwnd,EditControl[c])))
               {
                   MessageBox(hwnd,"入力が不正です","エラー",MB_OK);
                   /*  入力エラーがあるエディットコントロールにフォーカスを戻す    */
                   SetFocus(GetDlgItem(hwnd,EditControl[c]));
                   return  TRUE;
               }
           }
           
           /*  親の描画データを変更する    */
           GetWindowText(GetDlgItem(hwnd,IDC_XS),strbuf,5);
           gVarLineData-&gt;xs=strtol(strbuf,NULL,10);
           GetWindowText(GetDlgItem(hwnd,IDC_YS),strbuf,5);
           gVarLineData-&gt;ys=strtol(strbuf,NULL,10);
           GetWindowText(GetDlgItem(hwnd,IDC_XE),strbuf,5);
           gVarLineData-&gt;xe=strtol(strbuf,NULL,10);
           GetWindowText(GetDlgItem(hwnd,IDC_YE),strbuf,5);
           gVarLineData-&gt;ye=strtol(strbuf,NULL,10);

          <b>EndDialog(hwnd,IDOK)</b>;
           break;

      case    IDCANCEL:
           <b>EndDialog(hwnd,IDCANCEL)</b>;
           break;  
   }
   return  TRUE;
}

/*
  エディットコントロールに入力された文字列が整数に変換できるか調べる関数
   変換できる場合  :  TRUE
   変換できない場合:  FALSE

/

static BOOL IsNumber(HWND hwnd)
{
  char    *p;     /* for strbuf() */
   char    strbuf[5];

  GetWindowText(hwnd,strbuf,5);
   if(strlen(strbuf)==0)
       return  FALSE;
   strtol(strbuf,&p;,10);
   if(strlen(p)!=0)
       return  FALSE;
   return  TRUE;
}
</pre>
</table>
<p>
<b>・関数の分割</b>
<p>
 ダイアログプロシージャもウインドウプロシージャのようにメッセージを処理する関数ですから、何も考えずにメッセージを処理するコードを書いていれば、巨大なswitch文が出来てしまいます。従って、4章の方式に順じて、メッセージ毎に関数化するのがよいでしょう。
<p>
<b>・初期化処理</b>
<p>
 ウインドウプロシージャの初期化処理はWM_CREATEメッセージがSENDされてきたタイミングで行いますが、ダイアログプロシージャは、<b><i><u>WM_INITDIALOGメッセージ</u></i></b>がSENDされてきたタイミングで行います。WM_INITDIALOGメッセージがSENDされてきたときには、すでにダイアログテンプレートで定義されているコントロールは作成されています。
<p>
 サンプルプログラムでは、Wm_InitDialogProc関数でダイアログボックスの初期化を行っています。ここでの主な処理は3つです。つまり、4つのエディットコントロールに対する入力文字数の制限と初期値の代入、そして、最初のフォーカスの決定です。
<p>
<b>・コントロールの操作</b>
<p>
 <b><i><u>コントロールを操作する基本は、メッセージのSENDです</u></i></b>。それゆえ、<b><i><u>SendDlgItemMessage関数</u></i></b>がダイアログプロシージャからコントロールを操作するときの主役になります。
<p align="CENTER">
<table border="1" bgcolor="#F0F0F0">
<tr><td>
<pre>
LONG SendDlgItemMessage(

  HWND hDlg,      // handle of dialog box
   int nIDDlgItem, // identifier of control
   UINT Msg,       // message to send
   WPARAM wParam,  // first message parameter
   LPARAM lParam   // second message parameter
  );
</pre>
</table>
<p>
 今回の場合、エディットコントロールに対し、オペレーターが入力できる文字数の制限を行う為に、<b><i><u>EM_SETLIMITTEXT</u></i></b>メッセージをSENDしています。一般に、エディットコントロールに対して送るメッセージは、“EM_”で始まっています。しかし、コントロールも一種のウインドウですから、“WM_”で始まるメッセージも受け付けます。その代表的なものは、WM_SETTEXTやWM_GETTEXTでしょう。
<p>
 Wm_InitDialogProc関数は、エディットコントロールに対して初期値も行っています。つまり、現在描画されている直線の両端の座標をWM_INITDIALOGメッセージの付加情報として受け取り、エディットコントロールにセットしています。
<p>
 直線の両端の座標を保持する変数のアドレスは、DialogBoxParam関数を用いてWM_INITDIALOGメッセージの付加情報としてダイアログプロシージャに伝えています。そして、個々の座標値を文字列に変換(sprintf関数)して、しかるべきエディットコントロールにセットしています。そしてこの為に<b><i><u>SetWindowText関数</u></i></b>を用いています。本来なら、WM_SETTEXTをSENDするのが基本なのですが、<b><i><u>Windowsは、よく使うメッセージに対しては、ラップとなる関数を提供しています</u></i></b>。APIを多く覚え出すと、本質的なAPIは意外と少ない事に気が付くと思います。API関数群は、メッセージのラップ以外にも、ソースの判読性を考えてかなりの冗長性を持っています。
<p>
<b>・OKボタンに対する応答</b>
<p>
 ボタンコントロールは、BS_NOTIFYスタイルの有無に関わらず、BN_CLICKED通知メッセージをWM_COMMANDメッセージに付加してSENDしてきます。そして、今回は、BS_NOTIFYスタイルを指定していませんので、WM_COMMANDメッセージが発行されたら、これはボタンからのBN_CLICKED通知メッセージとして扱い、処理を簡略化しています。
<p>
 そして、コントロールIDがOKボタンのそれの場合、IsNumber関数でエディットコントロールに入力された値が整数に変換できるか調べて、直線の座標を格納している変数の内容を書き換えています。その後、EndDialog関数でIDOKを親ウインドウに返しています。もし、エディットコントロールに入力された物が、整数に変換出来ないものであれば、再入力を促す為にエラーのあったエディットコントロールにフォーカスを移動しています。
<p>
<b>・キャンセルボタンに対する応答</b>
<p>
 OKボタンとは対照的に、何もせず、EndDialog関数で親ウインドウにIDCANCELを返しています。
<p>
<b>・IsNumber関数について</b>
<p>
 エディットコントロールに入力された値が、整数に変換可能か調べる関数です。
<p>
 ここでは、多くを語りません。ただ、文字列を整数や実数に変換できるかどうかを調べるのに、一からそのロジックを書くのは間違いだという事を学んで欲しいです。既存の変換関数を応用するのが定石ですね。
<hr>
<p>
<b>・Windowsx.h</b>
<p>
 筆者はあまり使わないのですが、Windowsx.hというヘッダファイルがあります。このファイルの中では、API関数やメッセージを元に判読性の上がるマクロが定義されています。これを使うと、いかにもメッセージをSENDしていますといったプログラミングから開放され、RADツール感覚に一歩近づきます。
<p>
 リスト20-1のダイアログプロシージャをWindowsx.hで定義されているマクロを使って書き換えると、以下のリスト20-2の様になります。
<p>
<table border="1" bgcolor="#F0F0F0">
<caption align="BOTTOM">リスト20-2
<tr><td>
<pre>
/*
  『C言語で始めるWindowsプログラミング』
   18章のサンプルプログラム
                   Programmed by Y.Kondo

/


#define STRICT

#include &lt;windows.h&gt;
<b>#include &lt;windowsx.h&gt;</b>
#include &lt;stdio.h&gt;
#include "DlgProc1.h"
#include "chap18.h"
#include "resource.h"

/* このファイル内でのみ用いられる変数 */
static TLineData *gVarLineData;

/* このファイル内でのみ用いられる関数のプロトタイプ宣言 */
static BOOL Wm_InitDialogProc(HWND,HWND,TLineData*);
static BOOL Wm_CommandProc(HWND,WORD,WORD,HWND);
static BOOL IsNumber(HWND);

/* ダイアログプロシージャ */
BOOL CALLBACK ModalDialogBoxProc(HWND hwnd,UINT msg,WPARAM wparam,LPARAM lparam)
{
  switch(msg)
   {
       case    WM_INITDIALOG:
           return  Wm_InitDialogProc(hwnd,(HWND)wparam,(TLineData*)lparam);
       case    WM_COMMAND:
           return  Wm_CommandProc(hwnd,HIWORD(wparam),LOWORD(wparam),(HWND)lparam);
   }
   return  FALSE;
}

static BOOL Wm_InitDialogProc(HWND hwnd,HWND hwndFocus,TLineData* LineData )
{
  int         c;
   int         EditControl[]={IDC_XS,IDC_YS,IDC_XE,IDC_YE};
   char        strbuf[100];

  /*  ラインデータのアドレスを保存    */
   gVarLineData=LineData;

  /*  入力できる文字数を4に制限している  */
   for(c=0;c&lt;(sizeof(EditControl)/sizeof(EditControl[0]));c++)
           Edit_LimitText(GetDlgItem(hwnd,EditControl[c]),4);

  /*  エディットコントロールの初期化  */
   sprintf(strbuf,"%d",LineData-&gt;xs);
   <b>Edit_SetText(GetDlgItem(hwnd,IDC_XS),strbuf)</b>;
   sprintf(strbuf,"%d",LineData-&gt;ys);
   <b>Edit_SetText(GetDlgItem(hwnd,IDC_YS),strbuf)</b>;
   sprintf(strbuf,"%d",LineData-&gt;xe);
   <b>Edit_SetText(GetDlgItem(hwnd,IDC_XE),strbuf)</b>;
   sprintf(strbuf,"%d",LineData-&gt;ye);
   <b>Edit_SetText(GetDlgItem(hwnd,IDC_YE),strbuf)</b>;

  SetFocus(GetDlgItem(hwnd,IDC_XS));
   return  FALSE;  
}

static BOOL Wm_CommandProc(HWND hwnd,WORD wNotifyCode,WORD wID,HWND hwndCtl)
{
  int     c;
   int     EditControl[]={IDC_XS,IDC_YS,IDC_XE,IDC_YE};
   char    strbuf[5];

  switch(wID)
   {
       case    IDOK:
           /*  ここで入力エラーを検出する  */
           for(c=0;c&lt;(sizeof(EditControl)/sizeof(EditControl[0]));c++)
           {
               if(!IsNumber(GetDlgItem(hwnd,EditControl[c])))
               {
                   MessageBox(hwnd,"入力が不正です","エラー",MB_OK);
                   /*  入力エラーがあるエディットコントロールにフォーカスを戻す    */
                   SetFocus(GetDlgItem(hwnd,EditControl[c]));
                   return  TRUE;
               }
           }
           
           /*  親の描画データを変更する    */
           <b>Edit_GetText(GetDlgItem(hwnd,IDC_XS),strbuf,5)</b>;
           gVarLineData-&gt;xs=strtol(strbuf,NULL,10);
           <b>Edit_GetText(GetDlgItem(hwnd,IDC_YS),strbuf,5)</b>;
           gVarLineData-&gt;ys=strtol(strbuf,NULL,10);
           <b>Edit_GetText(GetDlgItem(hwnd,IDC_XE),strbuf,5)</b>;
           gVarLineData-&gt;xe=strtol(strbuf,NULL,10);
           <b>Edit_GetText(GetDlgItem(hwnd,IDC_YE),strbuf,5)</b>;
           gVarLineData-&gt;ye=strtol(strbuf,NULL,10);

          EndDialog(hwnd,IDOK);
           break;

      case    IDCANCEL:
           EndDialog(hwnd,IDCANCEL);
           break;  
   }
   return  TRUE;
}

/*
  エディットコントロールに入力された文字列が整数に変換できるか調べる関数
   変換できる場合  :  TRUE
   変換できない場合:  FALSE

/

static BOOL IsNumber(HWND hwnd)
{
  char    *p;     /* for strbuf() */
   char    strbuf[5];

  <b>Edit_GetText(hwnd,strbuf,5)</b>;
   if(strlen(strbuf)==0)
       return  FALSE;
   strtol(strbuf,&p;,10);
   if(strlen(p)!=0)
       return  FALSE;
   return  TRUE;
}
</pre>
</table>
<p>
 Windowsx.hで定義されているマクロは、リファレンスマニュアルに記載されていないようですね。この為、Windowsx.hを使ったサンプルソースは、分からない個所をリファレンスマニュアルで調べる前に、Windowsx.hを調べる必要があるので、この講座ではWindowsx.hで定義されているマクロは使いません。
<hr>
<p>
 今回は、モーダルダイアログボックスのダイアログプロシージャの説明を通してコントロールの使い方の基礎を学びました。次回は、もう一つのダイアログボックスであるモードレスダイアログボックスについて学びます。
<p>
 では、お楽しみに。
<p align="RIGHT">
1999年12月11日<br>
修正1999年12月13日<br>
修正1999年12月18日
<hr>
<p align="RIGHT">
<a href="/web/20160504210621/http://web.kyoto-inet.or.jp:80/people/ysskondo/index.html">目次</a><br>
<hr>
<p align="RIGHT">
著作権者:近藤妥

</body>
</html>

  • 最終更新:2018-03-11 05:06:46

このWIKIを編集するにはパスワード入力が必要です

認証パスワード