17章 コントロール概説(その2)

<html>

<head>
<meta http-equiv="Content-Type" content="text/html; charset=shift_jis"></meta>
<title>17章 コントロール概説(その2)</title>
</head>

<body bgcolor="WHITE">
<font size="5">17章 コントロール概説(その2)</font>
<hr>
<p>
 今回は、16章に続いて、コントロール概説(その2)です。
<p>
 今回は、前回紹介できなかった、<b><i><u>リストコントロール</u></i></b>、<b><i><u>コンボボックスコントロール</u></i></b>、そして、<b><i><u>スクロールバーコントロール</u></i></b>を紹介します。
<hr>
<p>
<b>・リストボックスコントロール</b>
<p>
 文字列を表示するコントロールです。ユーザーは、一つか複数の文字列を選択する事が出来ます。
<p>
<p>
<table border="1" bgcolor="#F0F0F0">
<caption align="BOTTOM">リスト17-1<a href="c17l.lzh">ダウンロード</a>
<tr><td>
<pre>
/*
  『C言語で始めるWindowsプログラミング』
   17章のサンプルプログラム
                   Programmed by Y.Kondo

/


#define STRICT
#include &lt;windows.h&gt;
#include &lt;stdlib.h&gt;
#include "wndproc.h"

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

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

/* メインウインドウのウインドウプロシージャ */
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);
   }
   return  DefWindowProc(hwnd,message,wparam,lparam);
}

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

static LRESULT Wm_CreateProc(HWND hwnd,LPCREATESTRUCT cs)
{
  /*  もっとも標準的とされるリストボックス            */
   /*  LBS_STANDARDは(LBS_NOTIFY|LBS_SORT|WS_VSCROLL|WS_BORDER)と等価  */
   CreateWindow("LISTBOX","",
                WS_CHILD|WS_VISIBLE|<b>LBS_STANDARD</b>,
                0,0,300,150,
                hwnd,
                (HMENU)1,
                cs-&gt;hInstance,
                NULL);
   AddSampleData(GetDlgItem(hwnd,1));

  /*  マルチカラムで複数のセルを選択可能なリストボックス  */
   CreateWindow("LISTBOX","",
                WS_CHILD|WS_VISIBLE|<b>LBS_STANDARD</b>|<b>LBS_MULTICOLUMN</b>|<b>LBS_MULTIPLESEL</b>,
                0,160,300,150,
                hwnd,
                (HMENU)2,
                cs-&gt;hInstance,
                NULL);
   AddSampleData(GetDlgItem(hwnd,2));

   return  0;  
}

static LRESULT Wm_DestroyProc(HWND hwnd)
{
  PostQuitMessage(0);
   return  0;
}

static LRESULT Wm_CommandProc(HWND hwnd,WORD wNotifyCode,WORD wID,HWND hwndCtl)
{
  if(wID==1 &amp;&amp; wNotifyCode==<b>LBN_DBLCLK</b>)
   {
       char    *pText;     /*  テキストへのポインタ    */
       LONG    cursel;     /*  カレントセル            */
       LONG    textlen;    /*  テキストの長さ          */
       cursel=SendDlgItemMessage(hwnd,1,<b>LB_GETCURSEL</b>,0,0);
       if(cursel==<b>LB_ERR</b>)
           return  0;
       textlen=SendDlgItemMessage(hwnd,1,<b>LB_GETTEXTLEN</b>,(WPARAM)cursel,0);
       pText=malloc(textlen+1);    /*  メモリは十分あると仮定  */
       SendDlgItemMessage(hwnd,1,<b>LB_GETTEXT</b>,cursel,(LPARAM)pText);
       MessageBox(hwnd,pText,"現在選択されているデータ",MB_ICONINFORMATION|MB_OK);
       free(pText);
   }
   return  0;
}

static void AddSampleData(HWND hwnd)
{
  static  char    *data[]={"DATA01","DATA02","DATA03","DATA04","DATA05",
                            "DATA06","DATA07","DATA08","DATA09","DATA10",
                            "DATA11","DATA12","DATA13","DATA14","DATA15",
                            "DATA16","DATA17","DATA18","DATA19","DATA20",};
   int c;

  for(c=0;c&lt;20;c++)
       SendMessage(hwnd,<b>LB_ADDSTRING</b>,0,(LPARAM)data[c]);
}
</pre>
</table>
<p>
 リスト17-1を見てお分かりのように、ウインドウスタイルとして<b><i><u>LBS_何々</u></i></b>を論理和で追加することにより、リストボックスの機能と概観が変ります。
<p>
 リストボックスにメッセージをSENDする事により、リストボックスから情報を取得したり、文字列を追加したり出来ます。リストボックスにSEND出来るメッセージの多くは<b><i><u>LB_何々</u></i></b>です。たとえば、<b><i><u>LB_ADDSTRING</u></i></b>を用いることにより、リストボックスに文字列を追加出来ます。
<p>
 また、リストボックスは作成されるとき、<b><i><u>LBS_NOTIFY</u></i></b>がウインドウスタイルとして指定してあれば、親に通知メッセージをSENDしてきます。上のサンプルでは、<b><i><u>LBN_DBLCLK</u></i></b>でダブルクリックに応答しています。
<hr>
<p>
<b>・コンボボックスコントロール</b>
<p>
 コンボボックスコントロールには、大きく3つのタイプがあります。つまり、<b><i><u>シンプル</u></i></b>、<b><i><u>ドロップダウン</u></i></b>、そして、<b><i><u>ドロップダウンリスト</u></i></b>です。
<p>
 シンプルと言うのは、シングルラインのエディットコントロールの下にリストボックスが付いた様な物です。エディットコントロールのように一から入力する事が出来、下のリストボックス部分に候補があれば、ここから選択することも可能です。もちろん、候補から選び、これを変更して用いる事も可能です。
<p>
 ドロップダウンと言うのは、シンプルの省スペース版です。入力ボックス右横のボタンを押すと隠れていた候補が表示されます。
<p>
 ドロップダウンリスト。これは、他の2つのタイプとは異なり、エディットボックスとしての機能を持っていません。つまり、省スペース版のリストボックスです。
<p>
 以下にサンプルを示します。
<p>
<p>
<table border="1" bgcolor="#F0F0F0">
<caption align="BOTTOM">リスト17-2<a href="c17c.lzh">ダウンロード</a>
<tr><td>
<pre>
/*
  『C言語で始めるWindowsプログラミング』
   17章のサンプルプログラム
                   Programmed by Y.Kondo

/


#define STRICT
#include &lt;windows.h&gt;
#include &lt;stdlib.h&gt;
#include "wndproc.h"

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

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

/* メインウインドウのウインドウプロシージャ */
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);
   }
   return  DefWindowProc(hwnd,message,wparam,lparam);
}

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

static LRESULT Wm_CreateProc(HWND hwnd,LPCREATESTRUCT cs)
{
  /*  シンプル    */
   CreateWindow("COMBOBOX","",
                WS_CHILD|WS_VISIBLE|WS_VSCROLL|WS_BORDER|<b>CBS_SIMPLE</b>,
                0,0,300,150,
                hwnd,
                (HMENU)1,
                cs-&gt;hInstance,
                NULL);
   AddSampleData(GetDlgItem(hwnd,1));
   /*  ドロップダウン  */
   CreateWindow("COMBOBOX","",
                WS_CHILD|WS_VISIBLE|WS_VSCROLL|WS_BORDER|<b>CBS_DROPDOWN</b>,
                0,160,300,150,
                hwnd,
                (HMENU)2,
                cs-&gt;hInstance,
                NULL);
   AddSampleData(GetDlgItem(hwnd,2));
   /*  ドロップダウンリスト    */
   CreateWindow("COMBOBOX","",
                WS_CHILD|WS_VISIBLE|WS_VSCROLL|WS_BORDER|<b>CBS_DROPDOWNLIST</b>,
                0,205,300,150,
                hwnd,
                (HMENU)3,
                cs-&gt;hInstance,
                NULL);
   AddSampleData(GetDlgItem(hwnd,3));

  return  0;  
}

static LRESULT Wm_DestroyProc(HWND hwnd)
{
  PostQuitMessage(0);
   return  0;
}

static void AddSampleData(HWND hwnd)
{
  static  char    *data[]={"DATA01","DATA02","DATA03","DATA04","DATA05",
                            "DATA06","DATA07","DATA08","DATA09","DATA10",
                            "DATA11","DATA12","DATA13","DATA14","DATA15",
                            "DATA16","DATA17","DATA18","DATA19","DATA20",};
   int c;

  for(c=0;c&lt;20;c++)
       SendMessage(hwnd,CB_ADDSTRING,0,(LPARAM)data[c]);
}
</pre>
</table>
<p>
 リスト17-2を見てお分かりのように、ウインドウスタイルとして<b><i><u>CBS_何々</u></i></b>を論理和で追加することにより、コンボボックスの機能と概観が変ります。
<p>
 コンボボックスにもメッセージをSENDする事により、コンボボックスから情報を取得したり、文字列を追加したり出来ます。コンボボックスにSENDするメッセージは<b><i><u>CB_何々</u></i></b>である事が多いです。ただ、機能から想像出来るように、そのメッセージの仕様は、エディットコントロールのそれのサブセットとリストボックスのそれのサブセットといった感じです。
<hr>
<p>
<b>・スクロールバーコントロールとスクロールバー</b>
<p>
 スクロールバーコントロールと言うのは、子ウインドウで、まさにコントロールです。下の図で説明するなら、ウインドウの中にあるツマミをスライド出来る大きなコントロールです。これに良く似た物に、ウインドウの右端や下に付くスクロールバーというのがあります。どちらも、ツマミをスライドさすことにより、その量を入力したりするのに用います。
<p>
 実装上の事は知る術もありませんが、この2つは全くの別物です。しかし、概観が良く似ているだけでなく、プログラミングの側面からも良く似ているので、同時に説明することにしました。
<p>
<p>
<table border="1" bgcolor="#F0F0F0">
<caption align="BOTTOM">リスト17-3<a href="c17s.lzh">ダウンロード</a>
<tr><td>
<pre>
/*
  『C言語で始めるWindowsプログラミング』
   17章のサンプルプログラム
                   Programmed by Y.Kondo

/


#define STRICT
#include &lt;windows.h&gt;
#include "wndproc.h"

/* アプリケーションエントリーポイント */
int WINAPI WinMain(HINSTANCE hInstance,
                 HINSTANCE    hPrevInstance,
                  LPSTR        CmdLine,
                  int          CmdShow)
{
  HWND            hwnd;       /*  メインウインドウのウインドウハンドル    */
   MSG             msg;        /*  メッセージキューから取得したメッセージ  */
   WNDCLASS        wc;         /*  ウインドウクラス登録用の構造体          */

  wc.style        =0;
   wc.lpfnWndProc  =WindowProc;
   wc.cbClsExtra   =0;
   wc.cbWndExtra   =0;
   wc.hInstance    =hInstance;
   wc.hIcon        =LoadIcon(NULL,IDI_APPLICATION);
   wc.hCursor      =LoadCursor(NULL,IDC_ARROW);
   wc.hbrBackground=(HBRUSH)GetStockObject(LTGRAY_BRUSH);
   wc.lpszMenuName =NULL;
   wc.lpszClassName="MainWindowClass";
   
   if(RegisterClass(&wc;)==0)               /*  ウインドウクラス登録    */
       return  0;

  hwnd=CreateWindow(  "MainWindowClass",  /*  ウインドウ作成          */  
                       "17章 スクロールバーコントロールのサンプル",
                       WS_OVERLAPPEDWINDOW|<b>WS_VSCROLL</b>,
                       CW_USEDEFAULT,
                       CW_USEDEFAULT,
                       CW_USEDEFAULT,
                       CW_USEDEFAULT,
                       NULL,
                       (HMENU)NULL,
                       hInstance,
                       0);
   if(hwnd==NULL)
       return  0;

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

  while(GetMessage(&msg;,NULL,0,0))    /*  メッセージループ        */
   {
       TranslateMessage(&msg;);
       DispatchMessage(&msg;);
   }

  return  msg.wParam;
}
</pre>
</table>
<p>
<table border="1" bgcolor="#F0F0F0">
<caption align="BOTTOM">リスト17-4<a href="c17s.lzh"></a>
<tr><td>
<pre>
/*
  『C言語で始めるWindowsプログラミング』
   17章のサンプルプログラム
                   Programmed by Y.Kondo

/


#define STRICT
#include &lt;windows.h&gt;
#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
#include "wndproc.h"

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

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

/* メインウインドウのウインドウプロシージャ */
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    <b>WM_VSCROLL</b>:
           return  Wm_VScrollProc(hwnd,LOWORD(wparam),HIWORD(wparam),(HWND)lparam);
   }
   return  DefWindowProc(hwnd,message,wparam,lparam);
}

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

static LRESULT Wm_CreateProc(HWND hwnd,LPCREATESTRUCT cs)
{
  /*  ウインドウの垂直方向のスクロールバーの設定  */  
   <b>SetScrollRange(hwnd,SB_VERT,0,1000,TRUE)</b>;
   <b>SetScrollPos(hwnd,SB_VERT,0,TRUE)</b>;

  /*  垂直方向のスクロールバーコントロールの作成  */  
   CreateWindow("SCROLLBAR","",
                WS_CHILD|WS_VISIBLE|<b>SBS_VERT</b>,
                10,10,150,300,
                hwnd,
                (HMENU)1,
                cs-&gt;hInstance,
                NULL);
   /*  垂直方向のスクロールバーコントロールの設定  */
   <b>SetScrollRange(GetDlgItem(hwnd,1),SB_CTL,0,10,TRUE)</b>;
   <b>SetScrollPos(GetDlgItem(hwnd,1),SB_CTL,5,TRUE)</b>;

  return  0;  
}

static LRESULT Wm_DestroyProc(HWND hwnd)
{
  PostQuitMessage(0);
   return  0;
}

static LRESULT Wm_VScrollProc(HWND hwnd,int nScrollCode,short nPos,HWND hwndScrollBar)
{
  static int      gPos;   /*  なぜstaticか?これは、SB_ENDSCROLL対策  */
          int      gMin;
          int      gMax;

  if(hwndScrollBar!=NULL)
   {   /*  スクロールバーコントロールの場合    */
       <b>GetScrollRange(hwndScrollBar,SB_CTL,&gMin;,&gMax;)</b>;
       switch(nScrollCode)
       {
           case    <b>SB_LINEDOWN</b>:
               gPos=<b>GetScrollPos(hwndScrollBar,SB_CTL)</b>;
               gPos+=1;
               if(gPos&gt;gMax)
                   gPos=gMax;
               break;
           case    <b>SB_LINEUP</b>:
               gPos=<b>GetScrollPos(hwndScrollBar,SB_CTL)</b>;
               gPos-=1;
               if(gPos&lt;gMin)
                   gPos=gMin;
               break;
           case    <b>SB_PAGEUP</b>:
               gPos=<b>GetScrollPos(hwndScrollBar,SB_CTL)</b>;
               gPos-=2;
               if(gPos&lt;gMin)
                   gPos=gMin;
               break;
           case    <b>SB_PAGEDOWN</b>:
               gPos=<b>GetScrollPos(hwndScrollBar,SB_CTL)</b>;
               gPos+=2;
               if(gPos&gt;gMax)
                   gPos=gMax;
               break;
           case    <b>SB_THUMBPOSITION</b>:
               gPos=nPos;
               break;
       }
       <b>SetScrollPos(hwndScrollBar,SB_CTL,gPos,TRUE)</b>;
   }
   else
   {   /*  ウインドウのスクロールバーの場合    */
       <b>GetScrollRange(hwnd,SB_VERT,&gMin;,&gMax;)</b>;
       switch(nScrollCode)
       {
           case    <b>SB_LINEDOWN</b>:
               gPos=<b>GetScrollPos(hwnd,SB_VERT)</b>;
               gPos+=1;
               if(gPos&gt;gMax)
                   gPos=gMax;
               break;
           case    <b>SB_LINEUP</b>:
               gPos=<b>GetScrollPos(hwnd,SB_VERT)</b>;
               gPos-=1;
               if(gPos&lt;gMin)
                   gPos=gMin;
               break;
           case    <b>SB_PAGEUP</b>:
               gPos=<b>GetScrollPos(hwnd,SB_VERT)</b>;
               gPos-=50;
               if(gPos&lt;gMin)
                   gPos=gMin;
               break;
           case    <b>SB_PAGEDOWN</b>:
               gPos=<b>GetScrollPos(hwnd,SB_VERT)</b>;
               gPos+=50;
               if(gPos&gt;gMax)
                   gPos=gMax;
               break;
           case    <b>SB_THUMBPOSITION</b>:
               gPos=nPos;
               break;
       }
       <b>SetScrollPos(hwnd,SB_VERT,gPos,TRUE)</b>;
   }

  return  0;
}
</pre>
</table>
<p>
 スクロールバーコントロールは、今回紹介した垂直方向以外に、水平方向の物もあります。これは、CreateWindow関数で<b><i><u>SBS_HORZ</u></i></b>とすれば良いだけです。
<p>
 ウインドウの右端に付くスクロールバーは、そのウインドウを作成するときに、<b><i><u>WS_VSCROLL</u></i></b>をウインドウスタイルに加えるだけです。ウインドウの下に付く横方向にスライドするスクロールバーを付けるには、<b><i><u>WS_HSCROLL</u></i></b>をウインドウスタイルに付け加えれば良いのです。
<p>
 ただ、今までのコントロールが、親ウインドウにメッセージを送って来るときに、WM_COMMANDを利用していたのに対し、スクロールバーコントロールとスクロールバーは垂直方向の場合、<b><i><u>WM_VSCROLLメッセージ</u></i></b>をSENDして来る事です。水平方向の場合は、<b><i><u>WM_HSCROLLメッセージ</u></i></b>です。
<p>
 では、スクロールバーコントロールの方からWM_VSCROLLメッセージが送られたか、ウインドウのスクロールバーからメッセージがSENDされたかはどこで判断するかというと、LPARAM引数の値です。コントロールからの場合、そのウインドウハンドルですが、そうでない場合、NULLです。
<p>
 さて、ウインドウのスクロールバーをスライドしても、ウインドウ内部がスクロールしないではないかと思った方も居ると思います。Windowsは、スクロールを求められている事をアプリケーションに知らすだけで、その実装はプログラマに依存します。よく、RADツールやMFCの初心者は、RADやMFCなら簡単にスクロール出来ると言います。たしかに、ちょっと画像をずらす程度のスクロールならそうかもしれません。しかし、テキストエディタを実装する場合、1万行のテキストを汎用的な方法でスクロールさすとなると、かなり苦しいでしょう。やはり、表示される部分だけ算出して、この部分だけ画面に描画し、スクロールしたように見せるのが普通でしょう。Windowsはこの様な機会を与えてくれるだけです。今回は、ツマミを動かす程度の実装で、それ以外、何もしていません。
<hr>
<p>
 今回も退屈だったと思います。この講座は、Windows初心者を的にしていますので、Windowsが提供する基本的なコントロールの紹介が必要と思い、16章・17章と2章分を割いて説明しました。
<p>
 また、単に説明だけでなく、CreateWindow関数を用いて作るのだという事も示唆しましたが、実際、この様なコントロールをまともにCreateWindow関数を用いて作成する事は、少ないと思います。なぜなら、この様なコントロールはダイアログボックスと言われるウインドウの中で用いられる事が多いからです。
<p>
 次章から、当分の間、ダイアログボックスを学びましょう。そして、その中で、16章と17章で紹介したコントロールの使い方も説明していきます。
<p>
 では、お楽しみに。
<p align="RIGHT">
1999年11月24日<br>
加筆修正1999年11月27日
<hr>
<p align="RIGHT">
<a href="/web/20160504210617/http://web.kyoto-inet.or.jp:80/people/ysskondo/index.html">目次</a><br>
<a href="chap18.html">次へ</a>
<hr>
<p align="RIGHT">
著作権者:近藤妥

</body>
</html>

  • 最終更新:2018-03-11 04:52:20

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

認証パスワード