Delphi Programming / Object Pascal
サイトのトップページへ リンクのページへ ロゴマーク
[掲載 2014年11月17日] [更新 2020年03月07日] Delphi サンプルプログラム集
128_ポップアップ表示する TListBox コンポーネント
動作確認等 Windows 7 U64(SP1) + Delphi XE5(UP2) Pro VCL-32
128_PopUpListBox.zip [1,814 KB] 2020年03月07日版 (EXE 同梱)







TListBox をポップアップ表示するコンポーネント


TListBox をポップアップ表示する例です.TListBox の部分はコンポーネントにしています.このコードは,下記のリンクに掲載されているコードを,筆者が整備したものです.

TListBox に以下の ShowBeneath メソッドと OnCloseUp イベントを追加しています.
ポップアップ表示するには ShowBeneath メソッドを使用します.ShowBeneath の引数は表示位置を設定するために使用します.OnCloseUp は TListBox が閉じた直後に発生するイベントです.このイベント内で,選択した項目を取得します.

MultiSelect の値が True の時は,クリックでは閉じません.[Enter] キー押下か TListBox 以外の場所をクリックすると閉じます.[ESC] キーで閉じると,ItemIndex の値は -1 となります.



ShowBeneath (ALeft, ATop AHeight)

引数 説明
ALeft Integer 表示開始位置の左端の X 座標値.スクリーン座標で指定する
ATop Integer 表示開始位置の上端の Y 座標値.スクリーン座標で指定する
AHeight Integer 省略可能
この値が指定されていると,画面の下端に表示する際にこの高さの分だけ上方から表示する


OnCloseUp (Sender)

引数 説明
Sender TObject 本コンポーネント


プロパティは以下を追加しています.全て public なプロパティです.

DropDownRows は可視状態 (クライアント領域) に表示する項目数ですが,設定した値にならないことがあります.この状態を確認するには,リストの項目数を少ない値にして DropDownRows の値をそれよりも大な値にします.その時にリスト表示の下部に余分な空白がある場合は temHeight が正常に機能していません.その場合は,表示に使用するフォントやフォントサイズ,ItemHeight の値を変えてみてください.
古バージョンの Delphi では,フォントサイズを奇数にすると有効になるという記述もありますが,筆者自身は詳細な確認をしていません.


プロパティ 説明
DefaultDraw Boolean デフォルトの描画処理にするかのフラグ.デフォルトは True
False にすると DrawItem イベントは発生しない.コンポーネント内部の描画コードで描画する
FocusRect Boolean フォーカスの枠線を描画するかのフラグ.デフォルトは True
SelBackColor TColor 選択行の背景色の指定.デフォルト値は $00FFD2A6
DefaultDraw が True の時は無効
SelFontColor TColor 選択行の背景色文字の色.デフォルトは clBlack
DefaultDraw が True の時は無効
DropDownRows Integer 可視領域の項目数 (行数).デフォルトは 8
この値でクライアント領域の高さが決まる


コントロールをポップアップ表示するには,サンプルの [126] [04] のように,対象のコントロールをフォームに配置して,そのフォームをボップアップ表示する方法もあります.フォームにコントロールを配置する方法は,デザインの変更等のカスタマイズが容易です.



[備考]
FireMonkey であれば,TPopup コンポーネントを使用して,同様な機能を実装することができます.





01_ポップアップ表示する TListBpx のテスト


コンポーネントのテストプログラムです.コンポーネントは実行時に生成してテストしています.
このテストでは,以下のコードでリストの文字列の行間を調整しています.これは,TListBox.Style の値を lbOwnerDrawFixed にすると TListBox.ItemHeightの値が有効になることを利用しています.


    // Style プロパティの値を lbOwnerDrawFixed にして ItemHeight の値を変更
    FplPopupListBox1.Style := lbOwnerDrawFixed;
    FplPopupListBox1.ItemHeight := 19;


このポップアップ表示する TListBox は,項目をクリックすすると TComboBox のように閉じます.通常の TListBox では,クリックは項目の選択だけです.



図1
EXE 実行開始画面
  • TEdit の右側のボタンをクリックすると TListBox を表示
  • これがデフォルトの描画
図2
リストの選択結果
  • 選択項目クリック,または [Enter] キー押下
  • [ESC] キーで閉じた場合は項目は選択されない
図3
DefaultDraw を False にした場合
  • 背景色は SelBackColor プロパティで変更可能
  • DefaultDraw を False にすると,DrawItem イベントは発生しない
図4
FocusRect も False にした場合
  • フォーカスの枠線を描画しない


下図ように,最下行のテキストが部分描画にならないようにするには,IntegralHeight プロパティの値を True にします.このプロパティのデフォルトは False です.


  FplPopupListBox1.IntegralHeight := True;


図5
IntegralHeight が False の場合
図6
IntegralHeight が True の場合

リスト1
ポップアップ表示する TListBox コンポーネントのテストプログラム
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ExtCtrls, plPopupListBox, Buttons;

type
  TForm1 = class(TForm)
    Edit1: TEdit;
    SpeedButton1: TSpeedButton;
    CheckBox1: TCheckBox;
    CheckBox2: TCheckBox;
    procedure SpeedButton1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    FplPopupListBox1 : TplPopupListBox;
    procedure OnplPopupListBox1CloseUp(Sender: TObject);
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

//=============================================================================
//  フォーム生成時の処理
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
  SpeedButton1.Caption := '▼';

  CheckBox1.Caption := 'デフォルト描画';
  CheckBox2.Caption := 'フォーカスの枠線';
  CheckBox1.Checked := True;
  CheckBox2.Checked := True;
end;

//=============================================================================
//  TplPopupListBox コンポをポップアップ表示
//  コンポーネントは実行時生成
//=============================================================================
procedure TForm1.SpeedButton1Click(Sender: TObject);
var
  LPos : TPoint;
begin
  if not Assigned(FplPopupListBox1) then begin
    FplPopupListBox1 := TplPopupListBox.Create(Self);
    FplPopupListBox1.Parent := self;

    // イベント用のメソッドを設定
    FplPopupListBox1.OnCloseUp := OnplPopupListBox1CloseUp;

    FplPopupListBox1.Width := Edit1.Width + 25;
    FplPopupListBox1.IntegralHeight := True;

    // Style プロパティの値を lbOwnerDrawFixed にして ItemHeight の値を変更
    FplPopupListBox1.Style := lbOwnerDrawFixed;
    FplPopupListBox1.ItemHeight := 19;
  end;

  //テスト用の表示項目を作成
  with FplPopupListBox1.Items do begin
    Clear;
    BeginUpdate;
    Add('東京都');
    Add('埼玉県');
    Add('神奈川県');
    Add('群馬県');
    Add('栃木県');
    Add('茨城県');
    Add('千葉県');
    Add('長野県');
    Add('山梨県');
    Add('岐阜県');
    Add('新潟県');
    Add('福島県');
    Add('山形県');
    Add('岩手県');
    Add('青森県');
    Add('沖縄県');
    Add('京都府');
    Add('大阪府');
    EndUpdate;
  end;

  // デフォルト描画とフォーカスの枠線描画の設定
  FplPopupListBox1.DefaultDraw := CheckBox1.Checked;
  FplPopupListBox1.FocusRect   := CheckBox2.Checked;


  // ShowBeneathメソッドで表示する
  // コントロールの左下の座標位置から表示する
  LPos := Self.ClientToScreen(Point(Edit1.Left, Edit1.Top));
  FplPopupListBox1.ShowBeneath(LPos.X, LPos.Y + Edit1.Height, Edit1.Height);
  FplPopupListBox1.ItemIndex := 2;
end;

//=============================================================================
//  TplPopupListBoxコンポのOnCloseUpイベント処理
//  項目をマウスクリックで選択するとコンポが閉じて,このイベントが発生する
//  [Enter], [ESC] キーでもコンポが閉じて,このイベントが発生する
//=============================================================================
procedure TForm1.OnplPopupListBox1CloseUp(Sender: TObject);
begin
  if FplPopupListBox1.ItemIndex >= 0 then begin
    Edit1.Text := FplPopupListBox1.Items[FplPopupListBox1.ItemIndex];
  end else begin
    // [ESC]で閉じるとここが実行される
    // Edit1.Text := '';
  end;
  Edit1.SetFocus;
  Edit1.SelStart := Length(Edit1.Text);
end;

end.




02_ポップアップ表示する TListBpx の TComboBox 的な使用


選択した項目の値を表示する TEdit と組み合わせて TComboBox のように使用する例です.
OnClick イベントを設定して使用します.矢印キーで TListBox の選択項目を変更すると,OnClick イベントが発生します.実際に項目をクリックすると TListBox は閉じますが,これは,TComboBox でも同じです.
TListBox では,項目の選択で OnClick イベントが発生します.


図7
選択中の項目の値が TEdit に表示されている
図8
矢印キーで選択項目を変更すると,その項目の値が TEdit に表示される

リスト2
ポップアップ表示する TListBpx の TComboBox のように使用する
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ExtCtrls, plPopupListBox, Buttons;

type
  TForm1 = class(TForm)
    Edit1: TEdit;
    SpeedButton1: TSpeedButton;
    procedure SpeedButton1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    FplPopupListBox1 : TplPopupListBox;
    procedure OnplPopupListBox1Click(Sender: TObject);
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

//=============================================================================
//  フォーム生成時の処理
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
  SpeedButton1.Caption := '▼';
end;

//=============================================================================
//  TplPopupListBox コンポをポップアップ表示
//  コンポーネントは実行時生成
//=============================================================================
procedure TForm1.SpeedButton1Click(Sender: TObject);
var
  LPos   : TPoint;
  LIndex : Integer;
begin
  if not Assigned(FplPopupListBox1) then begin
    FplPopupListBox1 := TplPopupListBox.Create(Self);
    FplPopupListBox1.Parent := self;

    // イベント用のメソッドを設定
    FplPopupListBox1.OnClick   := OnplPopupListBox1Click;

    FplPopupListBox1.Width := Edit1.Width + 25;
    FplPopupListBox1.IntegralHeight := True;

    // Style プロパティの値を lbOwnerDrawFixed にして ItemHeight の値を変更
    FplPopupListBox1.Style := lbOwnerDrawFixed;
    FplPopupListBox1.ItemHeight := 19;

    // デフォルト描画とフォーカスの枠線描画の設定
    FplPopupListBox1.DefaultDraw := False;
    FplPopupListBox1.FocusRect := False;

    //テスト用の表示項目を作成
    with FplPopupListBox1.Items do begin
      BeginUpdate;
      Clear;
      Add('東京都');
      Add('埼玉県');
      Add('神奈川県');
      Add('群馬県');
      Add('栃木県');
      Add('茨城県');
      Add('千葉県');
      Add('長野県');
      Add('山梨県');
      Add('岐阜県');
      Add('新潟県');
      Add('福島県');
      Add('山形県');
      Add('岩手県');
      Add('青森県');
      Add('沖縄県');
      Add('京都府');
      Add('大阪府');
      EndUpdate;
    end;
  end;

  // ShowBeneathメソッドで表示する
  // コントロールの左下の座標位置から表示する
  LPos := Self.ClientToScreen(Point(Edit1.Left, Edit1.Top));
  FplPopupListBox1.ShowBeneath(LPos.X, LPos.Y + Edit1.Height, Edit1.Height);

  // Edit1.Text と同じ文字列の項目を選択状態にする
  LIndex := FplPopupListBox1.Items.IndexOf(Edit1.Text);
  FplPopupListBox1.ItemIndex := LIndex;
end;

//=============================================================================
//  TplPopupListBoxコンポのOnClickイベント処理
//  TComboBoxのような使い方
//  TListBoxは,TComboBoxとは違い,元の値を表示したまま選択作業ができる
//  このような使い方は変則であるが,テストなので
//
//  選択項目を矢印キーで変更するとこのイベントが発生する
//  実際には OnSelectItem のような動作
// [ESC] で閉じても現在の値が TEdit にセットされる
//=============================================================================
procedure TForm1.OnplPopupListBox1Click(Sender: TObject);
var
  LIndex : Integer;
begin
  LIndex := TplPopupListBox(Sender).ItemIndex;
  Edit1.Text := TplPopupListBox(Sender).Items[LIndex];
end;

end.




03_TListBox をポップアップ表示するコンポのソースコード


コンポーネントのソースコードです.
このコンポーネントは,設計時にフォームに配置できますが,実行時には非表示になります.プロパティやイベントの全てが有効に機能するとは限りません.各自で確認してください.
このポップアップ表示する TListBox は,項目をクリックすすると TComboBox のように閉じます.通常の TListBox では,クリックは項目の選択だけです.
通称の TListBox のように,クリックを項目の選択だけにする場合は, [Enter] キー,あるいはダブルクリックで閉じる処理にすることになるでしょう.



リスト3
TListBox をポップアップ表示するコンポーネントのソースコード
unit plPopupListBox;

interface

uses
  Windows, Messages, SysUtils, Classes, Controls, StdCtrls, Graphics, Dialogs;

type
  TplPopupListBox = class(TCustomListBox)
  private
    FCloseupEvent : TNotifyEvent;
    FDefaultDraw  : Boolean;
    FFocusRect    : Boolean;
    FSelBackColor : TColor;
    FSelFontColor : TColor;
    FDropDownRows: Integer;
    procedure SetDefaultDraw(const Value: Boolean);
    procedure SetFocusRect(const Value: Boolean);
    procedure SetSelBackColor(const Value: TColor);
    procedure SetSelFontColor(const Value: TColor);
    procedure SetDropDownRows(const Value: Integer);
  protected
    procedure CloseUp;
    procedure CreateParams(var Params: TCreateParams); override;
    procedure DoEnter; override;
    procedure KeyDown(var Key: Word; Shift: TShiftState); override;
    procedure KeyPress(var Key: Char); override;
    function MouseIsOnScrollbar( const Pos: TPoint; var HittestCode:
    Integer): Boolean;
    function PerformNCMouseAction( Msg, HittestCode: Integer; const Pos:
    TPoint): Integer;
    procedure WMLButtonUp(var Message: TWMLButtonUp); message WM_LBUTTONUP;
    procedure WMLButtonDown(var Message: TWMLButtonDown);
    message WM_LBUTTONDOWN;
    procedure WMMouseMove(var Message: TWMMouseMove); message WM_MOUSEMOVE;
    procedure WMActivate(var Message: TWMActivate); message WM_ACTIVATE;
    procedure DrawItem(Index: Integer; Rect: TRect; State: TOwnerDrawState);
      override;
    function DoMouseWheel(Shift: TShiftState; WheelDelta: Integer;
      MousePos: TPoint): Boolean; override;
  public
    procedure ShowBeneath(ALeft, ATop: Integer; AHeight: Integer=0);
    constructor Create(AOwner: TComponent); override;
    property DefaultDraw  : Boolean read FDefaultDraw write SetDefaultDraw;
    property FocusRect    : Boolean read FFocusRect write SetFocusRect;
    property SelBackColor : TColor read FSelBackColor write SetSelBackColor;
    property SelFontColor : TColor read FSelFontColor write SetSelFontColor;
    property DropDownRows : Integer read FDropDownRows write SetDropDownRows;
  published
    property WheelAccumulator;
    property OnCloseUp: TNotifyEvent read FCloseupEvent write FCloseupEvent;
    property Style;
    property AutoComplete;
    property AutoCompleteDelay;
    property Color;
    property Columns;
    property DoubleBuffered;
    property Enabled;
    property ExtendedSelect;
    property Font;
    property IntegralHeight;
    property ItemHeight;
    property Items;
    property MultiSelect;
    property ParentBiDiMode;
    property ParentColor;
    property ParentDoubleBuffered;
    property ParentFont;
    property ParentShowHint;
    property PopupMenu;
    property ScrollWidth;
    property ShowHint;
    property Sorted;
    property TabWidth;
    property Touch;
    property OnClick;
    property OnContextPopup;
    property OnData;
    property OnDataFind;
    property OnDataObject;
    property OnDblClick;
    property OnDrawItem;
    property OnEnter;
    property OnExit;
    property OnGesture;
    property OnKeyDown;
    property OnKeyPress;
    property OnKeyUp;
    property OnMeasureItem;
    property OnMouseActivate;
    property OnMouseDown;
    property OnMouseEnter;
    property OnMouseLeave;
    property OnMouseMove;
    property OnMouseUp;
  end;

procedure Register;

implementation

uses
  Forms,
  Types;

{ TplPopupListBox }

//-----------------------------------------------------------------------------
//  Create処理
//-----------------------------------------------------------------------------
constructor TplPopupListBox.Create(AOwner: TComponent);
begin
  inherited;
  if (csDesigning in ComponentState ) then begin
    Width  := 28;
    Height := 28;
  end else begin
    ImeMode := imDisable;
    BorderStyle := bsNone;
    Width  := 120;
    Height := 220;
    Hide;
  end;

  FDefaultDraw   :=  True;
  FFocusRect     := True;
  FSelBackColor  := $00FFD2A6;
  FSelFontColor  := clBlack;
  FDropDownRows := 8;
end;

//-----------------------------------------------------------------------------
//  CreateParamsメソッド処理
//  コントロールのスタイル等を設定
//-----------------------------------------------------------------------------
procedure TplPopupListBox.CreateParams(var Params: TCreateParams);
var
  LParentForm : TCustomForm;
begin
  inherited;

  if not (csDesigning in ComponentState ) then begin
    Params.Style := Params.Style and not WS_CHILD or WS_POPUP or WS_BORDER;

    Params.WindowClass.Style := CS_SAVEBITS or CS_DROPSHADOW;
    LParentForm := GetParentForm(Self);
    if LParentForm.Handle <> 0 then Params.WndParent := LParentForm.Handle;
  end;
end;

//-----------------------------------------------------------------------------
//  DefaultDraw プロパティの設定用メソッド
//-----------------------------------------------------------------------------
procedure TplPopupListBox.SetDefaultDraw(const Value: Boolean);
begin
  if FDefaultDraw <> Value then begin
    FDefaultDraw := Value;
    Invalidate;
  end;
end;

//-----------------------------------------------------------------------------
//  FocusRect プロパティの設定用メソッド
//  フォーカスの枠線を描画するかどうかのフラグ
//-----------------------------------------------------------------------------
procedure TplPopupListBox.SetFocusRect(const Value: Boolean);
begin
  if FFocusRect <> Value then begin
    FFocusRect := Value;
    Invalidate;
  end;
end;

//-----------------------------------------------------------------------------
//  SelBackColor プロパティの設定用メソッド
//  選択行の背景色のプロパティの設定
//  DefaultDraw プロパティの値が False の時に有効となる
//-----------------------------------------------------------------------------
procedure TplPopupListBox.SetSelBackColor(const Value: TColor);
begin
  if FSelBackColor <> Value then begin
    FSelBackColor := Value;
    Invalidate;
  end;
end;

//-----------------------------------------------------------------------------
//  SelFontColor プロパティの設定用メソッド
//  選択行の文字の色のプロパティの設定
//  DefaultDraw プロパティの値が False の時に有効となる
//-----------------------------------------------------------------------------
procedure TplPopupListBox.SetSelFontColor(const Value: TColor);
begin
  if FSelFontColor <> Value then begin
    FSelFontColor := Value;
    Invalidate;
  end;
end;
//-----------------------------------------------------------------------------
//  DropDownCount プロパティの設定用メソッド
//  ドロップダウンのリストの項目数
//-----------------------------------------------------------------------------
procedure TplPopupListBox.SetDropDownRows(const Value: Integer);
begin
  if FDropDownRows <> Value then begin
    FDropDownRows := Value;
  end;
end;

//-----------------------------------------------------------------------------
//  本コンポ表示用のメソッド
//  X,Y(スクリーン座標)の位置から表示する
//  AHeightを指定すると,画面下端で表示する際に,その分だけ上から表示する
//-----------------------------------------------------------------------------
procedure TplPopupListBox.ShowBeneath(ALeft, ATop: Integer; AHeight: Integer=0);
var
  LBottom  : Integer;
  LRight   : Integer;
  LCtrlPos : TPoint;
  LMonitor : TMonitor;
begin
  if FDropDownRows >= Items.Count then FDropDownRows := Items.Count;
  if FDropDownRows <= 1 then FDropDownRows := 1;
  Height := FDropDownRows * ItemHeight + 4;

  LCtrlPos := Point(ALeft, ATop);
  // 表示位置の調整
  LRight  := LCtrlPos.X + Width;
  LBottom := LCtrlPos.Y + Height;

  LMonitor := Screen.MonitorFromPoint(LCtrlPos);
  if LMonitor.WorkareaRect.Bottom < LBottom then begin
    LCtrlPos.Y := LCtrlPos.Y - AHeight - Height;
  end;
  if LMonitor.WorkareaRect.Left > LCtrlPos.X then begin
    LCtrlPos.X := LMonitor.WorkareaRect.Left;
  end else
  if LMonitor.WorkareaRect.Right < LRight then begin
    LCtrlPos.X := LMonitor.WorkareaRect.Right - Width;
  end;

  SetBounds(LCtrlPos.x, LCtrlPos.y, Width, Height);
  ItemIndex := -1;
  if Items.Count >= 1 then ItemIndex := 0;

  // TListBoxのShowメソッドを実行
  Show;

  if not Focused and CanFocus then SetFocus;
end;

//-----------------------------------------------------------------------------
//  DoEnterメソッド処理
//  TWinControlがフォーカスを受取ると自動的に実行される
//  Showメソッドで実行するよりもチラつきが少ない
//-----------------------------------------------------------------------------
procedure TplPopupListBox.DoEnter;
var
  LParentForm : TCustomForm;
begin
  inherited;

  // このコンポを表示するフォームを疑似的にアクティブ状態にする
  LParentForm := GetParentForm(Self);
  if LParentForm.Handle <> 0 then begin
    SendMessage(LParentForm.Handle, WM_NCACTIVATE, 1, 0);
  end;
end;

//-----------------------------------------------------------------------------
//  KeyDownメソッド処理
//-----------------------------------------------------------------------------
procedure TplPopupListBox.KeyDown(var Key: Word; Shift: TShiftState);
begin
  if (Key = VK_ESCAPE) or (Key = VK_RETURN) then begin
    if Key = VK_ESCAPE then begin
      ItemIndex := -1;
    end;
    Key := 0;
  end else
  if (Key = VK_DOWN) and (ssAlt in Shift) then begin
    Key := 0;
    CloseUp;
  end else begin
    inherited;
  end;
end;

//-----------------------------------------------------------------------------
//  KeyPressメソッド処理
//-----------------------------------------------------------------------------
procedure TplPopupListBox.KeyPress(var Key: Char);
begin
  if Key = Char(VK_RETURN) then begin
    Closeup;
  end else
  if Key = Char(VK_ESCAPE) then begin
    ItemIndex := -1;
    Closeup;
  end else begin
    inherited;
  end;
end;

//-----------------------------------------------------------------------------
//  WM_LBUTTONDOWNメッセージ処理
//-----------------------------------------------------------------------------
procedure TplPopupListBox.WMLButtonDown(var Message: TWMLButtonDown);
var
  LWhere : Integer;
  LPos   : TPoint;
begin
  LPos := Point(Message.Pos.x, Message.Pos.y);

  if MouseIsOnScrollbar(LPos, LWhere) then begin
    PerformNCMouseAction(WM_NCLBUTTONDOWN, LWhere, LPos);
  end else begin
    inherited;
  end;
end;

//-----------------------------------------------------------------------------
//  WM_LBUTTONUPメッセージ処理
//-----------------------------------------------------------------------------
procedure TplPopupListBox.WMLButtonUp(var Message: TWMLButtonUp);
var
  LWhere : Integer;
  LPos   : TPoint;
begin
  LPos := Point(Message.Pos.x, Message.Pos.y);

  // スクロールバー上にある場合
  if MouseIsOnScrollbar(LPos, LWhere) then begin
    PerformNCMouseAction(WM_NCLBUTTONUP, LWhere, LPos);
  end else begin
    inherited;
    if not MultiSelect then begin
      Closeup;
    end;
  end;
end;

//-----------------------------------------------------------------------------
//  WM_MOUSEMOVEメッセージ処理
//-----------------------------------------------------------------------------
procedure TplPopupListBox.WMMouseMove(var Message: TWMMouseMove);
var
  LWhere : Integer;
  LPos   : TPoint;
begin
  LPos := Point(Message.Pos.x, Message.Pos.y);

  // スクロールバー上にある場合
  if MouseIsOnScrollbar(LPos, LWhere) then begin
    PerformNCMouseAction(WM_NCMOUSEMOVE, LWhere, LPos);
  end else begin
    inherited;
  end;
end;

//-----------------------------------------------------------------------------
//  引数のPosがマウスがスクロールバーの上にあるかを調査する関数
//-----------------------------------------------------------------------------
function TplPopupListBox.MouseIsOnScrollbar(const Pos: TPoint; var
  HittestCode: Integer): Boolean;
var
  LPos : TPoint;
begin
  if csDesigning In ComponentState then begin
    Result := False;
  end else begin
    LPos := Pos;
    Windows.ClientToScreen(Handle, LPos);
    Hittestcode := Perform(WM_NCHITTEST, 0, MakeLParam(LPos.X, LPos.Y));
    Result      := (Hittestcode = HTVSCROLL) or (Hittestcode = HTHSCROLL);
  end;
end;

//-----------------------------------------------------------------------------
//  各マウスイベント(メッセージ)処理から呼出して実行する関数
//  マウスがスクロールバー上にある場合は,非クライアント領域として操作
//-----------------------------------------------------------------------------
function TplPopupListBox.PerformNCMouseAction(Msg, HittestCode: Integer;
  const Pos: TPoint): Integer;
var
  LPos : TPoint;
begin
  LPos := Pos;
  Windows.ClientToScreen(Handle, LPos);
  Result := Perform(Msg, HittestCode, MakeLParam(LPos.X, LPos.Y));
end;

//-----------------------------------------------------------------------------
//  マウスホイール操作の処理
//  この処理がないと親コントロールがスクロールメッセージを受信してしまう
//-----------------------------------------------------------------------------
function TplPopupListBox.DoMouseWheel(Shift: TShiftState; WheelDelta: Integer;
  MousePos: TPoint): Boolean;
begin
  Result := inherited;

  if Visible and MouseInClient then begin
    if WheelDelta > 0 then begin
      Perform(WM_VSCROLL, MakeWParam(SB_LINEUP, 0), 0);
    end else begin
      Perform(WM_VSCROLL, MakeWParam(SB_LINEDOWN, 0), 0);
    end;
    // 他のウィンドウにはスクロールメッセージを送信しない
    Result := True;
  end;
end;

//-----------------------------------------------------------------------------
//  WM_ACTIVATEメッセージ処理
//  本コンポが非アクティブになったら閉じる
//
//  MultiSelect=Trueの場合の対策
//  MultiSelect=Falseの場合はクリックで閉じるので必要ない
//-----------------------------------------------------------------------------
procedure TplPopupListBox.WMActivate(var Message: TWMActivate);
begin
  inherited;
  if Visible then begin
    if (Message.ActiveWindow <> Handle) and (Message.Active = 0) then begin
      ItemIndex := -1;
      CloseUp;
    end;
  end;
end;

//-----------------------------------------------------------------------------
//  閉じる時の処理用関数
//-----------------------------------------------------------------------------
procedure TplPopupListBox.CloseUp;
begin
  Hide;

  // OnCloseUpイベントが設定してあれば実行
  if Assigned(FCloseupEvent) then begin
    FCloseupEvent(Self);
  end;

  // 親コントロールにフォーカス移動
  // 親以外のコントロールにフォーカスしたい場合は利用側のコードで対応する
  TWinControl(Parent).SetFocus;
  SetActiveWindow(TWinControl(Parent).Handle);
end;

//-----------------------------------------------------------------------------
//  DrawItem メソッドの処理
//  イベントでは全て自前の処理となるが,継承クラスのメソッドでは使い分けが可能
//-----------------------------------------------------------------------------
procedure TplPopupListBox.DrawItem(Index: Integer; Rect: TRect;
  State: TOwnerDrawState);
var
  LRect      : TRect;
  LText      : string;
  LFormat    : TTextFormat;
  LTempColor : TColor;
begin
  LTempColor := Canvas.Font.Color;

  if FDefaultDraw then begin
    inherited DrawItem(Index, Rect, State);
  end else begin
    if odSelected in State then begin
      Canvas.Brush.Color := FSelBackColor;
      Canvas.Font.Color  := FSelFontColor;
    end else begin
      Canvas.Brush.Color := Color;
      Canvas.Font.Color  := clBlack;
    end;
    Canvas.FillRect(Rect);

    LRect := Rect;
    InflateRect(LRect , -3, 0);
    LText := Items[Index];
    LFormat := [tfLeft, tfVerticalCenter, tfSingleLine];
    Canvas.TextRect(LRect, LText, LFormat);
  end;
  Canvas.Font.Color := LTempColor;

  if (odFocused in State) and not FFocusRect then begin
    Canvas.DrawFocusRect(Rect);
  end;
end;

//-----------------------------------------------------------------------------
//  コンポーネント登録情報
//-----------------------------------------------------------------------------
procedure Register;
begin
  RegisterComponents('plXRAY', [TplPopupListBox]);
end;

end.