第13章 コンボボックス

コンボボックスは、リストボックスと一行エディットが組合わさっている機能をもつコントロールであり、VCLTComboBox クラスに相当する。 実際、コンボボックスを操作するメッセージには、リストボックスやエディットに送るメッセージとよく似たものが多い。 この章では、このコントロールをコードで作って動作をテストしてから、その機能をカプセル化して TAPIComboBox クラスを作ることにしよう。

13−0 この章の内容

第13章 コンボボックス
13−1 コンボボックスをコードでつくる
13−2 コンボボックスのラッパークラス
13−3 ドライブコンボボックス
13−4 まとめ


| arrw18c.gif 前の章へ | 総目次へ | 次の章へ arrw18d.gif

13−1 コンボボックスをコードでつくる

コンボボックスコントロールは、CreateWindowEx( ) APIのクラス名に 'COMBOBOX' を指定して作られる。 他のコントロールとの違いは、 コントロールの高さには、リストボックスをドロップダウンするための領域を予め確保しておかなければならないことだ。windows.pas には、コンボボックスに指定できるウィンドウスタイルの値が以下のように定義されている。

const
  { Combo Box styles }
  CBS_SIMPLE = 1;			//単純なコンボボックス。リストは常にドロップダウンされている。
  CBS_DROPDOWN = 2;		//CBS_SIMPLEで、リストはドロップダウンアイコンで表示する。
  CBS_DROPDOWNLIST = 3;		//CBS_DROPDOWNと同じであるが、 選択フィールドには入力不可。
  CBS_OWNERDRAWFIXED = $10;		//項目の高さが一定のオーナー描画。
  CBS_OWNERDRAWVARIABLE = $20;	//項目の高さが可変のオーナー描画。
  CBS_AUTOHSCROLL = $40;		//選択フィールドの水平スクロールを自動的に行う。
  CBS_OEMCONVERT = $80;		//入力文字をOEM文字セットに変換する。
  CBS_SORT = $100;			//リスト項目の文字列をソートする。
  CBS_HASSTRINGS = $200;		//文字列を保持する。
  CBS_NOINTEGRALHEIGHT = $400;	//項目の高さの途中でも表示できる。
  CBS_DISABLENOSCROLL = $800;	//スクロール不要なときに、スクロールバーを使用不可にする。
  CBS_UPPERCASE = $2000;		//入力文字を大文字に変換する。
  CBS_LOWERCASE = $4000;		//入力文字を小文字に変換する。

このスタイルを見ても分かるように、オーナー描画を除けば、コンボボックスにはシンプルドロップダウンドロップダウンリストの三つのスタイルがある。 選択フィールドに入力できない CBS_DROPDOWNLIST スタイルでは、選択フィールドはエディットコントロールではない。 VCL TComboBox のデフォルトのインスタンスのクラス情報を見てみると、ExStyle がゼロであり、Style$54010242 になっている。

デフォルトスタイルが	$54010242

WS_CHILD =		$40000000
WS_VISIBLE = 		$10000000
WS_CLIPSIBLINGS = 		 $4000000
WS_TABSTOP =		   $10000
CBS_HASSTRINGS =		     $200
CBS_AUTOHSCROLL =		      $40
CBS_DROPDOWN =		        2

これは、CBS_DROPDOWN 型のコンボボックスである。 スタイルの他のビットフィールドと異なり、最下位の2ビットはビットのオン・オフによるフラグではないことに気づいただろうか。 CBS_SIMPLE or CBS_DROPDOWNCBS_DROPDOWNLIST と同じ値( = 3 )になる。 このことから、この3つのスタイルはそのうちのひとつだけを指定するものだ。 ここでは、この三つのスタイルのコンボボックスをコードで作ってテストしよう。 コンボボックスコントロールが認識できるメッセージは、Messages.pas に定義されている。

const
{ Combo Box messages }

  CB_GETEDITSEL            = $0140; // エディットの選択領域の取得
  CB_LIMITTEXT             = $0141; // エディットに入力できる文字数の設定
  CB_SETEDITSEL            = $0142; // エディットの選択領域の設定
  CB_ADDSTRING             = $0143; // リストボックスへの文字列の追加 
  CB_DELETESTRING          = $0144; // リストボックスの文字列の削除
  CB_DIR                   = $0145; // リストボックスへのファイル一覧の設定
  CB_GETCOUNT              = $0146; // リストボックスの項目数の取得
  CB_GETCURSEL             = $0147; // リストボックスの選択項目インデックスの取得
  CB_GETLBTEXT             = $0148; // 任意リスト項目の文字列を取得
  CB_GETLBTEXTLEN          = $0149; // 任意リスト項目の文字列の長さを取得
  CB_INSERTSTRING          = $014A; // リストボックスへの文字列の挿入
  CB_RESETCONTENT          = $014B; // リストボックスとエディットの全内容をクリア
  CB_FINDSTRING            = $014C; // リストボックス項目内の文字列検索
  CB_SELECTSTRING          = $014D; // 文字列を検索し見つかったら選択してエディットに表示
  CB_SETCURSEL             = $014E; // 特定項目を選択してエディットに表示
  CB_SHOWDROPDOWN          = $014F; // ドロップダウンリストの表示のスイッチ
  CB_GETITEMDATA           = $0150; // 項目に関連づけられた32ビットデータの取得
  CB_SETITEMDATA           = $0151; // 項目に関連づける32ビットデータの設定
  CB_GETDROPPEDCONTROLRECT = $0152; // ドロップダウンリストの座標を取得 
  CB_SETITEMHEIGHT         = $0153; // 項目の高さの設定
  CB_GETITEMHEIGHT         = $0154; // 項目の高さの取得
  CB_SETEXTENDEDUI         = $0155; // 拡張UIの設定
  CB_GETEXTENDEDUI         = $0156; // 拡張UIの取得
  CB_GETDROPPEDSTATE       = $0157; // ドロップダウンリストの表示状況の取得
  CB_FINDSTRINGEXACT       = $0158; // リストボックス項目内の文字列検索
  CB_SETLOCALE             = 345;   // ロケールの設定
  CB_GETLOCALE             = 346;   // ロケールの取得
  CB_GETTOPINDEX           = 347;   // リストボックスの可視領域の最初の項目インデックスの取得
  CB_SETTOPINDEX           = 348;   // リストボックスの可視領域の最初の項目インデックスの設定
  CB_GETHORIZONTALEXTENT   = 349;   // リストボックスの水平スクロール領域の取得
  CB_SETHORIZONTALEXTENT   = 350;   // リストボックスの水平スクロール領域の設定
  CB_GETDROPPEDWIDTH       = 351;   // ドロップダウンリストの横幅の取得
  CB_SETDROPPEDWIDTH       = 352;   // ドロップダウンリストの横幅の設定
  CB_INITSTORAGE           = 353;   // リストボックスのメモリの初期化
  CB_MSGMAX                = 354;   //  

  WM_SETTEXT          = $000C;
  WM_GETTEXT          = $000D;
  WM_GETTEXTLENGTH    = $000E;

CB_ プリフィックス以外は、 リストボックスやエディットに送るメッセージとほぼ同じだ。 他のコントロールと同様、 コンボボックスもイベントを親ウィンドウのウィンドウ関数に WM_COMMAND メッセージとともに以下の通知コードを送って知らせる。

const
  CBN_ERRSPACE   = (-1);//メモリ割り当てに失敗した。
  CBN_SELCHANGE  = 1;   //リストの選択項目が変更された。
  CBN_DBLCLK     = 2;   //CBS_SIMPLE のリスト項目がダブルクリックされた。
  CBN_SETFOCUS   = 3;   //フォーカスを得ようとしている。
  CBN_KILLFOCUS  = 4;   //フォーカスを失おうとしている。
  CBN_EDITCHANGE = 5;   //エディットのテキストが変更され、表示も更新された。
  CBN_EDITUPDATE = 6;   //エディットのテキストが変更され、表示は更新されていない。
  CBN_DROPDOWN   = 7;   //リストがドロップダウンされようとしている。
  CBN_CLOSEUP    = 8;   //リストが非表示にされた。
  CBN_SELENDOK   = 9;   //リストの選択項目がエディットに受け入れられた。
  CBN_SELENDCANCEL = 10;//選択項目がキャンセルされた。

以上の知識を使ってコードでコンボボックスを作ってみよう。

  1. エクスプローラを使って、新しいフォルダを 1301ComboBox01 という名前でつくる。
  2. 作ったフォルダに、エクスプローラを使って 1205FakeFakeHTML フォルダの Skeleton03.dprUtilFunc.pasGDIObject.pasUtilClass.pasAPIControl.pasAPIWindow.pas をコピーする。
  3. コピーした Skeleton03.dpr をダブルクリックして Delphi を立ち上げる。
  4. コードエディタに Skeleton03.dpr のコードがあることを確認してから、[ファイル]メニューから[プロジェクトに名前を付けて保存]を選んで ComboBox01.dpr という名前で保存する。
  5. コードエディタで以下のように編集する。
  6. [F.9]でコンパイルして実行する。

以下に ComboBox01.dpr の全リストを示す

program ComboBox01;

uses
  windows,
  messages,
  UtilFunc in 'UtilFunc.pas',
  UtilClass in 'UtilClass.pas',
  GDIObject in 'GDIObject.pas',
  APIWindow in 'APIWindow.pas',
  APIControl in 'APIControl.pas';

//------------------------------------------------------------
//             Global Types, Constants and Variables
//------------------------------------------------------------
const
  SIMPLE = 600;
  DROPDOWN = 601;
  DROPDOWNLIST = 602;

var
  MainWnd: TSDIMainWindow;

  hSimple,hDropDown,hDropDownList: HWND;

  L1,L2,L3: TAPILabel;

//------------------------------------------------------------
//             Helper Handlers
//------------------------------------------------------------
function GetText(hComboBox: HWND): string;
var
  s:string;
  Len: integer;
begin
  SetLength(s,256);
  Len := SendMessage(hComboBox,WM_GETTEXT,256,integer(PChar(s)));
  SetLength(s,Len);
  result := s;
end;

function GetSelString(hComboBox: HWND): string;
var
  SelIndex,Len: integer;
  s: string;
begin
  SelIndex := SendMessage(hComboBox,CB_GETCURSEL,0,0);
  Len := SendMessage(hComboBox,CB_GETLBTEXTLEN, SelIndex,0);
  SetLength(s,Len);
  SendMessage(hComboBox,CB_GETLBTEXT, SelIndex, integer(PChar(s)));
  Result := s;
end;

//------------------------------------------------------------
//             Object Event Handlers
//------------------------------------------------------------

//------------------------------------------------------------
//             Main Window Event Handlers
//------------------------------------------------------------
procedure MainCreate(var Msg: TMessage);
var
  hWindow: HWND;
  i: integer;
  s: string;
begin
  hWindow := MainWnd.Handle;
  MakeInitMenu(hWindow);

  L1 := CreateLabel(hWindow,10,10,150,30,'');
  L1.Margin := 6;

  L2 := CreateLabel(hWindow,170,10,150,30,'');
  L2.Margin := 6;

  L3 := CreateLabel(hWindow,330,10,150,30,'');
  L3.Margin := 6;

  hSimple := CreateWindowEx(0,'COMBOBOX',nil,
                            $54010240 or WS_VSCROLL or CBS_SIMPLE,
                            10,50,150,180,
                            hWindow,SIMPLE,
                            hInstance,
                            nil);
  hDropDown := CreateWindowEx(0,'COMBOBOX',nil,
                            $54010240 or WS_VSCROLL or CBS_DROPDOWN,
                            170,50,150,180,
                            hWindow,DROPDOWN,
                            hInstance,
                            nil);
  hDropDownList := CreateWindowEx(0,'COMBOBOX',nil,
                            $54010240 or WS_VSCROLL or CBS_DROPDOWNLIST,
                            330,50,150,180,
                            hWindow,DROPDOWNLIST,
                            hInstance,
                            nil);

  for i := 1 to 12 do begin
    s := 'Item '+AIntToStr(i);
    SendMessage(hSimple,CB_ADDSTRING,0,integer(PChar(s)));
    SendMessage(hDropDown,CB_ADDSTRING,0,integer(PChar(s)));
    SendMessage(hDropDownList,CB_ADDSTRING,0,integer(PChar(s)));
  end;

  with MainWnd do begin
    ClientWidth := 490;
    ClientHeight := 240;
    Center;
  end;
end;

procedure OnTest(hWindow: HWND);
var
  s: string;
begin
  SendMessage(hSimple,CB_DELETESTRING,3,0);
  s := 'これが挿入文';
  SendMessage(hDropDown,CB_INSERTSTRING,3,integer(PChar(s)));
end;

procedure MainCommand(var Msg: TMessage);
begin
  Msg.Msg := MainWnd.Handle;
  InitMenuCommand(Msg,OnTest);
  if (Msg.LParam <> 0 ) then  // from ComboBox
    case Msg.WParamLo of
      SIMPLE:begin
        case Msg.WParamHi of
          CBN_EDITCHANGE:L1.Text := GetText(hSimple);
          CBN_SELCHANGE:L1.Text := GetSelString(hSimple);
        end;
      end;
      DROPDOWN:begin
        case Msg.WParamHi of
          CBN_EDITCHANGE:L2.Text := GetText(hDropDown);
          CBN_SELCHANGE:L2.Text := GetSelString(hDropDown);
        end;
      end;
      DROPDOWNLIST:begin
        case Msg.WParamHi of
          CBN_SELCHANGE:L3.Text := GetText(hDropDownList);
        end;
      end;
    end; // case
end;

procedure MainDestroy(var Msg: TMessage);
begin
  PostQuitMessage(0);
end;

//------------------------------------------------------------
//             Main Procedure
//------------------------------------------------------------
begin

  MainWnd := TSDIMainWindow.Create(0,GetExeName);
  with MainWnd do begin
    OnCreate := MainCreate;
    OnDestroy := MainDestroy;
    OnCommand := MainCommand;
  end;
  MainWnd.DoCreate;

  Halt(MessageLoopNormal);

end.

DownloadExe.gifComboBox01.zip(16.036kB)
ComboBox01.gif

実行すると右図のようになる。 三つのコンボボックスは左から、CBS_SIMPLECBS_DROPDOWNCBS_DROPDOWNLIST のスタイルのものだ。 最初の二つは、エディットにキャレットを置いて任意の文字を入力できる。 エディットの文字列が変化するたびに上のラベルに変更された文字列が表示される。 CBS_DROPDOWNLIST のスタイルのコンボボックスはエディットに入力できない。リストから選択された文字列を表示するだけだ。 どのスタイルのコンボボックスもリストの選択を変更するたびに、エディットの表示が更新され、ラベルにも同じ文字列が表示されることが確かめられる。 Test! メニューを選択すると、左のコンボボックスのリストの4番目(ゼロベースのインデックスでは3)の項目が削除される。 さらに中央のコンボボックスのリストの4番目に新しい文字列が挿入される。

コードを見ていこう。MainCreate( ) ハンドラでは、 ラベルを三つ作ったあとで、CreateWindowEx( ) APIでコンボボックスを三つ作っている。 クラス名には、 Win32 で登録済みのウィンドウクラスである 'COMBOBOX' を指定している。 ウィンドウスタイルは、上記のVCLの値を参考にして $54010240 or WS_VSCROLL の後に、それぞれのコンボボックススタイルを追加している。 各コンボボックスのコントロールIDは、定数である SIMPLEDROPDOWNDROPDOWNLIST を割り当てている。ウィンドウハンドルは、対応するグローバル変数に格納される。 コンボボックスを作成したあとで各コントロールに CB_ADDSTRING メッセージを送って、12個の項目を追加している。

CB_ADDSTRING 
wParam = 0;                         // not used; must be zero 
lParam = (LPARAM) (LPCTSTR) lpsz;    // address of string to add 

戻り値には、追加された項目のリストにおけるゼロベースのインデックスが返される。エラーが起きると CB_ERRCB_ERRSPACE が返される。 MainCommand( ) ハンドラでは、コンボボックスが親のウィンドウ関数に送ってきた WM_COMMAND を処理している。 コントロールからのメッセージであることを確認してから、 wParam の下位ワードを参照してコントロールIDごとに処理を分ける。 右側の CS_DROPDOWNLIST スタイルのコンボボックスはエディットを編集できないのでリストの選択項目が変更された通知である CBN_SELCHANGE だけをトラップしている。 ヘルパ関数 GetText( ) では、指定のコンボボックスに WM_GETTEXT メッセージを送って、エディットに表示される文字列を取得している。 ところが、CBS_SIMPLECBS_DROPDOWN のスタイルを持つコンボボックスでは、 CBN_SELCHANGECBN_SELENDOK の通知で、GetText( ) で文字列を取得すると直前にエディットに表示されていた文字列が得られるので、望みの文字列が得られない。 ここでは、ヘルパ関数 GetSelString( )CB_GETLBTEXT を送ってリストで選択されている項目の文字列を取得している。

CB_GETLBTEXT 
wParam = (WPARAM) index;                // item index 
lParam = (LPARAM) (LPCSTR) lpszBuffer;  // address of buffer 

戻り値は、コピーされた文字列の長さをバイト単位で表したものだ。 ただし、ヌルターミネイトの分は含まれていない。 現在選択されている項目のインデックスは CB_GETCURSEL メッセージで取得する。

CB_GETCURSEL 
wParam = 0;     // not used; must be zero 
lParam = 0;     // not used; must be zero 

項目の文字列の長さは、CB_GETLBTEXTLEN メッセージを送って取得する。

CB_GETLBTEXTLEN 
wParam = (WPARAM) index;  // item index 
lParam = 0;  

以上の三つのメッセージを送って、選択項目のインデックスと文字列の長さを取得してバッファを適切に用意してから、選択項目の文字列を取得している。 選択項目が変化してエディットの表示が更新されても CBN_EDITCHANGE メッセージを送ってこないので、CBN_SELCHANGE をトラップする必要がある。 CBN_EDITCHANGE の通知は、 キャレットを置いてキー入力により文字列が変更されたときに送られる。 このとき WM_GETTEXT を送って取得される文字列は、 エディットの表示が正しく反映されたものだ。 Test! メニューで実行されるリスト項目への挿入・削除には、CB_INSERTSTRINGCB_DELETESTRING を送ることで実現する。

CB_INSERTSTRING 
wParam = (WPARAM) index;         // item index 
lParam = (LPARAM) (LPCTSTR) lpsz; // address of string to insert 

CB_DELETESTRING 
wParam = (WPARAM) index;  // item to delete 
lParam = 0;

どちらの index にも挿入または削除する位置を示すゼロベースの項目インデックスを指定する。 上述の CB_ADDSTRING メッセージとあわせて、これで項目の追加・削除・挿入の仕方が分かった。 通知メッセージのタイミングにあわせて、エディットの文字列やリストの選択項目の文字列の取得の仕方も分かった。

redball.gifこの章の目次へ戻る

13−2 コンボボックスのラッパークラス

前節でテストしたコンボボックスコントロールのラッパークラスを作ろう。 まず最初に、三つのスタイルに共通の性質をもつ TAPIComboBoxBase クラスを作成し、エディットの編集入力ができないのでイベントの数が一つ少ない CBS_DROPDOWNLIST スタイルのコンボボックスのラッパークラス TAPIDDLCombo クラスをこの TAPIComboBoxBase から派生して作る。 つぎに、CBS_SIMPLE スタイルのコンボボックスに相当するクラス TAPISMPComboTAPIComboBoxBase から派生し、さらにこの派生クラスから CBS_DROPDOWN スタイルのコンボボックスのラッパークラス TAPIDDCombo 派生して作ることにしよう。

  1. エクスプローラを使って、新しいフォルダを 1302ComboBox02 という名前でつくる。
  2. 作ったフォルダに、エクスプローラを使って 1301ComboBox01 フォルダの Skeleton03.dprUtilFunc.pasGDIObject.pasUtilClass.pasAPIControl.pasAPIWindow.pas をコピーする。
  3. コピーした Skeleton03.dpr をダブルクリックして Delphi を立ち上げる。
  4. コードエディタに Skeleton03.dpr のコードがあることを確認してから、[ファイル]メニューから[プロジェクトに名前を付けて保存]を選んで ComboBox02.dpr という名前で保存する。
  5. [Ctrl]+[f12] キーを押して、表示されるダイアログから APIControl.pas を選んでコードエディタに表示させる。
  6. コードエディタで以下のように編集する。
  7. [F.9]でコンパイルして実行する。

まず最初に TAPIControl.pas に三つのクラスを実装しよう。宣言は以下のようになる。

//--------- TAPIComboBoxBase --------------------------------------------
TAPIComboBoxBase = class(TAPIControl)
private
  FResultText: string;
  FOnSelChange: TNotifyMessage;
  function GetSelText:string;
  function GetSelIndex:integer;
  procedure SetSelIndex(index:integer);
  function GetCount: integer;
  function GetItemData(index:integer):integer;
  procedure SetItemData(index,value: integer);
protected
public
  function AddString(value: string):integer;
  procedure DeleteString(index:integer);
  function InsertString(value:string;index:integer):integer;
  function GetText(index: integer):string;
  procedure Clear;
  property SelText: string read GetSelText;
  property ResultText: string read FResultText;
  property SelIndex: integer read GetSelIndex write SetSelIndex;
  property Count: integer read GetCount;
  property ItemData[index:integer]:integer read GetItemData write SetItemData;
  property OnSelChange: TNotifyMessage read FOnSelChange write FOnSelChange;
end;

//--------- TAPIDDLCombo --------------------------------------------
TAPIDDLCombo = class(TAPIComboBoxBase)
private
protected
  procedure APICreateParams(var Params: TAPICreateParams); override;
public
  procedure DefaultHandler(var Message); override;
  procedure ShowDropDown(value:Boolean);
end;

//--------- TAPISMPCombo --------------------------------------------
TAPISMPCombo = class(TAPIComboBoxBase)
private
  FOnEditChange: TNotifyMessage;
  function GetEditText:string;
  procedure SetEditText(value:string);
protected
  procedure APICreateParams(var Params: TAPICreateParams); override;
public
  procedure DefaultHandler(var Message); override;
  property EditText: string read GetEditText write SetEditText;
  property OnEditChange: TNotifyMessage read FOnEditChange write FOnEditChange;
end;

//---------- TAPIDDCombo --------------------------------------------
TAPIDDCombo = class(TAPISMPCombo)
protected
  procedure APICreateParams(var Params: TAPICreateParams); override;
public
  procedure ShowDropDown(value:Boolean);
end;

TAPIComboBoxBase クラスでは、コンボボックスのほとんどの性質をカプセル化しているが、上にあげたコンボボックス特有のメッセージのうち無視しているものもある。 リストボックスでの文字列項目の追加・挿入・削除・全削除の他に、任意項目の文字列を得る GetText( ) や項目数を得る Count プロパティーや選択文字列や項目インデックスを取得するプロパティーもある。 ItemData プロパティーは、リスト項目に任意の32ビット値を設定取得できる。 これから派生する三つの具体クラスは、ウィンドウスタイルを設定する APICreateParams( )WM_COMMAND メッセージを捕まえてイベントハンドラを呼び出す DefaultHandler( ) 以外に2,3のプロパティーやメソッドを追加するだけでできあがる。 ResutText プロパティーは、リストボックスの選択項目の変化やエディットの内容の変化のいずれにも、結果として得られる文字列を保持している。

実装部を以下に示す。

//--------- TAPIComboBoxBase --------------------------------------------
function TAPIComboBoxBase.AddString(value: string):integer;
begin
  result := SendMessage(FHandle,CB_ADDSTRING,0,integer(PChar(value)));
end;

procedure TAPIComboBoxBase.DeleteString(index:integer);
begin
  SendMessage(FHandle,CB_DELETESTRING,index,0);
end;

function TAPIComboBoxBase.InsertString(value:string;index:integer):integer;
begin
  result := SendMessage(FHandle,CB_INSERTSTRING,index,integer(PChar(value)));
end;

function TAPIComboBoxBase.GetSelText:string;
begin
  Result := GetText(SelIndex);
end;

function TAPIComboBoxBase.GetSelIndex:integer;
begin
  result := SendMessage(FHandle,CB_GETCURSEL,0,0);
end;

procedure TAPIComboBoxBase.SetSelIndex(index:integer);
var
  AMsg: TMessage;
begin
  if index = SelIndex then exit;
  SendMessage(FHandle,CB_SETCURSEL,index,0);
  AMsg.Msg := WM_COMMAND;
  AMsg.WParamHi := CBN_SELCHANGE;
  AMsg.WParamLo :=  FID;
  AMsg.LParam := FHandle;
  Dispatch(AMsg);
end;

function TAPIComboBoxBase.GetText(index: integer):string;
var
  s:string;
  Len: integer;
begin
  Len := SendMessage(FHandle,CB_GETLBTEXTLEN,index,0);
  SetLength(s,Len);
  SendMessage(FHandle,CB_GETLBTEXT,index,integer(PChar(s)));
  result := s;
end;

procedure TAPIComboBoxBase.Clear;
begin
  SendMessage(FHandle,CB_RESETCONTENT,0,0);
end;

function TAPIComboBoxBase.GetCount:integer;
begin
  result := SendMessage(FHandle,CB_GETCOUNT,0,0);
end;

function TAPIComboBoxBase.GetItemData(index:integer):integer;
begin
  result := SendMessage(FHandle,CB_GETITEMDATA,index,0);
end;

procedure TAPIComboBoxBase.SetItemData(index,value: integer);
begin
  SendMessage(FHandle,CB_SETITEMDATA,index,value);
end;

//--------- TAPIDDLCombo --------------------------------------------
procedure TAPIDDLCombo.APICreateParams(var Params: TAPICreateParams);
begin
  with Params do begin
    Caption := '';
    Style := $54010240 or WS_VSCROLL or CBS_DROPDOWNLIST;
    ExStyle := WS_EX_CLIENTEDGE;
    X := 50;
    Y := 50;
    Width := 150;
    Height := 32;
    WinClassName := 'COMBOBOX';
  end;
end;

procedure TAPIDDLCombo.DefaultHandler(var Message);
var
  AMsg: TMessage;
begin
  AMsg := TMessage(message);

  case AMsg.Msg of

    WM_COMMAND:begin
      if (AMsg.LParam <> LPARAM(FHandle)) then exit;
      case AMsg.WParamHi of
        CBN_SELCHANGE: begin
          AMsg.Msg := UINT(self);
          FResultText := SelText;
          if Assigned(FOnSelChange) then OnSelChange(AMsg);
        end;
      end;
      if AMsg.WParamHi = WORD(CBN_ERRSPACE) then
        MessageBox(Parent,'ComboBox Control Buffer Full!','Warning',MB_OK);
    end;

  end; // case

end;

procedure TAPIDDLCombo.ShowDropDown(value:Boolean);
begin
  if value then
    SendMessage(FHandle,CB_SHOWDROPDOWN,1,0)
  else
    SendMessage(FHandle,CB_SHOWDROPDOWN,0,0);
end;

//--------- TAPISMPCombo --------------------------------------------
procedure TAPISMPCombo.APICreateParams(var Params: TAPICreateParams);
begin
  with Params do begin
    Caption := '';
    Style := $54010240 or WS_VSCROLL or CBS_SIMPLE;
    ExStyle := WS_EX_CLIENTEDGE;
    X := 50;
    Y := 50;
    Width := 150;
    Height := 32;
    WinClassName := 'COMBOBOX';
  end;
end;

procedure TAPISMPCombo.DefaultHandler(var Message);
var
  AMsg: TMessage;
begin
  AMsg := TMessage(message);

  case AMsg.Msg of

    WM_COMMAND:begin
      if (AMsg.LParam <> LPARAM(FHandle)) then exit;
      case AMsg.WParamHi of
        CBN_EDITCHANGE: begin
          AMsg.Msg := UINT(self);
          FResultText := EditText;
          if Assigned(FOnEditChange) then OnEditChange(AMsg);
        end;
        CBN_SELCHANGE: begin
          AMsg.Msg := UINT(self);
          FResultText := SelText;
          if Assigned(FOnSelChange) then OnSelChange(AMsg);
        end;
      end;
      if AMsg.WParamHi = WORD(CBN_ERRSPACE) then
        MessageBox(Parent,'ComboBox Control Buffer Full!','Warning',MB_OK);
    end;

  end; // case

end;

function TAPISMPCombo.GetEditText:string;
var
  s:string;
  Len: integer;
begin
  SetLength(s,256);
  Len := SendMessage(FHandle,WM_GETTEXT,256,integer(PChar(s)));
  SetLength(s,Len);
  result := s;
end;

procedure TAPISMPCombo.SetEditText(value:string);
var
  AMsg: TMessage;
begin
  SetWindowText(FHandle,PChar(value));
  AMsg.Msg := WM_COMMAND;
  AMsg.WParamHi := CBN_EDITCHANGE;
  AMsg.WParamLo :=  FID;
  AMsg.LParam := FHandle;
  Dispatch(AMsg);
end;

//---------- TAPIDDCombo --------------------------------------------
procedure TAPIDDCombo.APICreateParams(var Params: TAPICreateParams);
begin
  inherited APICreateParams(Params);
  Params.Style := $54010240 or WS_VSCROLL or CBS_DROPDOWN;
end;

procedure TAPIDDCombo.ShowDropDown(value:Boolean);
begin
  if value then
    SendMessage(FHandle,CB_SHOWDROPDOWN,1,0)
  else
    SendMessage(FHandle,CB_SHOWDROPDOWN,0,0);
end;

これらのコードは、前節で説明した事を参照すれば容易に理解できるだろう。いずれもイベントハンドラを呼び出す前に、ResultText が正しい文字列を保持するようにしている。
選択項目の初期値を設定するには、 SelIndex プロパティーに希望する項目のインデックスを設定する。 SelIndex プロパティーの write アクセスメソッド SetSelIndex( ) では、CB_SETCURSEL メッセージを送って選択項目がコードで変更されるので、ここからも DefaultHandler( ) にメッセージを送って OnSelChange イベントを呼び出している。

CB_SETCURSEL 
wParam = (WPARAM) index;  // item index 
lParam = 0; 

GetText( ) メソッドを使うと、任意のインデックスの文字列を取得できる。 Clear メソッドでは、 CB_RESETCONTENT メッセージを送ってリストの全項目とエディットの文字列を削除する。

CB_RESETCONTENT 
wParam = 0;     // not used; must be zero 
lParam = 0;     // not used; must be zero 

ShowDropDown メソッドを実行すると、CB_SHOWDROPDOWN を送ってコードによりドロップダウンリストを表示することができる。

CB_SHOWDROPDOWN 
wParam = (WPARAM) (BOOL) fShow;     // the show/hide flag 
lParam = 0;                         // not used; must be zero

fShow にゼロ以外を設定するとリストが表示され、ゼロを設定するとリストが閉じられる。 リストボックスの項目への任意の32ビット値の取得・設定は以下のメッセージを使って行う。

CB_GETITEMDATA 
wParam = (WPARAM) index;  // item index 
lParam = 0;               // not used; must be zero 

CB_SETITEMDATA 
wParam = (WPARAM) index;            // item index 
lParam = (LPARAM) (DWORD) dwData;   // item data

いずれも wParam に設定・取得する項目のインデックスを設定する。 最後に、これらのクラスのインスタンスを作成する関数を APIControl.pas につくろう。宣言部を以下に示す。

function CreateDDLCombo(hParent: HWND; x,y,cx,cy: integer;
                      sOnSelChange:TNotifyMessage): TAPIDDLCombo;

function CreateSMPCombo(hParent: HWND; x,y,cx,cy: integer;
                      sOnSelChange:TNotifyMessage): TAPISMPCombo;

function CreateDDCombo(hParent: HWND; x,y,cx,cy: integer;
                      sOnSelChange:TNotifyMessage): TAPIDDCombo;

実装部を以下に示す。

function CreateDDLCombo(hParent: HWND; x,y,cx,cy: integer;
                      sOnSelChange:TNotifyMessage): TAPIDDLCombo;
begin
  result := TAPIDDLCombo.Create(hParent);
  with result do begin
    Dimension := SetDim(x,y,cx,cy);
    OnSelChange := sOnSelChange;
  end;
end;

function CreateSMPCombo(hParent: HWND; x,y,cx,cy: integer;
                      sOnSelChange:TNotifyMessage): TAPISMPCombo;
begin
  result := TAPISMPCombo.Create(hParent);
  with result do begin
    Dimension := SetDim(x,y,cx,cy);
    OnSelChange := sOnSelChange;
  end;
end;

function CreateDDCombo(hParent: HWND; x,y,cx,cy: integer;
                      sOnSelChange:TNotifyMessage): TAPIDDCombo;
begin
  result := TAPIDDCombo.Create(hParent);
  with result do begin
    Dimension := SetDim(x,y,cx,cy);
    OnSelChange := sOnSelChange;
  end;
end;

つぎに ComboBox02.dpr にこれらのインスタンスを作ってテストするプログラムを書こう。 以下に全リストを示す。

program ComboBox02;

uses
  windows,
  messages,
  UtilFunc in 'UtilFunc.pas',
  UtilClass in 'UtilClass.pas',
  GDIObject in 'GDIObject.pas',
  APIWindow in 'APIWindow.pas',
  APIControl in 'APIControl.pas';

//------------------------------------------------------------
//             Global Types, Constants and Variables
//------------------------------------------------------------
var
  MainWnd: TSDIMainWindow;

  DDLCombo: TAPIDDLCombo;
  SMPCombo: TAPISMPCombo;
  DDCombo: TAPIDDCombo;
  L1,L2,L3: TAPILabel;

//------------------------------------------------------------
//             Helper Handlers
//------------------------------------------------------------

//------------------------------------------------------------
//             Object Event Handlers
//------------------------------------------------------------
procedure SMPSelChange(var AMsg: TMessage);
begin
  L1.Text := SMPCombo.ResultText;
end;

procedure DDSelChange(var AMsg: TMessage);
begin
  L2.Text := DDCombo.ResultText;
end;

procedure DDLSelChange(var AMsg: TMessage);
begin
  L3.Color := DDLCombo.ItemData[DDLCombo.SelIndex];
  L3.Text := DDLCombo.ResultText;
end;

//------------------------------------------------------------
//             Main Window Event Handlers
//------------------------------------------------------------
procedure MainCreate(var Msg: TMessage);
var
  hWindow: HWND;
  i: integer;
  s: string;
begin
  hWindow := MainWnd.Handle;
  MakeInitMenu(hWindow);

  L1 := CreateLabel(hWindow,10,10,150,30,'');
  L1.Margin := 6;

  L2 := CreateLabel(hWindow,170,10,150,30,'');
  L2.Margin := 6;

  L3 := CreateLabel(hWindow,330,10,150,30,'');
  L3.Margin := 6;

  SMPCombo := CreateSMPCombo(hWindow,10,50,150,180,SMPSelChange);
  SMPCombo.OnEditChange := SMPSelChange;

  DDCombo := CreateDDCombo(hWindow,170,50,150,180,DDSelChange);
  DDCombo.OnEditChange := DDSelChange;

  DDLCombo := CreateDDLCombo(hWindow,330,50,150,180,DDLSelChange);

  for i := 1 to 12 do begin
    s := 'Item '+AIntToStr(i);
    SMPCombo.AddString(s);
    DDCombo.AddString(s);
  end;
  SMPCombo.SelIndex := 3;
  DDCombo.SelIndex := 3;

  with DDLCombo do begin
    ItemData[AddString('Lime')] := clrLIme;
    ItemData[AddString('Yellow')] := clrYellow;
    ItemData[AddString('Aqua')] := clrAqua;
    ItemData[AddString('Fuchsia')] := clrFuchsia;
  end;
  DDLCombo.SelIndex := 0;

  with MainWnd do begin
    ClientWidth := 490;
    ClientHeight := 240;
    Center;
  end;
end;

procedure OnTest(hWindow: HWND);
begin
  DDCombo.ShowDropDown(true);
end;

procedure MainCommand(var Msg: TMessage);
begin
  Msg.Msg := MainWnd.Handle;
  InitMenuCommand(Msg,OnTest);
end;

procedure MainDestroy(var Msg: TMessage);
begin
  PostQuitMessage(0);
end;

//------------------------------------------------------------
//             Main Procedure
//------------------------------------------------------------
begin

  MainWnd := TSDIMainWindow.Create(0,GetExeName);
  with MainWnd do begin
    OnCreate := MainCreate;
    OnDestroy := MainDestroy;
    OnCommand := MainCommand;
  end;
  MainWnd.DoCreate;

  Halt(MessageLoopNormal);

end.

DownloadExe.gifComboBox02.zip(17.427kB)
ComboBox02.gif

実行すると右図のようになる。 MainCreate( ) ハンドラでは、三つのラベルと三種類のコンボボックスを作成している。 前節のコードベースの場合と異なり、 リストボックスの選択状態の変化とエディットの変化の両方のイベントで ResultText を参照できるので同じハンドラで処理できる。 右側のドロップダウンリストのコンボボックスでは、項目の文字列に色を設定し、対応する COLORREF 型の値を ItemData プロパティーを通して設定している。 選択項目の変化に対するイベントハンドラを以下に示す。

procedure DDLSelChange(var AMsg: TMessage);
begin
  L3.Color := DDLCombo.ItemData[DDLCombo.SelIndex];
  L3.Text := DDLCombo.ResultText;
end;

このように、選択項目のインデックスを取得して、それからそれに関連づけられている32ビットデータをラベルの色に設定している。

redball.gifこの章の目次へ戻る

13−3 ドライブコンボボックス

コンボボックスの特徴は、リストボックスより配置にスペースをとらないことであり、リストが保持している文字列の一覧から簡単に一つだけを選べることだ。このコントロールの応用として、デフォルトでシステムにインストールされているディスクドライブの一覧をリストに表示するドライブコンボボックスのクラス TAPIDriveCombo を作ろう。 また、ドライブの一覧から一つ選ぶと、このクラスのプロパティーを通じて、そのドライブのラベルや容量、未使用容量などの情報をアクセスできるようにしよう。ドライブの一覧を得るためのAPIGetDriveType( ) だ。

  function GetDriveType(lpRootPathName: PChar): UINT; stdcall;

このAPIの唯一のパラメータには、ドライブのルートディレクトリを指定する文字列を指定する。例えば、

var
  s:string;
  ret:integer;

として

  s := 'C:\';
  ret := GetDriveType(PChar(s));

とする。このAPIの戻り値としては以下のような定数が windows.pas に宣言されている。

const
  DRIVE_UNKNOWN = 0;     //ドライブタイプを決定できない
  DRIVE_NO_ROOT_DIR = 1; //このルートディレクトリは存在しない
  DRIVE_REMOVABLE = 2;   //リムーバブルドライブである
  DRIVE_FIXED = 3;       //ハードディスクである
  DRIVE_REMOTE = 4;      //ネットワークドライブである
  DRIVE_CDROM = 5;       //CD-ROMドライブである
  DRIVE_RAMDISK = 6;     //ラムディスクである

システムに、対応するドライブがインストールされていないときは、1以下の値を返すのでこのときはコンボボックスのリストに加えない。 インストールされているドライブが分かったら、各ドライブの情報にアクセスするために、 GetVolumeInformation( )GetDiskFreeSpace( ) APIを使う。

  function GetVolumeInformation(lpRootPathName: PChar;
                            lpVolumeNameBuffer: PChar; 
                               nVolumeNameSize: DWORD; 
                          lpVolumeSerialNumber: PDWORD;
                  var lpMaximumComponentLength, 
                             lpFileSystemFlags: DWORD;
                        lpFileSystemNameBuffer: PChar; 
                           nFileSystemNameSize: DWORD): BOOL; stdcall;

lpRootPathName には、GetDriveType( )のパラメータと同じように 'C:\'などと指定する。 lpVolumeNameBuffer には、名前を受け取るバッファを指定する。 nVolumeNameSize には、受け取るバッファのサイズをバイト単位で指定する。 lpVolumeSerialNumber にはシリアル番号を受け取る DWORD 型変数のアドレスを指定する。 lpMaximumComponentLength はファイルシステムのパス名のうち \ 記号で挟まれている部分で使える最長の名前の大きさをバイト単位で受け取るために DWORD 型の変数を指定する。長いファイル名をサポートしているシステムでは、255 を受け取る。 lpFileSystemFlags には、以下の定数の組み合わせが返される。

  FS_CASE_IS_PRESERVED = FILE_CASE_PRESERVED_NAMES;//大文字・小文字を予約ずみ
  FS_CASE_SENSITIVE = FILE_CASE_SENSITIVE_SEARCH;  //大文字・小文字を区別する
  FS_UNICODE_STORED_ON_DISK = FILE_UNICODE_ON_DISK;//ファイル名にユニコードをサポート
  FS_PERSISTENT_ACLS = FILE_PERSISTENT_ACLS;       //ACLSを使用(NTなど)
  FS_VOL_IS_COMPRESSED = FILE_VOLUME_IS_COMPRESSED;//圧縮されている
  FS_FILE_COMPRESSION = FILE_FILE_COMPRESSION;     //ファイルベースの圧縮をサポート

  FILE_CASE_SENSITIVE_SEARCH          = $00000001; 
  FILE_CASE_PRESERVED_NAMES           = $00000002; 
  FILE_UNICODE_ON_DISK                = $00000004; 
  FILE_PERSISTENT_ACLS                = $00000008; 
  FILE_FILE_COMPRESSION               = $00000010; 
  FILE_VOLUME_IS_COMPRESSED           = $00008000; 

lpFileSystemNameBuffer は、そのボリュウムのファイルシステムの名前が返される。 筆者のシステムでは、ハードディスクは FATCD-ROM では CDFS が返ってきた。 nFileSystemNameSize は、システム名を受け取るバッファのサイズをバイト単位で指定する。ディスクの使用状況は GetDiskFreeSpace( ) APIで取得する。

  function GetDiskFreeSpace(lpRootPathName: PChar;
                   var lpSectorsPerCluster, 
                          lpBytesPerSector, 
                    lpNumberOfFreeClusters, 
                   lpTotalNumberOfClusters: DWORD): BOOL; stdcall;

lpRootPathName には、GetDriveType( ) のパラメータと同じように 'C:\'などと指定する。 lpSectorsPerClusterlpBytesPerSector は、クラスタあたりのセクタ数とセクタあたりのバイト数 lpNumberOfFreeClusterslpTotalNumberOfClusters は、未使用クラスタ数と前クラスタ数を受け取る DWORD 型の変数を指定する。

以上の知識を使って、 TAPIDriveCombo クラスを実装してテストするプログラムを作ろう。

  1. エクスプローラを使って、新しいフォルダを 1303DriveCombo という名前でつくる。
  2. 作ったフォルダに、エクスプローラを使って 1302ComboBox02 フォルダの Skeleton03.dprUtilFunc.pasGDIObject.pasUtilClass.pasAPIControl.pasAPIWindow.pas をコピーする。
  3. コピーした Skeleton03.dpr をダブルクリックして Delphi を立ち上げる。
  4. コードエディタに Skeleton03.dpr のコードがあることを確認してから、[ファイル]メニューから[プロジェクトに名前を付けて保存]を選んで DriveCombo.dpr という名前で保存する。
  5. [Ctrl]+[f12] キーを押して、表示されるダイアログから APIControl.pas を選んでコードエディタに表示させる。
  6. コードエディタで以下のように編集する。
  7. [F.9]でコンパイルして実行する。

まず最初に TAPIControl.pasTAPIDriveCombo クラスを実装しよう。宣言は以下のようになる。

//---------- TAPIDriveCombo -----------------------------------------
TAPIDriveCombo = class(TAPIDDLCombo)
private
  FRootDir: string;
  FVolumeName: string;
  FFileSystem: string;
  FSerialNumber: integer;
  FDiskSpace: integer;
  FUsedSpace: integer;
  FFreeSpace: integer;
protected
  procedure Initialize; override;
  function GetVolumeName(Root: string):string;
public
  procedure DefaultHandler(var Message); override;
  procedure GetVolumeInfo(Root: string);
  property RootDir: string read FRootDir;
  property VolumeName: string read FVolumeName;
  property FileSystem: string read FFileSystem;
  property SerialNumber: integer read FSerialNumber;
  property DiskSpace: integer read FDiskSpace;
  property UsedSpace: integer read FUsedSpace;
  property FreeSpace: integer read FFreeSpace;
end;

このクラスは、エディットが編集できない TAPIDDLCombo クラスから派生する。 読みとり専用のプロパティーが7個追加されているが、これはディスクの情報をユーザーに公開するためのものだ。 実装コードは以下のようになる。

//---------- TAPIDriveCombo -----------------------------------------
procedure TAPIDriveCombo.Initialize;
var
  i,r: integer;
  s,t: string;
begin
  inherited Initialize;

  for i := byte('A') to byte('Z') do begin
    s := Char(i)+':\';
    r := GetDriveType(PChar(s));
    case r of
      DRIVE_UNKNOWN:     t := 'Unknown';
      DRIVE_NO_ROOT_DIR: t := 'No Root';
      DRIVE_REMOVABLE:   t := 'Removable';
      DRIVE_FIXED:       t := GetVolumeName(s);
      DRIVE_REMOTE:      t := 'Network';
      DRIVE_CDROM:       t := 'CD_ROM';
      DRIVE_RAMDISK:     t := 'RamDisk';
    end;
    if r >1 then begin
      t := s+' '+t;
      AddString(t);
    end;
  end;
end;

procedure TAPIDriveCombo.DefaultHandler(var Message);
var
  AMsg: TMessage;
begin
  AMsg := TMessage(message);

  case AMsg.Msg of

    WM_COMMAND:begin
      if (AMsg.LParam <> LPARAM(FHandle)) then exit;
      case AMsg.WParamHi of
        CBN_SELCHANGE: begin
          FRootDir := Copy(SelText,1,3);
          GetVolumeInfo(FRootDir);
          AMsg.Msg := UINT(self);
          FResultText := SelText;
          if Assigned(FOnSelChange) then OnSelChange(AMsg);
        end;
      end;
      if AMsg.WParamHi = WORD(CBN_ERRSPACE) then
        MessageBox(Parent,'ComboBox Control Buffer Full!','Warning',MB_OK);
    end;

  end; // case

end;

function TAPIDriveCombo.GetVolumeName(Root: string):string;
var
  Vol,FileSys: array[0..100] of Char;
  SN,MaxN,FSF: DWORD;
begin
  GetVolumeInformation(PChar(Root),Vol,100,@SN,MaxN,FSF,FileSys,100);
  result := '['+Vol+']';
end;

procedure TAPIDriveCombo.GetVolumeInfo(Root: string);
var
  Vol,FileSys: array[0..100] of Char;
  SN,MaxN,FSF: DWORD;
  SectPerClstr,BytePerSect,FreeClstr,Clstr: DWORD;
begin
  GetVolumeInformation(PChar(Root),Vol,100,@SN,MaxN,FSF,FileSys,100);
  GetDiskFreeSpace(PChar(Root),SectPerClstr,BytePerSect,FreeClstr,Clstr);
  FRootDir := Root;
  FVolumeName := '['+Vol+']';
  FFileSystem := FileSys;
  FSerialNumber := SN;
  FDiskSpace := Clstr*BytePerSect*SectPerClstr;
  FFreeSpace := FreeClstr*BytePerSect*SectPerClstr;
  FUsedSpace := DiskSpace-FreeSpace;
end;

コードを見ていこう。 コンストラクタから呼び出される Initialize メソッドをオーバーライドして、最初に継承メソッドを実行した後、GetDriveType( ) を使ってインストールされているドライブを取得している。 その際、ドライブがハードディスクであるときは、GetVolumeName( ) メソッドを呼び出して、ドアライブ名を取得している。 オーバーライドした DefaultHandler( ) では、OnSelChange イベントを呼び出すまえに GetVolumeInfo( ) メソッドを呼び出し、読みとり専用プロパティーのフィールドに値を設定している。

これでこのクラスを実装できたので、いつもどおりインスタンス生成関数をつくろう。宣言部は、

function CreateDriveCombo(hParent: HWND; x,y,cx,cy: integer;
                      sOnSelChange:TNotifyMessage): TAPIDriveCombo;

となり、実装部は、

function CreateDriveCombo(hParent: HWND; x,y,cx,cy: integer;
                      sOnSelChange:TNotifyMessage): TAPIDriveCombo;
begin
  result := TAPIDriveCombo.Create(hParent);
  with result do begin
    Dimension := SetDim(x,y,cx,cy);
    OnSelChange := sOnSelChange;
  end;
end;

である。

それでは、TAPIDriveCombo クラスをテストするプログラムを DriveCombo.dpr に作成しよう。以下に全リストを示す。

program DriveCombo;

uses
  windows,
  messages,
  UtilFunc in 'UtilFunc.pas',
  UtilClass in 'UtilClass.pas',
  GDIObject in 'GDIObject.pas',
  APIWindow in 'APIWindow.pas',
  APIControl in 'APIControl.pas';

//------------------------------------------------------------
//             Global Types, Constants and Variables
//------------------------------------------------------------
var
  MainWnd: TSDIMainWindow;

  DriveCmb: TAPIDriveCombo;
  L1: TAPILabel;
  LB: TAPIListBox;

//------------------------------------------------------------
//             Helper Handlers
//------------------------------------------------------------

//------------------------------------------------------------
//             Object Event Handlers
//------------------------------------------------------------
procedure DriveSelChange(var AMsg: TMessage);
var
  FreePercent,UsedPercent: integer;
begin
  with DriveCmb do begin
    L1.Text := RootDir;
    LB.Clear;
    FreePercent := FreeSpace div (DiskSpace div 100);
    UsedPercent := 100 - FreePercent;
    LB.AddString(RootDir+#9#9+VolumeName);
    LB.AddString('Serial Number :'+#9+AIntToHex(SerialNumber,8));
    LB.AddString('File System :'+#9+FileSystem);
    LB.AddString(' ');
    LB.AddString('Disk Space :'+#9+AIntToStr(DiskSpace));
    LB.AddString(' ');
    LB.AddString('Used Space :'+#9+AIntToStr(UsedSpace)+
                  '  '+AIntToStr(UsedPercent)+'%');
    LB.AddString('Free Space :'+#9+AIntToStr(FreeSpace)+
                '  '+AIntToStr(FreePercent)+'%');
  end;
end;
//------------------------------------------------------------
//             Main Window Event Handlers
//------------------------------------------------------------
procedure MainCreate(var Msg: TMessage);
var
  hWindow: HWND;
begin
  hWindow := MainWnd.Handle;
  MakeInitMenu(hWindow);

  L1 := CreateLabel(hWindow,10,10,150,30,'');
  L1.Margin := 6;

  DriveCmb := CreateDriveCombo(hWindow,10,50,150,160,DriveSelChange);

  LB := CreateListBox(hWindow,170,10,250,200,nil);

  with MainWnd do begin
    ClientWidth := 430;
    ClientHeight := 220;
    Center;
  end;
end;

procedure OnTest(hWindow: HWND);
begin
  DriveCmb.SelIndex := 1;
end;

procedure MainCommand(var Msg: TMessage);
begin
  Msg.Msg := MainWnd.Handle;
  InitMenuCommand(Msg,OnTest);
end;

procedure MainDestroy(var Msg: TMessage);
begin
  PostQuitMessage(0);
end;

//------------------------------------------------------------
//             Main Procedure
//------------------------------------------------------------
begin

  MainWnd := TSDIMainWindow.Create(0,GetExeName);
  with MainWnd do begin
    OnCreate := MainCreate;
    OnDestroy := MainDestroy;
    OnCommand := MainCommand;
  end;
  MainWnd.DoCreate;

  Halt(MessageLoopNormal);

end.

DownloadExe.gifDriveCombo.zip(18.646kB)
DriveCombo.gif

実行すると、右図のようになる。 MainCreate( ) ハンドラでは、ラベルとドライブコンボとリストボックスを一つずつ作っている。DriveCmb のリストからドライブを選択すると、 OnSelChange イベントを通して DriveSelChange ハンドラが実行される。 このハンドラでは、 DriveCmb のプロパティーを読みとって、選択されたドライブの情報をリストボックスに表示している。

TAPIDriveCombo のようなクラスは、インスタンスを作るだけで特定の情報にアクセスできる。 このようなコンボボックスクラスで最も一般的に使われているのは、 システムにインストールされているフォントをリストに列挙するものだろう。 たいていのワープロアプリケーションではメインウィンドウの左上にフォントを設定するコンボボックスがあるはずである。 このようなクラスは、 後の章でフォントを扱うときに作ることにしよう。

redball.gifこの章の目次へ戻る

13−4 まとめ

本章では、最初にコンボボックスの三つのスタイルについてコードで作成し、それらを制御するAPIについて調べた。次に、これらのコンボボックスのラッパークラスを作成し、テストした。最後に、 システムに存在するドライブの一覧とその情報を取得する TAPIDriveCombo クラスを作成し、テストした。


ここまでのプログラムファイル

  1. Skeleton03.dpr (2 kB)
  2. UtilFunc.pas (10 kB)
  3. UtilClass.pas (31 kB)
  4. GDIObject.pas (16 kB)
  5. APIControl.pas (53 kB)
  6. APIWindow.pas (19 kB)



redball.gifこの章の目次へ戻る