Delphi Programming / Object Pascal
サイトのトップページへ リンクのページへ ロゴマーク
[掲載 2014年11月23日] [更新 2020年03月11日] Delphi サンプルプログラム集
   
056_TValueListEditor のインプレイスボタンとその機能
動作確認等 Windows 7 U64(SP1) + Delphi XE(UP1) Pro
056_TValueListEditor_Button.zip [2,434 KB] 2020年03月11日版 (EXE 同梱)



  • 2014年12月09日
  • 必要なファイルをプロジェクトに追加するのはトラブル発生の要因のようなので,ライブラリパスに置いて使用する方式に変更




TValueListEditor コンポーネント


TValueListEditor コンポーネントは,2 列だけのセルを持った TStringGrid のようなコンポーネントです. TStringGrid や TDBGrid 同様,セルが編集状態となるとインプレイスエディタが起動して,ドロップダウン表示用のボタンと,省略記号の参照ボタンを表示することができます.
本ページのサンプルは,そのテストプログラムです.



TValueListEditor の左側の列は,デフォルトでは編集できません.右側のセルに値を入力する形式となっています.最初の 1 行目は,タイトル行で,これもデフォルトでは編集できません.
タイトル行のテキストと,各セルに表示する値は,下図のように,設計時に編集できます.
タイトル行は,DisplayOptions プロパティの doColumnTitles メンバの値を False にすると非表示となります.

本ページのサンプルでは,これらの設定は行っていません.設計時に TValueListEditor コンポーネントを配置し,全て実行時に設定しています.
このコンポーネントは,TStringGrid と同じように,セルにチェックボックスやボタンを表示する等のカスタム描画も可能です



図1
列のタイトル名の設定ダイアログ
  • 設計時に設定する場合
  • プロパティの [TitleCaptions] から起動するダイアログ
  • コンテキストメニューで,設定値の読み込みや保存も可能
  • DisplayOptions の doColumnTitles の値を False にすると,タイトル行を表示しない
図2
値リストの編集ダイアログ
  • 設計時にキーや値を設定する場合
  • プロパティの [Strings] から起動するダイアログ
  • コンテキストメニューで,設定値の読み込みや保存も可能

タイトル行のテキストは,次のようなメソッドでファイルからの読み書きができます.



  TValueListEditor1.TitleCaptions.SaveToFile
  TValueListEditor1.TitleCaptions.LoadFromFile


ドロップダウンで表示するリストも次のようにファイルから読書きできます.
0 は,タイトル行を除いた最初の行を意味します.したがって,最初の入力用セルにドロップダウンで表示するリストを設定しています.この設定をすると,0 行目のセルは,編集状態になると,自動的に下向きの矢印のボタンが表示されます.GetPickList イベントの実装は必要ありません.



  ValueListEditor1.ItemProps[0].PickList.LoadFromFile('PickList.txt');
  ValueListEditor1.ItemProps[0].PickList.SaveToFile('PickList.txt');

  //KeyNameを使用する場合
  ValueListEditor1.ItemProps['都道府県'].PickList.LoadFromFile('PickList.txt');
  ValueListEditor1.ItemProps['都道府県'].PickList.SaveToFile('PickList.txt');


GetPickList イベント内でドロップダウンのリストを設定することもできます.
下のコードは,KeyName が [都道府県] の時は,リストをファイルから読込んで表示します.



  //KeyNameが[都道府県]の編集時はドロップダウンのボタンを表示する
  ValueListEditor1.ItemProps['都道府県'].EditStyle := esPickList;


//OnGetPickListイベントのコード例
procedure TForm1.ValueListEditor1GetPickList(Sender: TObject;
  const KeyName: string; Values: TStrings);
begin
  if KeyName = '都道府県' then begin
    Values.LoadFromFile('PickList.txt');
  end;
end;


セルのデータの Strings プロパティは,TStrings 型となっています.次のように,ファイルから読み出したり,入力した値をファイルに保存することができます.
読込むデータは,「都道府県=東京都」 のように,キー名と値のペアで記述しておきます.



  ValueListEditor1.Strings.LoadFromFile('CellData.txt');
  ValueListEditor1.Strings.SaveToFile('CellData.txt');




01_動作確認用のテストプログラム


セルが編集状態になった時に,どのボタンを表示するかは,TItemProp の EditStyle プロパティの値で設定します.TEditStyle 型の 3 つの内,いずれかを指定します.

EditStyle に esPickList を指定した場合,ドロップダウン表示用のボタンが表示されます.ボタンをクリックするか,[Alt] + [↓] でドロップダウンのリストを表示します.
ただし,ドロップダウンで表示するリストが作成されていないと,何も表示しません.

EditStyle に esEllipsis を指定した場合,省略記号 [...] の参照用ボタンが表示されますが,実際には OnEditButtonClick イベントを実装しないと何も起こりません.ボタンクリックか,セルにフォーカスがある時に [Ctrl] + [Enter] で,そのイベントが発生します.

下のサンプルでは,最初の入力セルにドロップダウン用のボタンを表示し,その次のセルは,参照ボタンを表示するようにしています.最後のセルは何も設定してません.普通のセルです.



図3
ドロップダウンのリストを表示
  • [Alt] + [↓] でも表示する
図4
参照ボタンクリックでダイアログを表示
  • [Ctrl] + [Enter] でも表示する
  • 表示位置の設定はしていない

リスト1
セルの編集開始でボタンを表示して,ドロップダウンのリストを表示したりダイアログを表示
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ComCtrls, Grids, ValEdit, ExtCtrls;

type
  TForm1 = class(TForm)
    ValueListEditor1: TValueListEditor;
    Panel1: TPanel;
    procedure FormCreate(Sender: TObject);
    procedure ValueListEditor1EditButtonClick(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

//=============================================================================
//  フォーム生成時の処理
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
var
  LItemList : TItemProp;
  LIndex    : Integer;
begin
  // 値を全てクリア
  ValueListEditor1.Strings.Clear;

  // キー例の幅
  ValueListEditor1.ColWidths[0] := 90;

  // 各列のタイトル文字を設定
  ValueListEditor1.TitleCaptions.Text := '';
  ValueListEditor1.TitleCaptions.Add('入力項目');
  ValueListEditor1.TitleCaptions.Add('値');

  // 行を追加してキー項目を設定
  ValueListEditor1.InsertRow('都道府県', '', True);
  ValueListEditor1.InsertRow('調査番号', '', True);
  ValueListEditor1.InsertRow('調査結果', '', True);


  // ドロップダウンのメニューを作成
  LItemList := TItemProp.Create(ValueListEditor1);
  try
    with LItemList.PickList do begin
      Add('東京都');
      Add('埼玉県');
      Add('神奈川県');
      Add('群馬県');
      Add('栃木県');
      Add('茨城県');
      Add('千葉県');
      Add('長野県');
      Add('山梨県');
      Add('岐阜県');
      Add('新潟県');
      Add('福島県');
      Add('山形県');
    end;
    // 最初の行の入力はドロップダウン用のボタンを表示する
    // そのリストに,上で作成したリストを使用する
    ValueListEditor1.ItemProps[0].Assign(LItemList);
  finally
    FreeAndNil(LItemList);
  end;

  // 2 番名の行の編集時は参照ボタンを表示する
  // ボタンをクリックした時の動作は OnEditButtonClick イベントで設定する
  ValueListEditor1.ItemProps[1].EditStyle := esEllipsis;
  //セルに値をセット
  ValueListEditor1.Cells[1, 2] := 'A-100';

  // 行の高さは全て同じにする(0はタイトル行)
  // DefaultRowHeight プロパティでも設定できる
  for LIndex := 0 to ValueListEditor1.RowCount - 1 do begin
    ValueListEditor1.RowHeights[LIndex] := 20;
  end;

  //最初は2行目にフォーカスを移動
  ValueListEditor1.Row := 2;
end;

//=============================================================================
//  TValueListEditor の OnEditButtonClick イベント処理
//  省略記号 [...] ボタンをクリックすると発生する
//=============================================================================
procedure TForm1.ValueListEditor1EditButtonClick(Sender: TObject);
var
  LCol  : Integer;
  LRow  : Integer;
  LText : string;
begin
  LCol  := TValueListEditor(Sender).Col;
  LRow  := TValueListEditor(Sender).Row;

  if LRow = 2 then begin
    LText := IntToStr(LRow) + ' 行目 : '
             + TValueListEditor(Sender).Cells[LCol, LRow] + sLineBreak
             + '参照ボタンクリック';
    MessageBox(Handle, PChar(LText), '情報', MB_ICONINFORMATION);
  end;
end;

end.


ドロップダウンのリストは,TStringList を使用して,次のようにも設定できます.


procedure TForm1.FormCreate(Sender: TObject);
var
  SL : TStringList;
begin
  ...
  ...

  SL := TStringList.Create;
  try
    with SL do begin
      Add('東京都');
      Add('埼玉県');
      Add('神奈川県');
      Add('群馬県');
      Add('栃木県');
      Add('茨城県');
      Add('千葉県');
      Add('長野県');
      Add('山梨県');
      Add('岐阜県');
      Add('新潟県');
      Add('福島県');
      Add('山形県');
    end;
    plValueListEditor1.ItemProps[0].PickList.Assign(SL);
  finally
    FreeAndNil(SL);
  end;

  ...
  ...
end;  


上のコードでは,ドロップダウンで表示するリストをあらかじめ設定しています.このリストは,次のように,TValueListEditor の OnGetPickList イベント内で設定することもできます.
イベントを作成しなければ機能しませんから,どちらかと言えば,こちらの方が直観的です.


リスト2
TValueListEditor の OnGetPickList イベント内でリストを設定する
引数の KeyName で処理の分岐が可能
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ComCtrls, Grids, ValEdit, ExtCtrls;

type
  TForm1 = class(TForm)
    ValueListEditor1: TValueListEditor;
    Panel1: TPanel;
    procedure FormCreate(Sender: TObject);
    procedure ValueListEditor1EditButtonClick(Sender: TObject);
    procedure ValueListEditor1GetPickList(Sender: TObject;
      const KeyName: string; Values: TStrings);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

//=============================================================================
//  フォーム生成時の処理
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
var
  LIndex : Integer;
begin
  // 値を全てクリア
  ValueListEditor1.Strings.Clear;

  // キー例の幅
  ValueListEditor1.ColWidths[0] := 90;

  // 各列のタイトル文字を設定
  ValueListEditor1.TitleCaptions.Text := '';
  ValueListEditor1.TitleCaptions.Add('入力項目');
  ValueListEditor1.TitleCaptions.Add('値');

  // 行を追加してキー項目を設定
  ValueListEditor1.InsertRow('都道府県', '', True);
  ValueListEditor1.InsertRow('調査番号', '', True);
  ValueListEditor1.InsertRow('調査結果', '', True);


  // KeyName が[都道府県] の編集時はドロップダウンのボタンを表示する
  // ボタンをクリックした時の動作は OnEGetPickList イベントで設定する
  ValueListEditor1.ItemProps['都道府県'].EditStyle := esPickList;

  // KeyNameが [調査番号] の編集時は参照ボタンを表示する
  // ボタンをクリックした時の動作は OnEditButtonClick イベントで設定する
  ValueListEditor1.ItemProps['調査番号'].EditStyle := esEllipsis;
  //セルに値をセット
  ValueListEditor1.Cells[1, 2] := 'A-100';

  // 行の高さは全て同じにする (0はタイトル行)
  // DefaultRowHeight プロパティでも設定できる
  for LIndex := 0 to ValueListEditor1.RowCount - 1 do begin
    ValueListEditor1.RowHeights[LIndex] := 20;
  end;

  //最初は2行目にフォーカスを移動
  ValueListEditor1.Row := 2;
end;

//=============================================================================
//  TValueListEditorのOnEditButtonClick イベント処理
//  省略記号 [...] ボタンをクリックすると発生する
//=============================================================================
procedure TForm1.ValueListEditor1EditButtonClick(Sender: TObject);
var
  LCol  : Integer;
  LRow  : Integer;
  LText : string;
begin
  LCol  := TValueListEditor(Sender).Col;
  LRow  := TValueListEditor(Sender).Row;

  if LRow = 2 then begin
    LText := IntToStr(LRow) + ' 行目 : '
           + TValueListEditor(Sender).Cells[LCol, LRow] + sLineBreak
           + '参照ボタンクリック';
    MessageBox(Handle, PChar(LText), '情報', MB_ICONINFORMATION);
  end;
end;

//=============================================================================
//  TValueListEditor の OnGetPickList イベント処理
//  矢印のボタンをクリックすると発生する
//  イベント内でドロップダウンのリストを作成する例
//=============================================================================
procedure TForm1.ValueListEditor1GetPickList(Sender: TObject;
  const KeyName: string; Values: TStrings);
begin
  if KeyName = '都道府県' then begin
    Values.Clear;
    Values.Add('東京都');
    Values.Add('埼玉県');
    Values.Add('神奈川県');
    Values.Add('群馬県');
    Values.Add('栃木県');
    Values.Add('茨城県');
    Values.Add('千葉県');
    Values.Add('長野県');
    Values.Add('山梨県');
    Values.Add('岐阜県');
    Values.Add('新潟県');
    Values.Add('福島県');
    Values.Add('山形県');
  end;
end;

end.




02_ドロップダウンのウィンドウをカスタマイズ


上のサンプルの実行結果を見ると,ドロップダウンのウィンドウは,
  • システムの設定が,ウィンドウの下に影を表示しないになっていると,ドロップダウンのウィンドウの境界が明確でない
  • 日本語の場合,表示に使用するフォントによっては行間が狭くなる

という仕様になっています.そこで,少しは体裁を整えるために,このリストをカスタマイズしたサンプルです.TInplaceEditList の CreateWnd と DropDown メソッドをオーバライドして,そのメソッド内に設定コードを記述しています.そのためには,インプレイスエディタコントロールを生成する際に,TInplaceEditList を,カスタム操作用の継承クラスに置き換える必要があります.

TStringGrid の場合は,この実装は比較的簡単でした.下の最初のコードが TStringGrid の場合,その下が TValueListEditor の CreateEditor のコードです.
TValueListEditor の方は,FEditList という読み出し専用のプロパティに値をセットしています.このプロパティは,ValEdit.pas 内では一切使用されていません.値のセットだけです.外部から使用する際に使われるプロパティです.本サンプルでは,この部分を書き換えています.それがどのように影響するかは確認していません.



function TCustomGrid.CreateEditor: TInplaceEdit;
begin
  Result := TInplaceEdit.Create(Self);
end;

function TValueListEditor.CreateEditor: TInplaceEdit;
begin
  FEditList := TInplaceEditList.Create(Self);
  EditList.DropDownRows := FDropDownRows;
  EditList.OnEditButtonClick := FOnEditButtonClick;
  EditList.OnGetPickListitems := EditListGetItems;
  Result := FEditList;
end;


以下がカスタマイズしたコードと実行結果です.
TListBox の OnDrawItem イベント用のコードも実装していますが,今回のカスタマイズの目的とは直接関係はありません.必要であれば,このようにして使用できるという例です.

本サンプルでは,TValueListEditor の派生クラスを作成して,それを利用します.テストのためにコンポーネントをインストール (登録) するのは面倒です.そこで,[派生元のコンポーネントと交換する方法] の記事のユニットを使用して,実行時に,フォームに配置した TValueListEditor と入れ換えてテストしています.
このユニットは,サンプルプログラムのルートフォルダにあります.そのユニットを,ライブラリのパスが通ったフォルダに置いてテストしています.



図5
カスタマイズしたドロップダウンのウィンドウ
  • プロパティやメソッドとして実装していないが,
  • 行間の変更可能
  • 選択行の背景色の変更も可能
  • これは選択行のフォーカスの枠線描画なしの場合

このサンプルでは,ドロップダウンのリストの描画に PickListDrawItemEvent メソッドを使用しています.これは TListBox の OnDrawItem イベントに相当します.
このイベントは,TListBox の Style プロパティが lbOwnerDrawVariable か lbVirtualOwnerDraw の場合に発生するイベントです.このサンプルでは lbOwnerDrawVariable にしています.したがって,このイベント内に描画コードがないと,下図のように空のリストが表示されます.
イベント処理が必要なければ削除します.



図6
イベント内に描画コードがない場合

リスト3
ドロップダウンのリストをカスタマイズする例
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ComCtrls, Grids, ValEdit, ExtCtrls;

type
  //  TValueListEditor の継承クラス
  //  TInplaceEditList をカスタマイズするために使用する
  TplValueListEditor = class(TValueListEditor)
  private
  public
    function CreateEditor: TInplaceEdit; override;
  end;

  //  TInplaceEditList の継承クラス
  //  PickList(TListBox) をカスタマイズするために使用する
  TplInplaceEditList = class(TInplaceEditList)
  private
    procedure PickListDrawItemEvent(Control: TWinControl; Index: Integer;
      Rect: TRect; State: TOwnerDrawState);
    procedure PickListClickEvent(Sender: TObject);
  protected
    procedure CreateWnd; override;
    procedure DropDown; override;
    procedure WMMouseWheel(var Message: TWMMouseWheel); message WM_MOUSEWHEEL;
  end;


  TForm1 = class(TForm)
    Panel1: TPanel;
    ValueListEditor1: TValueListEditor;
    procedure FormCreate(Sender: TObject);
    procedure ValueListEditor1EditButtonClick(Sender: TObject);
  private
    { Private 宣言 }
    plValueListEditor1 : TplValueListEditor;
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

uses
  plChgCompo;

{$R *.dfm}

//=============================================================================
//  フォーム生成時の処理
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
var
  LItemList : TItemProp;
  LIndex    : Integer;
begin
  // フォーム作成時にコンポーネントの入れ替え
  plValueListEditor1 := TplValueListEditor(ChangeComponent(ValueListEditor1,
                                           TplValueListEditor));

  // 値を全てクリア
  plValueListEditor1.Strings.Clear;

  // キー例の幅
  plValueListEditor1.ColWidths[0] := 90;

  // 各列のタイトル文字を設定
  plValueListEditor1.TitleCaptions.Text := '';
  plValueListEditor1.TitleCaptions.Add('入力項目');
  plValueListEditor1.TitleCaptions.Add('値');

  //行を追加してキー名を設定
  plValueListEditor1.InsertRow('都道府県', '', True);
  plValueListEditor1.InsertRow('調査番号', '', True);
  plValueListEditor1.InsertRow('調査結果', '', True);


  //ドロップダウンのメニューを作成
  LItemList := TItemProp.Create(plValueListEditor1);
  try
    with LItemList.PickList do begin
      Add('東京都');
      Add('埼玉県');
      Add('神奈川県');
      Add('群馬県');
      Add('栃木県');
      Add('茨城県');
      Add('千葉県');
      Add('長野県');
      Add('山梨県');
      Add('岐阜県');
      Add('新潟県');
      Add('福島県');
      Add('山形県');
    end;
    // 最初の行の入力はドロップダウン用のボタンを表示する
    // そのリストに,上で作成したリストを使用する
    plValueListEditor1.ItemProps[0].Assign(LItemList);
  finally
    FreeAndNil(LItemList);
  end;

  // 2 番目の編集時は参照ボタンを表示する
  // ボタンをクリックした時の動作は OnEditButtonClick イベントで設定する
  ValueListEditor1.ItemProps[1].EditStyle := esEllipsis;
  // セルに値をセット
  ValueListEditor1.Cells[1, 2] := 'A-100';

  // 行の高さは全て同じにする(0はタイトル行)
  // DefaultRowHeight プロパティでも設定できる
  for LIndex := 0 to plValueListEditor1.RowCount - 1 do begin
    plValueListEditor1.RowHeights[LIndex] := 20;
  end;

  // 最初は2行目にフォーカスを移動
  plValueListEditor1.Row := 2;
end;

//=============================================================================
//  TValueListEditorのOnEditButtonClick イベント処理
//  省略記号 [...] ボタンをクリックすると発生する
//=============================================================================
procedure TForm1.ValueListEditor1EditButtonClick(Sender: TObject);
var
  LCol  : Integer;
  LRow  : Integer;
  LText : string;
begin
  LCol  := TValueListEditor(Sender).Col;
  LRow  := TValueListEditor(Sender).Row;

  if LRow = 2 then begin
    LText := IntToStr(LRow) + ' 行目 : '
           + TValueListEditor(Sender).Cells[LCol, LRow] + sLineBreak
           + '参照ボタンクリック';
    MessageBox(Handle, PChar(LText), '情報', MB_ICONINFORMATION);
  end;
end;

{ TplValueListEditor }

//=============================================================================
//  TValueListEditor を継承したクラスの CreateEditor メソッド
//  TInplaceEditList を継承クラスに設定
//  継承元のクラスでは,EditList(TInplaceEditList) を設定している
//  これが他の動作にどう影響するかは未調査
//  このプロパティは ValEdit.pas 内では使用していない
//  外部からこのプロパティを使用する際に問題となるかもしれない
//=============================================================================
function TplValueListEditor.CreateEditor: TInplaceEdit;
var
  LInplaceEditList : TplInplaceEditList;
begin
  inherited;

  LInplaceEditList := TplInplaceEditList.Create(Self);
  LInplaceEditList.OnEditButtonClick  := OnEditButtonClick;
  LInplaceEditList.OnGetPickListitems := EditListGetItems;

  Result := LInplaceEditList;
end;

{ TplInplaceEditList }

//=============================================================================
//  TInplaceEditList を継承したクラスの CreateWndメソッド
//  PickList(TListBox) のスタイルを設定
//
//  ItemHeight は行の高さ (ピクセル単位)
//=============================================================================
procedure TplInplaceEditList.CreateWnd;
begin
  inherited;

  // TPopupListboxの継承元はTCustomListBox
  with TListBox(PickList) do begin
    ItemHeight  := 20;
    BevelKind   := bkFlat;
    BevelWidth  := 1;
    BevelInner  := bvNone;
    BevelOuter  := bvRaised;
    Style       := lbOwnerDrawFixed;
    IntegralHeight := True;
    // 指定しない時はデフォルト  
    // DropDownRows   := 8;

    // カスタム描画用のイベントの定義.不要なら削除する
    OnDrawItem  := PickListDrawItemEvent;
    OnClick     := PickListClickEvent;
  end;
end;

//=============================================================================
//  TInplaceEditList を継承したクラスの DropDown メソッド
//  PickList(TListBox) の色と幅等を設定
//
//  インプレイスエディタのリストの表示位置のデフォルトはセルの左下
//  Left, Top プロパティ (スクリーン座標値) で変更可能
//=============================================================================
procedure TplInplaceEditList.DropDown;
var
  LGrid     : TValueListEditor;
  LCellRect : TRect;
begin
  inherited;

  LGrid := TValueListEditor(Grid);

  // 行と列番号からセルの矩形領域を取得
  LCellRect := LGrid.CellRect(LGrid.Col, LGrid.Row);

  // TListBox の背景色
  TListBox(PickList).Color := $00FEF8E7;

  // TListBox の表示幅
  TListBox(PickList).Width := LCellRect.Right - LCellRect.Left;

  // 表示開始時に選択状態にする項目番号 (0 ベース)
  TListBox(PickList).ItemIndex := 2;
end;

//-----------------------------------------------------------------------------
//  PickList(TListBox) の OnDrawItem イベント
//  カスタム描画が必要な場合に利用する
//  CreateWnd メソッドで定義しているので,不要ならば削除する
//

//  選択項目のテキスト描画の色 (TFont.Color) のデフォルトは clWhite
//-----------------------------------------------------------------------------
procedure TplInplaceEditList.PickListDrawItemEvent(Control: TWinControl;
  Index: Integer; Rect: TRect; State: TOwnerDrawState);
var
  LListBox   : TCustomListBox;
  LRect      : TRect;
  LTempColor : TColor;
begin
  LListBox := TCustomListBox(Control);

  LTempColor := LListBox.Canvas.Font.Color;

  // カスタム描画の場合,まず背景を塗り潰す
  if odSelected in State then begin
    // 選択行の背景色と文字の色
    LListBox.Canvas.Brush.Color := clHighlight;
    LListBox.Canvas.Font.Color  := clWhite;
  end;
  LRect := Rect;
  LListBox.Canvas.FillRect(LRect);

  LRect := Rect;
  InflateRect(LRect, -3, 0);
  DrawText(LListBox.Canvas.Handle,
           PChar(LListBox.Items[Index]),
           -1,
           LRect,
           DT_SINGLELINE or DT_LEFT or DT_VCENTER);

  LListBox.Canvas.Font.Color := LTempColor;

  //  フォーカスの枠線を描画しない場合
  // 現在の TBrush.Color で枠線を重ね描画
  if odFocused in State then begin
    LRect := Rect;
    DrawFocusRect(LListBox.Canvas.Handle, LRect);
  end;
end;

//-----------------------------------------------------------------------------
//  PickList(TListBox) の OnClick イベント
//  ドロップダウンのリストが閉じた時に発生する
//  [ESC]や他の場所をアクティブにして閉じた場合は発生しない
//-----------------------------------------------------------------------------
procedure TplInplaceEditList.PickListClickEvent(Sender: TObject);
begin
//
end;

//=============================================================================
//  マウスホイール操作の処理
//  この処理がないとマウスホイール操作でリストが閉じてしまう
//=============================================================================
procedure TplInplaceEditList.WMMouseWheel(var Message: TWMMouseWheel);
var
  LListBox : TListBox;
begin
  LListBox := TListBox(PickList);

  if LListBox.MouseInClient then begin
    if Message.WheelDelta > 0 then begin
      LListBox.Perform(WM_VSCROLL, MakeWParam(SB_LINEUP, 0), 0);
    end else begin
      LListBox.Perform(WM_VSCROLL, MakeWParam(SB_LINEDOWN, 0), 0);
    end;
  end else begin
    // TListBox 領域外でのマウスホイール操作で TListBox を閉じたい場合
    // 以下のコードを実行する
//    inherited;
  end;
end;

end.




03_ドロップダウン用のボタンでダイアログを表示


ボタンのスタイルを esPickList にすると,セルが編集状態になると,矢印ボタンが表示されます.
ドロップダウンで表示するリストを作成しないで OnGetPickListイベントを実装すれば,ボタンをクリックしても何も表示しません.このことを利用したのが下のコードです.位置を指定してダイアログを表示します.

下のコードでは単純なダイアログを表示するだけですが,項目の選択等のフォームをポップアップ表示すれば,本来のドロップダウンのような用途としても利用可能でしょう.



図7
ドロップダウンの代りにダイアログを表示
  • セルの左下から表示するようにしている
  • 少し位置がずれているが,原因については不明

リスト4
ドロップダウンのボタンでダイアログを表示するサンプル
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ComCtrls, Grids, ValEdit, ExtCtrls;

type
  TForm1 = class(TForm)
    ValueListEditor1: TValueListEditor;
    Panel1: TPanel;
    procedure FormCreate(Sender: TObject);
    procedure ValueListEditor1EditButtonClick(Sender: TObject);
    procedure ValueListEditor1GetPickList(Sender: TObject;
      const KeyName: string; Values: TStrings);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

//=============================================================================
//  フォーム生成時の処理
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
var
  LIndex : Integer;
begin
  // 値を全てクリア
  ValueListEditor1.Strings.Clear;

  // キー例の幅
  ValueListEditor1.ColWidths[0] := 90;

  // 各列のタイトル文字を設定
  ValueListEditor1.TitleCaptions.Text := '';
  ValueListEditor1.TitleCaptions.Add('入力項目');
  ValueListEditor1.TitleCaptions.Add('値');

  // 行を追加してキー項目を設定
  ValueListEditor1.InsertRow('都道府県', '', True);
  ValueListEditor1.InsertRow('調査番号', '', True);
  ValueListEditor1.InsertRow('調査結果', '', True);


  // 最初の行の入力はドロップダウン用のボタンを表示する
  ValueListEditor1.ItemProps[0].EditStyle := esPickList;

  // 2 番名の行の編集時は参照ボタンを表示する
  // ボタンをクリックした時の動作は OnEditButtonClick イベントで設定する
  ValueListEditor1.ItemProps[1].EditStyle := esEllipsis;
  //セルに値をセット
  ValueListEditor1.Cells[1, 2] := 'A-100';

  // 行の高さは全て同じにする (0 はタイトル行)
  // DefaultRowHeight プロパティでも設定できる
  for LIndex := 0 to ValueListEditor1.RowCount - 1 do begin
    ValueListEditor1.RowHeights[LIndex] := 20;
  end;

  // 最初は 2 行目にフォーカスを移動
  ValueListEditor1.Row := 2;
end;

//=============================================================================
//  TValueListEditor の OnEditButtonClick イベント処理
//  省略記号 [...] ボタンをクリックすると発生する
//=============================================================================
procedure TForm1.ValueListEditor1EditButtonClick(Sender: TObject);
var
  LCol  : Integer;
  LRow  : Integer;
  LText : string;
begin
  LCol  := TValueListEditor(Sender).Col;
  LRow  := TValueListEditor(Sender).Row;

  if LRow = 2 then begin
    LText := IntToStr(LRow) + ' 行目 : '
           + TValueListEditor(Sender).Cells[LCol, LRow] + sLineBreak
           + '参照ボタンクリック';
    MessageBox(Handle, PChar(LText), '情報', MB_ICONINFORMATION);
  end;
end;

//=============================================================================
//  TValueListEditor の OnGetPickList イベント処理
//  矢印のボタンをクリックすると発生する
//=============================================================================
procedure TForm1.ValueListEditor1GetPickList(Sender: TObject;
  const KeyName: string; Values: TStrings);
var
  LCol    : Integer;
  LRow    : Integer;
  LRect   : TRect;
  LPos    : TPoint;
  LDialog : TForm;
  LText   : string;
begin
  if KeyName = '都道府県' then begin
    LCol  := TValueListEditor(Sender).Col;
    LRow  := TValueListEditor(Sender).Row;

    LRect := TValueListEditor(Sender).CellRect(LCol, LRow);
    LPos  := TValueListEditor(Sender).ClientToScreen(Point(LRect.Left, LRect.Bottom));

    LText := '矢印が押された';
    LDialog := CreateMessageDialog(LText, mtInformation, [mbOK], mbOK);
    try
      LDialog.Left  := LPos.X;
      LDialog.Top   := LPos.Y;
      LDialog.Width := 180;
      // ダイアログを表示
      LDialog.ShowModal;
    finally
      FreeAndNil(LDialog);
    end;
  end;
end;

end.




04_キー項目の階層表示


コミュニティの場で,TValueListEditor コンポーネントを使用して,オブジェクトインスペクタのような表示ができないか,という質問を見かけました.
下図は,JEDI プロジェクトの TJvInspector を使用した例ですが,おそらくこのような表示のことではないかと思われます.

TJvInspector のソースコード見てみると,表示テキストは TValueListEditor と同じで,TStrings 型の変数に格納しています.その Objects プロパティに,表示・非表示,つまり,展開されているかどうかという情報や,自分が親であるか子であるかの情報を保持しているようです.
したがって,そのような実装をすれば可能なのではないかと思われます.


        
のような,展開中か,折りたたんでいるかの記号は,おそらくリソースから読み出して使用しているものと思われます.
TJvInspector コンポーネントは [Jv Runtime Design] タブにあります.


図8
TJvInspector コンポーネント
  • フォームに配置した Edit1 の published なプロパティを表示している
  • この表示でプロパティの設定も可能
  • 実行時の [オブジェクト・インスペクタ] 表示と言うことになる

リスト5
上の図を作成するために使用したコード
フォームに配置した Edit1 の published なプロパティを表示する
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, JvExControls, JvInspector, StdCtrls, JvComponentBase;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Edit1: TEdit;
    JvInspector1: TJvInspector;
    procedure Button1Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
begin
 JvInspector1.Clear;
 JvInspector1.AddComponent(Edit1);
end;

end.