21章 ダイアログボックス(その4)

<html>

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

<body bgcolor="WHITE">
<font size="5">21章 ダイアログボックス(その4)</font>
<hr>
<p>
 この章では、モードレスダイアログボックスの作成の仕方を学びます。
<p align="CENTER">
<img src="/web/20160504210626im_/http://web.kyoto-inet.or.jp:80/people/ysskondo/from16/img18c.gif"><br><a href="/web/20160504210626/http://web.kyoto-inet.or.jp:80/people/ysskondo/from16/chap18b.lzh">ダウンロード</a>
<p>
 今回も19章と同様に、教材となるサンプルプログラムは18章の物です。したがって、18章でダウンロードされた方は再度ダウンロードする必要はありません。
<hr>
<p>
<b>・モードレスダイアログボックス作成の簡単な予習</b>
<p>
 <b><i><u>モードレスダイアログボックスには、OKボタンやキャンセルボタンが無い</u></i></b>などの違いはありますが、その<b><i><u>作成の概要</u></i></b>は、モーダルダイアログボックスに似ています。つまり、概略として、以下の4ステップを踏めば、モードレスダイアログボックスを作成できます。
<p align="CENTER">
<table border="1" bgcolor="#F0F0F0">
<tr><td>
・ダイアログテンプレートの作成<br>
・ダイアログボックスプロシージャの定義<br>
・専用の関数でモードレスダイアログボックスを作成する<br>
・メッセージループにモードレスダイアログボックス用の専用関数を入れる
</table>
<p>
 ダイアログテンプレートの作成方法は、モーダルダイアログボックスと大差ありませんので、この章では省きます。
<hr>
<p>
<b>・とにかく、ソースコードを眺めてみよう</b>
<p>
とにかくソースコードを眺めてみましょう。今まで通り、重要な個所だけ載せました。全部を見たい方は、サンプルをダウンロードして見てください。
<p>
<table border="1" bgcolor="#F0F0F0">
<caption align="BOTTOM">リスト21-1(Resource.rcより抜粋)
<tr><td>
<pre>

IDD_DIALOG2 DIALOG DISCARDABLE 0, 0, 154, 115
STYLE DS_MODALFRAME | WS_CAPTION | WS_SYSMENU
CAPTION "モードレスダイアログボックス"
FONT 9, "MS Pゴシック"
BEGIN
  EDITTEXT        IDC_XS,35,15,40,14,ES_AUTOHSCROLL
   EDITTEXT        IDC_YS,35,35,40,14,ES_AUTOHSCROLL
   LTEXT           "X=",IDC_STATIC,10,21,12,8
   LTEXT           "Y=",IDC_STATIC,10,35,12,8
   GROUPBOX        "描画開始位置",IDC_STATIC,5,5,90,50
   EDITTEXT        IDC_XE,35,70,40,14,ES_AUTOHSCROLL
   EDITTEXT        IDC_YE,35,90,40,14,ES_AUTOHSCROLL
   LTEXT           "X=",IDC_STATIC,10,75,12,8
   LTEXT           "Y=",IDC_STATIC,10,90,12,8
   GROUPBOX        "描画終止位置",IDC_STATIC,5,60,90,50
   <b>PUSHBUTTON      "描画",IDC_LINE,100,10,50,14</b>
END


</pre>
</table>

<p>
 ダイアログテンプレートでは、OKボタンとキャンセルボタンが定義されていない事が特徴ですね。代わりに、描画ボタンが定義されていますね。

<p>
<table border="1" bgcolor="#F0F0F0">
<caption align="BOTTOM">リスト21-2(chap18.h)
<tr><td>
<pre>
/*
  『C言語で始めるWindowsプログラミング』
   18章のサンプルプログラム
                   Programmed by Y.Kondo

/


#ifndef __chap18__
#define __chap18__
  /*  直線描画用の構造体  */
   typedef struct
   {
       int     xs;
       int     ys;
       int     xe;
       int     ye;
   }   TLineData;
#endif
</pre>
</table>

<p>
<table border="1" bgcolor="#F0F0F0">
<caption align="BOTTOM">リスト21-3(DlgProc2.c)
<tr><td>
<pre>
/*
  『C言語で始めるWindowsプログラミング』
   18章のサンプルプログラム
                   Programmed by Y.Kondo

/


#define STRICT

#include &lt;windows.h&gt;
#include &lt;stdio.h&gt;
#include "DlgProc2.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 Wm_CloseProc(HWND);
static BOOL IsNumber(HWND);

/* ダイアログプロシージャ */
BOOL CALLBACK ModelessDialogBox(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);
       case    <b>WM_CLOSE</b>:
           return  Wm_CloseProc(hwnd);
   }
   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)
{
  HWND    hOwnerWnd;  /*  オーナーウインドウのハンドル    */
   char    strbuf[5];  /*  4文字しか受け付けないので      */
   int     EditControl[]={IDC_XS,IDC_YS,IDC_XE,IDC_YE};
   int     c;

  if(wID==IDC_LINE)
   {
       /*  ここで入力エラーを検出する  */
       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]));
               /*  メッセージボックスによりウインドウのZオーダーが変化することに対する対策    */
               <b>SetWindowPos(GetWindow(hwnd,GW_OWNER),HWND_TOP,0,0,0,0,SWP_NOMOVE|SWP_NOSIZE)</b>;
               return  TRUE;
           }
       }

<b>
      /*  オーナーの描画データを変更する    */
       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);

      /*  オーナーの画面を無効化して再描画さす  */
       hOwnerWnd=GetWindow(hwnd,GW_OWNER);
       InvalidateRect(hOwnerWnd,NULL,TRUE);
       UpdateWindow(hOwnerWnd);</b>
   }

  return  TRUE;
}
<b>
static BOOL Wm_CloseProc(HWND hwnd)
{
  HWND    hOwnerWnd;  /*  オーナーウインドウのハンドル    */

  hOwnerWnd=GetWindow(hwnd,GW_OWNER);
   ShowWindow(hwnd,SW_HIDE);
   CheckMenuItem(GetMenu(hOwnerWnd),IDM_MODELESS,MF_BYCOMMAND|MF_UNCHECKED);
   return  TRUE;
}
</b>
/*
  エディットコントロールに入力された文字列が整数に変換できるか調べる関数
   変換できる場合  :  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>
 リスト21-3は、モードレスダイアログボックスのダイアログプロシージャです。
<p>
 メインウインドウの直線を書き換える方針は、次の通りです。描画する直線の終点始点情報を格納する変数のアドレスを受け取り、ダイアログプロシージャ内で新たな値に変更する。その後、オーナーウインドウのクライアント領域を無効化して、再描画さす。
<p>
 GUIプログラミング独特の描画方法なので、慣れないと、ダイアログプロシージャから直に再描画したくなってしまいますね。
<p>
 さて、モーダルダイアログボックスと異なり、<b><i><u>EndDialog関数が無い事に注意</u></i></b>してください。代わりに、<b><i><u>WM_CLOSEメッセージ</u></i></b>に応答して、自分自身をあたかも破壊されたかのように画面から消去しています。
<p>
 WM_CLOSEメッセージというのは、オペレーターがウインドウの消滅を意味する行為(タイトルバーの×ボタンを押すなど)を行った場合、発生するメッセージです。このメッセージは、オペレーターに確認の機会を与えるのに用いられます。そして、普通のウインドウプロシージャの場合、デフォルト動作は、ウインドウの破壊です。これに対し、ダイアログプロシージャでは、デフォルトでウインドウの破壊をしません。今回は、後述のように、モードレスダイアログボックスを動的に生成・破壊をしていません。代わりに、自分自身を画面から見えなくし、あたかも破壊されたかのような動作をしています。もし、動的にモードレスダイアログボックスを生成・破壊するなら、DestoryWindow関数を用いる必要があります。(もちろん、これだけの処理では不十分です。)<p>
<table border="1" bgcolor="#F0F0F0">
<caption align="BOTTOM">リスト21-4(WndProc.c)
<tr><td>
<pre>
/*
  『C言語で始めるWindowsプログラミング』
   18章のサンプルプログラム
                   Programmed by Y.Kondo

/


#define STRICT
#include &lt;windows.h&gt;
#include "wndproc.h"
#include "resource.h"
#include "DlgProc2.h"
#include "chap18.h"
#include "string.h"

/*===============================================================================*/

/* このファイル内でのみ用いられる関数のプロトタイプ宣言 */
static LRESULT Wm_CreateProc(HWND,LPCREATESTRUCT);
static LRESULT Wm_DestroyProc(HWND);
static LRESULT Wm_CommandProc(HWND,WORD,WORD,HWND);
static LRESULT Wm_PaintProc(HWND);

/* このファイル内でのみ用いられる変数 */
static TLineData ForModeless; /* モードレスダイアログボックス用 */
static HWND hModelessDlg; /* モードレスダイアログボックスのハンドル */

/* メインウインドウのウインドウプロシージャ */
LRESULT CALLBACK WindowProc(HWND hwnd,UINT message,WPARAM wparam,LPARAM lparam)
{
  switch(message)
   {
       case    WM_CREATE:
           return  Wm_CreateProc(hwnd,(LPCREATESTRUCT)lparam);
       case    WM_DESTROY:
           return  Wm_DestroyProc(hwnd);
       case    WM_COMMAND:
           return  Wm_CommandProc(hwnd,HIWORD(wparam),LOWORD(wparam),(HWND)lparam);
       case    WM_PAINT:
           return  Wm_PaintProc(hwnd);
   }
   return  DefWindowProc(hwnd,message,wparam,lparam);
}

/*===============================================================================*/

static LRESULT Wm_CreateProc(HWND hwnd,LPCREATESTRUCT cs)
{
  /*  直線の描画データ初期化  */
   TLineData   org={0,100,100,0};
   ForModeless =org;

  /*  モードレスダイアログボックスを作成する  */
   /*  ただし、ここでは、表示しない            */

  <b>hModelessDlg
           =CreateDialogParam( cs-&gt;hInstance,
                               MAKEINTRESOURCE(IDD_DIALOG2),
                               hwnd,
                               (DLGPROC)ModelessDialogBox,
                               (LPARAM)&ForModeless;);</b>
   if(hModelessDlg==NULL)
       return  -1;


  /*  WinMain関数からでも見えるようにする  */
   *(HWND*)cs-&gt;lpCreateParams=hModelessDlg;
   
   return  0;
}
static LRESULT Wm_DestroyProc(HWND hwnd)
{
  PostQuitMessage(0);
   return  0;
}

static LRESULT Wm_CommandProc(HWND hwnd,WORD wNotifyCode,WORD wID,HWND hwndCtl)
{
  HINSTANCE   hInstance;
   switch(wID)
   {
           /*  モードレスダイアログボックス表示    */
       case    IDM_MODELESS:
           CheckMenuItem(GetMenu(hwnd),IDM_MODELESS,MF_BYCOMMAND|MF_CHECKED);
           ShowWindow(hModelessDlg,SW_SHOWNORMAL);
           break;
   }
   return  0;
}

static LRESULT Wm_PaintProc(HWND hwnd)
{
  PAINTSTRUCT ps;

  BeginPaint(hwnd,&ps;);
       MoveToEx(ps.hdc,ForModeless.xs,ForModeless.ys,NULL);
       LineTo(ps.hdc,ForModeless.xe,ForModeless.ye);
   EndPaint(hwnd,&ps;);
   return  0;
}
</pre>
</table>
<p>
 先で少し触れたように、今回のモードレスダイアログボックスは、寿命に関して7章のオーナー付きウインドウに似ています。つまり、オーナーウインドウが作成されるときに生成されて、オーナーウインドウが、破壊されるとき、運命を共にします。ただ、ウインドウがダイアログボックスになり、その作成方法(作成関数とダイアログテンプレート)が若干変っただけです。ある意味、モーダルダイアログボックスより理解しやすいかと思います。
<p>
<table border="1" bgcolor="#F0F0F0">
<caption align="BOTTOM">リスト21-5(WinMain.cより抜粋)
<tr><td>
<pre>
/* アプリケーションエントリーポイント */
int WINAPI WinMain(HINSTANCE hInstance,
                 HINSTANCE    hPrevInstance,
                  LPSTR        CmdLine,
                  int          CmdShow)
{
  HWND            hwnd;       /*  メインウインドウのウインドウハンドル    */
   MSG             msg;        /*  メッセージキューから取得したメッセージ  */

  HWND            hDlgWnd;    /*  モードレスダイアログのウインドウハンドル    */

  if(RegMainWindowClass(hInstance)==0)       /*  ウインドウクラス登録    */
       return  0;

  hwnd=CreateWindow(  "MainWindowClass",  /*  ウインドウ作成          */  
                       "18章 モードレスダイアログボックス サンプルプログラム",
                       WS_OVERLAPPEDWINDOW,
                       CW_USEDEFAULT,
                       CW_USEDEFAULT,
                       CW_USEDEFAULT,
                       CW_USEDEFAULT,
                       NULL,
                       (HMENU)NULL,
                       hInstance,
                       &hDlgWnd;);      /*  モードレスのダイアログボックスのハンドルを受け取る為    */
   if(hwnd==NULL)
       return  0;

  ShowWindow(hwnd,CmdShow);           /*  ウインドウの表示        */
   UpdateWindow(hwnd);                 /*  ウインドウの最初の更新  */

  while(GetMessage(&msg;,NULL,0,0))    /*  メッセージループ        */
   {
       if(!<b>IsDialogMessage(hDlgWnd,&msg;)</b>)
       {
           TranslateMessage(&msg;);
           DispatchMessage(&msg;);
       }
   }

  return  msg.wParam;
}
</pre>
</table>
<p>
 メッセージループに新しいAPI関数がお目見えしましたね。つまり、<b><i><u>IsDialogMessage関数</u></i></b>です。これに関しては、以下で説明します。
<hr>
<p>
<b><p>
・モードレスダイアログボックスに関するAPI
</b>
<p>
 では、モードレスダイアログを実現するにあたって登場する新しいAPI関数について簡単に説明しましょう。
<b><p>・CreateDialogParam関数</b>
<p>
 <b><i><u>CreateWindow関数の様な関数です。</u></i></b>ただ、ウインドウの概観の決定や子ウインドウの作成をダイアログテンプレートを用いて行う点、そしてダイアログプロシージャのアドレスを必要とする点で異なります。CreateDialogParam関数で作成したモードレスダイアログボックスは、CreateWindow関数で作成したウインドウ同様、DestoryWindow関数で破壊します。
<p>
 CreateDialogParam関数より知名度が高いのが、<b><i><u>CreateDialogマクロ</u></i></b>。これは、CreateDialogParam関数からダイアログプロシージャに32ビット値を渡すパラメーターを削除したマクロです。このマクロは、ダイアログボックスに値を渡す必要が無い場合を除いて、使いべきではありません。なぜなら、グローバル変数に依存したコーディングになるからです。

<b><p>・IsDialogMessage関数</b>
<p>
 メッセージループに登場した新しい関数ですね。この関数は、GetMessage関数で吸い上げたメッセージがモードレスダイアログボックスに由来するかどうかを調べます。そして、もし、モードレスダイアログボックスに由来するなら、ダイアログボックス内でのTABキーによるフォーカスの移動などダイアログボックス独自の処理を行い、そして、TranslateMessage関数とDispatchMessage関数の処理も内部的に行います。
<p>
 もし、この関数がメッセージを処理したら、0以外の値を返します。また、0を返したときは、従来の方法で、メッセージを処理する必要があります。
<p>
 したがって、このAPI関数をメッセージループに入れ忘れても、モードレスダイアログボックスは使いにくいですが、普通のオーナー付きウインドウの様に動きます。
<hr>
<b><p>
・モードレスダイアログボックスのダイアログプロシージャの特徴
</b>
<p>
 基本的には、モーダルダイアログボックスのダイアログプロシージャと同じです。しかし、先に述べたように、<b><i><u>EndDialog関数を用いてはいけません</u></i></b>。モードレスダイアログボックスを終了さすのは、DestroyWindow関数です。今回は、オーナー付きウインドウ同様、暗黙のうちにDestroyWindow関数でモードレスダイアログボックスは破壊されています。
<hr>
<p>
 さて、これで、モーダルダイアログボックスとモードレスダイアログボックスの基本は説明したつもりです。恐らく、簡単だったと思います。特に、モードレスダイアログボックスは、普通のオーナー付きウインドウに極めて制御の流れが近いため、簡単だったと思います。
<p>
 次回は、モーダルダイアログボックスに付いて単に使うだけでなく考察したいと思います。
<p>
 では、お楽しみに。
<p align="RIGHT">
2000年1月23日
<hr>
<p align="RIGHT">
<a href="/web/20160504210626/http://web.kyoto-inet.or.jp:80/people/ysskondo/index.html">目次</a><br>
<a href="chap22.html">次へ</a>
<hr>
<p align="RIGHT">
著作権者:近藤妥

</body>
</html>

  • 最終更新:2018-03-11 05:07:24

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

認証パスワード