Delphi Programming / Object Pascal
サイトのトップページへ リンクのページへ ロゴマーク
[掲載 2014年12月09日] [更新 2014年12月15日] Delphi サンプルプログラム集
   
055_ドロップダウンリストを実装した TStringGrid コンポーネント
動作確認等 Windows 7 U64(SP1) + Delphi XE(UP1) Pro
055_TplDropStringGrid.zip [4,884 KB] 2014年12月15日版 (EXE 同梱)







ドロップダウンリストを実装した TStringGrid コンポーネント


ドロップダウンリストの機能付きの TStringGrid コンポーネントです.
サンプルプログラム集の [054_TStringGrid のインプレイスボタンとその機能] でテストしたコードの他に,インプレイスエディタ関係のプロパティとイベント類だけを追加しています.

サードパーティ製の TStringGird には,TDBGrid のように列単位でプロパティの設定を行うカラムエディタを実装しているコンポーネントがあります.また,列,行,あるいはセル単位でプロパティを設定できるコンポーネントもあります.本ページのコンポーネントは,そのような機能は実装していません.イベント内で設定する方式です.

添付のプロジェクトを使用するには,プロジェクトを起動する前に,本コンポーネントをインストール (登録) しておく必要があります.



下図は,本ページのテストで使用したプロジェクトのフォームの設計時画面です.
コンポーネント TplDropStringGrid をフォームに配置しています.プロパティ等はデフォルトのままにして,実行時に設定しています.



図1
テストに使用したフォームの設計時画面
  • 上部の TPanel は,テスト用にポンタ類を配置するために配置している




01_基本的な使用方法とドロップダウンリスト関係のプロパティ


セルが編集状態になった時,つまりインプレイスエディタが起動した時に,セルの右側にボタンを表示します.下向き矢印のドロップダウンのボタンを表示するには,以下の 1. と 2. のコードを記述します.省略記号 [...] の参照ボタンを表示するには,1, と 3. のコードを記述します.
  • (1) OnGetEditStyle イベント内で,どのセルにどのボタンを表示するかを指定する
  • (2) OnGetPickListItems イベント内でドロップリストの項目リストを設定する
    ドロップダウンのボタンをクリックすると,このイベント内で設定したリストが表示される
  • (3) OnEditButtonClick イベント内に,参照ボタンをクリックした時の動作コードを書く
このインプレイスなボタンについては,以下の記事を参考にしてください.
本サンプルは,下の記事でテストした内容をコンポーネントにして動作確認したものです. TStringGrid でこのようなリストを表示して,項目を選択する機能を実装するには,TComboBox を直接利用する方法もあります.TComboBox のイベントやプロパティ等が利用できるため,ある意味便利と言えます.

図2
起動時の画面
図3
ドロップダウンリストを表示したところ
  • セルを編集状態にして,矢印ボタンをクリック
  • [Alt} + 矢印 [↓] でも表示できる
  • セルに値が入力されていると,その値に該当する項目が自動的に選択状態となる
図4
参照ボタンをクリック
  • ダイアログを表示

リスト1
基本的な使用方法とドロップダウンリスト関係のプロパティ
unit Unit1;

interface

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

type
  TForm1 = class(TForm)
    Panel1: TPanel;
    plDropStringGrid1: TplDropStringGrid;
    procedure FormCreate(Sender: TObject);
    procedure plDropStringGrid1GetEditStyle(TSender: TObject; ACol,
      ARow: Integer; var EditStyle: TEditStyle);
    procedure plDropStringGrid1EditButtonClick(Sender: TObject; ACol,
      ARow: Integer; ARect: TRect);
    procedure FormDestroy(Sender: TObject);
    procedure plDropStringGrid1GetPickListItems(ACol, ARow: Integer;
      Items: TStrings);
    procedure plDropStringGrid1DropDownList(Sender: TObject; ACol,
      ARow: Integer; ARect: TRect; var DefaultAction: Boolean);
  private
    { Private 宣言 }
    PickList : TStringList;
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

//=============================================================================
//  テスト用に列数と列幅等を設定し,テスト用のデータを各セルにセット
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
  plDropStringGrid1.DefaultDrawing := True;
  //編集可能にしないとインプレイスエディタは起動しない
  plDropStringGrid1.Options := plDropStringGrid1.Options + [goEditing];

  plDropStringGrid1.FixedCols       := 1;
  plDropStringGrid1.FixedRows       := 1;
  plDropStringGrid1.ColCount        := 3;
  plDropStringGrid1.RowCount        := 4;
  plDropStringGrid1.DefaultColWidth := 120;

  //左端の列幅設定
  plDropStringGrid1.ColWidths[0]  := 10;

  //テスト用の値をセット(固定行)
  plDropStringGrid1.Cells[1,0] := '都道府県';
  plDropStringGrid1.Cells[2,0] := '調査番号';

  //テスト用の値をセット
  plDropStringGrid1.Cells[2,1] := 'A-100';
  plDropStringGrid1.Cells[2,2] := 'A-200';
  plDropStringGrid1.Cells[2,3] := 'A-300';

  //ドロップ表示する時のリストを作成
  PickList := TStringList.Create;
  PickList.Clear;
  with PickList do begin
    Add('東京都');
    Add('埼玉県');
    Add('神奈川県');
    Add('群馬県');
    Add('栃木県');
    Add('茨城県');
    Add('千葉県');
    Add('長野県');
    Add('山梨県');
    Add('岐阜県');
    Add('新潟県');
    Add('福島県');
    Add('山形県');
  end;

  //各種プロパティの設定(TplDropStringGridで追加したもの)
  plDropStringGrid1.InplaceButtonWidth := 17;
  plDropStringGrid1.DropListRowCount   := 10;
  plDropStringGrid1.DropListItemHeight := 17;
  plDropStringGrid1.DropListWidth      := 150;
  plDropStringGrid1.DropListColor      := $00FEF8E7;;
end;

//=============================================================================
//  フォーム破棄時の処理
//=============================================================================
procedure TForm1.FormDestroy(Sender: TObject);
begin
  FreeAndNil(PickList);
end;

//=============================================================================
//  TplDropStringGridコンポのOnGetEditStyleイベント処理
//  セルの編集時に右端に表示するボタンの種類を設定
//=============================================================================
procedure TForm1.plDropStringGrid1GetEditStyle(TSender: TObject; ACol,
  ARow: Integer; var EditStyle: TEditStyle);
begin
  if ACol = 1 then EditStyle := esPickList;
  if ACol = 2 then EditStyle := esEllipsis;
end;

//=============================================================================
//  TplDropStringGridコンポのOnGetPickListItemsイベント処理
//  ドロップダウンで表示するリストの内容を設定
//=============================================================================
procedure TForm1.plDropStringGrid1GetPickListItems(ACol, ARow: Integer;
  Items: TStrings);
begin
  if ACol = 1 then Items.Assign(PickList);
end;

//=============================================================================
//  TplDropStringGridコンポのOnEditButtonClickイベント処理
//  参照ボタン(省略記号ボタン)をクリックした時の実行内容
//=============================================================================
procedure TForm1.plDropStringGrid1EditButtonClick(Sender: TObject; ACol,
  ARow: Integer; ARect: TRect);
var
  TheText : String;
begin
  if ACol = 2 then begin
    TheText := '参照ボタンクリック';
    MessageBox(Handle, PChar(TheText), '情報', MB_ICONINFORMATION);
  end;
end;

//=============================================================================
//  TplDropStringGridコンポのOnDropDownListイベント処理
//  ドロップダウンのボタンをクリックした時のイベント
//
//  このイベントを作成しなければDefaultAction := True;と同じ
//  ドロップダウン用のボタンで,他のウィンドウを表示する用途に使用可能
//=============================================================================
procedure TForm1.plDropStringGrid1DropDownList(Sender: TObject; ACol,
  ARow: Integer; ARect: TRect; var DefaultAction: Boolean);
begin
  //Falseにするとドロップダウンのリストを表示しない
  DefaultAction := True;
end;

end.




02_ドロップダウンリストのカスタム描画


本コンポーネントで実装した OnDropListDrawItem イベントを使用して,ドロップダウンのリストをカスタマイズする例です.
本サンプルは,単に選択中の項目の背景色を変更するだけですが,このリスト表示のコントロールには TCanvas があります.いろいなカスタム描画が可能です.OnDropListDrawItem イベントを実装すると,リストの項目の全てを自前で描画することになります.デフォルトの描画は行われません.


図5
リストの選択項目の背景を変更

リスト2
ドロップダウンリストのカスタム描画の例
unit Unit1;

interface

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

type
  TForm1 = class(TForm)
    Panel1: TPanel;
    plDropStringGrid1: TplDropStringGrid;
    procedure FormCreate(Sender: TObject);
    procedure plDropStringGrid1GetEditStyle(TSender: TObject; ACol,
      ARow: Integer; var EditStyle: TEditStyle);
    procedure FormDestroy(Sender: TObject);
    procedure plDropStringGrid1GetPickListItems(ACol, ARow: Integer;
      Items: TStrings);
    procedure plDropStringGrid1DropListDrawItem(Control: TWinControl;
      Index: Integer; Rect: TRect; State: TOwnerDrawState);
  private
    { Private 宣言 }
    PickList : TStringList;
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

//=============================================================================
//  テスト用に列数と列幅等を設定し,テスト用のデータを各セルにセット
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
  plDropStringGrid1.DefaultDrawing := True;
  //編集可能にしないとインプレイスエディタは起動しない
  plDropStringGrid1.Options := plDropStringGrid1.Options + [goEditing];

  plDropStringGrid1.FixedCols       := 1;
  plDropStringGrid1.FixedRows       := 1;
  plDropStringGrid1.ColCount        := 3;
  plDropStringGrid1.RowCount        := 4;
  plDropStringGrid1.DefaultColWidth := 120;

  //左端の列幅設定
  plDropStringGrid1.ColWidths[0]  := 10;

  //テスト用の値をセット(固定行)
  plDropStringGrid1.Cells[1,0] := '都道府県';
  plDropStringGrid1.Cells[2,0] := '調査番号';

  //テスト用の値をセット
  plDropStringGrid1.Cells[2,1] := 'A-100';
  plDropStringGrid1.Cells[2,2] := 'A-200';
  plDropStringGrid1.Cells[2,3] := 'A-300';

  //ドロップ表示する時のリストを作成
  PickList := TStringList.Create;
  PickList.Clear;
  with PickList do begin
    Add('東京都');
    Add('埼玉県');
    Add('神奈川県');
    Add('群馬県');
    Add('栃木県');
    Add('茨城県');
    Add('千葉県');
    Add('長野県');
    Add('山梨県');
    Add('岐阜県');
    Add('新潟県');
    Add('福島県');
    Add('山形県');
  end;

  //各種プロパティの設定(TplDropStringGridで追加したもの)
  plDropStringGrid1.InplaceButtonWidth := 17;
  plDropStringGrid1.DropListRowCount   := 10;
  plDropStringGrid1.DropListItemHeight := 17;
  plDropStringGrid1.DropListColor      := clWindow;
end;

//=============================================================================
//  フォーム破棄時の処理
//=============================================================================
procedure TForm1.FormDestroy(Sender: TObject);
begin
  FreeAndNil(PickList);
end;

//=============================================================================
//  TplDropStringGridコンポのOnGetEditStyleイベント処理
//  列単位でボタンの種類を設定
//=============================================================================
procedure TForm1.plDropStringGrid1GetEditStyle(TSender: TObject; ACol,
  ARow: Integer; var EditStyle: TEditStyle);
begin
  if ACol = 1 then EditStyle := esPickList;
end;

//=============================================================================
//  TplDropStringGridコンポのOnGetPickListItemsイベント処理
//  ドロップダウンで表示するリストの内容を設定
//=============================================================================
procedure TForm1.plDropStringGrid1GetPickListItems(ACol, ARow: Integer;
  Items: TStrings);
begin
  if ACol = 1 then Items.Assign(PickList);
end;

//=============================================================================
//  TplDropStringGridコンポのOnDropListDrawItemイベント処理
//  このイベントはTListBoxのOnDrawItemイベントに相当する
//  ドロップダウンで表示するリストの描画をカスタマイズ
//  このイベントを設定すると,全てこのイベント内で描画する必要がある
//=============================================================================
procedure TForm1.plDropStringGrid1DropListDrawItem(Control: TWinControl;
  Index: Integer; Rect: TRect; State: TOwnerDrawState);
var
  LControl    : TCustomListBox;
  LCanvas     : TCanvas;
  LBackground : TColor;
  Flags       : Integer;
  LItem       : String;
begin
  if plDropStringGrid1.Col = 1 then begin
    LControl    := TCustomListBox(Control);
    LCanvas     := LControl.Canvas;
    LBackground := LCanvas.Brush.Color;

    //カスタム描画の場合,まず背景を塗り潰した方が無難
    LCanvas.Brush.Style := bsSolid;

    if odSelected in State then begin
      LCanvas.Brush.Color := $00E7FFDD;
      LCanvas.FillRect(Rect);
      LCanvas.Font.Color := clBlack;
    end else begin
      LCanvas.Brush.Color := LBackground;
      LCanvas.FillRect(Rect);
      LCanvas.Font.Color := clBlack;
    end;

    InflateRect(Rect, -5, 0);
    Flags := DT_SINGLELINE or DT_LEFT or DT_VCENTER or DT_NOPREFIX;
    LItem := LControl.Items[Index];
    DrawText(LCanvas.Handle, PChar(LItem), -1, Rect, Flags);
  end;
end;

end.




03_コンボボックス風のドロップダウンリスト


上のサンプルは,ドロップダウン表示したリストの項目をクリックするか,[Enter] キーで閉じないとセルの値は変更しません.本サンプルは,矢印キーで選択項目を移動すると同時に,セルに表示する値も変更する例です.
本コンポーネントで実装した OnDropListClick( イベントを使用しています.

本サンプルの左側の列は ReadOnly となっています.キーボードからの入力はできません.



図6
ドロップダウンのリストを表示
  • リストの項目の値がセルに表示される
図7
リストの選択項目を変更
  • セルの値も変わる

リスト3
DropListClick イベントの使用例.コンボボックス風のドロップダウンリスト
unit Unit1;

interface

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

type
  TForm1 = class(TForm)
    Panel1: TPanel;
    plDropStringGrid1: TplDropStringGrid;
    procedure FormCreate(Sender: TObject);
    procedure plDropStringGrid1GetEditStyle(TSender: TObject; ACol,
      ARow: Integer; var EditStyle: TEditStyle);
    procedure FormDestroy(Sender: TObject);
    procedure plDropStringGrid1GetPickListItems(ACol, ARow: Integer;
      Items: TStrings);
    procedure plDropStringGrid1DropListDrawItem(Control: TWinControl;
      Index: Integer; Rect: TRect; State: TOwnerDrawState);
    procedure plDropStringGrid1DropListClick(Control: TWinControl; ACol,
      ARow: Integer; ARect: TRect; Index: Integer; ListItem: string;
      InpEdit: TplInplaceEdit);
    procedure plDropStringGrid1GetEditText(Sender: TObject; ACol, ARow: Integer;
      var Value: string);
  private
    { Private 宣言 }
    PickList : TStringList;
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

//=============================================================================
//  テスト用に列数と列幅等を設定し,テスト用のデータを各セルにセット
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
  plDropStringGrid1.DefaultDrawing := True;
  //編集可能にしないとインプレイスエディタは起動しない
  plDropStringGrid1.Options := plDropStringGrid1.Options + [goEditing];

  plDropStringGrid1.FixedCols       := 1;
  plDropStringGrid1.FixedRows       := 1;
  plDropStringGrid1.ColCount        := 3;
  plDropStringGrid1.RowCount        := 4;
  plDropStringGrid1.DefaultColWidth := 120;

  //左端の列幅設定
  plDropStringGrid1.ColWidths[0]  := 10;

  //テスト用の値をセット(固定行)
  plDropStringGrid1.Cells[1,0] := '都道府県';
  plDropStringGrid1.Cells[2,0] := '調査番号';

  //テスト用の値をセット
  plDropStringGrid1.Cells[2,1] := 'A-100';
  plDropStringGrid1.Cells[2,2] := 'A-200';
  plDropStringGrid1.Cells[2,3] := 'A-300';

  //ドロップ表示する時のリストを作成
  PickList := TStringList.Create;
  PickList.Clear;
  with PickList do begin
    Add('東京都');
    Add('埼玉県');
    Add('神奈川県');
    Add('群馬県');
    Add('栃木県');
    Add('茨城県');
    Add('千葉県');
    Add('長野県');
    Add('山梨県');
    Add('岐阜県');
    Add('新潟県');
    Add('福島県');
    Add('山形県');
  end;

  //各種プロパティの設定(TplDropStringGridで追加したもの)
  plDropStringGrid1.InplaceButtonWidth := 17;
  plDropStringGrid1.DropListRowCount   := 10;
  plDropStringGrid1.DropListItemHeight := 17;
  plDropStringGrid1.DropListColor      := clWindow;
end;

//=============================================================================
//  フォーム破棄時の処理
//=============================================================================
procedure TForm1.FormDestroy(Sender: TObject);
begin
  FreeAndNil(PickList);
end;

//=============================================================================
//  TplDropStringGridコンポのOnGetEditStyleイベント処理
//  列単位でボタンの種類を設定
//=============================================================================
procedure TForm1.plDropStringGrid1GetEditStyle(TSender: TObject; ACol,
  ARow: Integer; var EditStyle: TEditStyle);
begin
  if ACol = 1 then EditStyle := esPickList;
end;

//=============================================================================
//  TplDropStringGridコンポのOnGetPickListItemsイベント処理
//  ドロップダウンで表示するリストの内容を設定
//=============================================================================
procedure TForm1.plDropStringGrid1GetPickListItems(ACol, ARow: Integer;
  Items: TStrings);
begin
  if ACol = 1 then Items.Assign(PickList);
end;

//=============================================================================
//  TplDropStringGridコンポのOnDropListDrawItemイベント処理
//  このイベントはTListBoxのOnDrawItemイベントに相当する
//  ドロップダウンで表示するリストの描画をカスタマイズ
//  このイベントを設定すると,全てこのイベント内で描画する必要がある
//=============================================================================
procedure TForm1.plDropStringGrid1DropListDrawItem(Control: TWinControl;
  Index: Integer; Rect: TRect; State: TOwnerDrawState);
var
  LControl    : TCustomListBox;
  LCanvas     : TCanvas;
  LBackground : TColor;
  Flags       : Integer;
  LItem       : String;
begin
  if plDropStringGrid1.Col = 1 then begin
    LControl    := TCustomListBox(Control);
    LCanvas     := LControl.Canvas;
    LBackground := LCanvas.Brush.Color;

    //カスタム描画の場合,まず背景を塗り潰した方が無難
    LCanvas.Brush.Style := bsSolid;

    if odSelected in State then begin
      LCanvas.Brush.Color := $00E7FFDD;
      LCanvas.FillRect(Rect);
      LCanvas.Font.Color := clBlack;
    end else begin
      LCanvas.Brush.Color := LBackground;
      LCanvas.FillRect(Rect);
      LCanvas.Font.Color := clBlack;
    end;

    InflateRect(Rect, -5, 0);
    Flags := DT_SINGLELINE or DT_LEFT or DT_VCENTER or DT_NOPREFIX;
    LItem := LControl.Items[Index];
    DrawText(LCanvas.Handle, PChar(LItem), -1, Rect, Flags);
  end;
end;

//=============================================================================
//  TplDropStringGridコンポのOnDropListClickイベント処理
//
//  このイベントは,ドロップダウンのウィンドウの以下の操作で発生する
//  (1) [Enter]キーで閉じた
//  (2) マウスクリックで閉じた
//  (3) 矢印キーで選択項目を変更した
//
//  このイベントのコードは,ドロップダウンのリストで,上記の操作を行った場合
//  インプレスエディタのセルに選択中の項目の値を表示する
//  つまり,ドロップダウンリストをコンボボックスのように見せる例
//=============================================================================
procedure TForm1.plDropStringGrid1DropListClick(Control: TWinControl; ACol,
  ARow: Integer; ARect: TRect; Index: Integer; ListItem: string;
  InpEdit: TplInplaceEdit);
begin
  if InpEdit <> nil then begin
    if ACol = 1 then begin
      InpEdit.Text := ListItem;
    end;
  end;
end;

//=============================================================================
//  インプレイスエディタがセルのテキストを取得した時発生するイベント
//  インプレスエディタ関係では一番早く発生するイベント
//
//  背景やフォント,テキストの左右のアライメントはこのイベント内で設定する
//  特定の列をReadOnlyにする例
//=============================================================================
procedure TForm1.plDropStringGrid1GetEditText(Sender: TObject; ACol,
  ARow: Integer; var Value: string);
var
  InpEdit : TplInplaceEdit;
begin
  InpEdit := TplDropStringGrid(Sender).InplaceEditor;
  if InpEdit <> nil then begin
    if ACol = 1 then begin
      InpEdit.ReadOnly := True;
    end;
  end;
end;

end.




04_セルの編集終了時のイベントの使用例  -  入力した値のチェック


セルに入力した値をチェックする 1 つの方法です.
本コンポーネントで実装した OnInplaceEditClose イベントを使用した例です.

OnInplaceEditClose イベントは,セルの編集が終了すると発生します.セルの編集が終了するのは,インプレイスエディタが閉じた時です.本サンプルでは,指定した値以外の時はダイアログを表示して,他のセルには移動できないようにしています.注意を促すダイアログだけは表示して,別のセルへ移動するようにすることも可能です.入力だけはしておいて,後で一括してチェックする等の用途に利用できるでしょう.
本サンプルのコードは,フォームを閉じる時は入力の値をチェックしません.



TStringGrid では,インプレイスエディタに TMaskEdit と同じような機能を持たすことができます.OnGetEditMask イベントで設定します.
マウスやキー操作のイベントも検出できますから,半角の英数字であれば,それらのイベント内でタイプした値をチェックできます.その時,セルの編集中でのイベントなのかは,インプレイスエディタが表示されているかで判定できます.インプレイスエディタが表示されていれば,インプレイスエディタの Visible プロパティが True となっています.

マスクの設定は,実際にフォームに TMaskEdit コンポーネントを配置して確認すると便利です.
TMaskEdit コンポーネントのポップアップメニューの [入力マスクの設定],あるいはオブジェクトインスペクタの EditMask から設定ダイアログを表示することができます.



図8
指定の値以外の時はダイアログを表示
  • 別のセルには移動できない
  • フォームを閉じることはできる

リスト4
セルに入力した値のチェック - セル編集終了時のイベントの使用例
unit Unit1;

interface

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

type
  TForm1 = class(TForm)
    Panel1: TPanel;
    plDropStringGrid1: TplDropStringGrid;
    procedure FormCreate(Sender: TObject);
    procedure plDropStringGrid1GetEditStyle(TSender: TObject; ACol,
      ARow: Integer; var EditStyle: TEditStyle);
    procedure FormDestroy(Sender: TObject);
    procedure plDropStringGrid1GetPickListItems(ACol, ARow: Integer;
      Items: TStrings);
    procedure plDropStringGrid1InplaceEditClose(Sender: TObject; ACol,
      ARow: Integer; Value: string; var CanNextCell: Boolean);
  private
    { Private 宣言 }
    PickList1 : TStringList;
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

//=============================================================================
//  テスト用に列数と列幅等を設定し,テスト用のデータを各セルにセット
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
  plDropStringGrid1.DefaultDrawing := True;
  //編集可能にしないとインプレイスエディタは起動しない
  plDropStringGrid1.Options := plDropStringGrid1.Options + [goEditing];

  plDropStringGrid1.FixedCols       := 1;
  plDropStringGrid1.FixedRows       := 1;
  plDropStringGrid1.ColCount        := 3;
  plDropStringGrid1.RowCount        := 4;
  plDropStringGrid1.DefaultColWidth := 120;

  //左端の列幅設定
  plDropStringGrid1.ColWidths[0]  := 10;

  //テスト用の値をセット(固定行)
  plDropStringGrid1.Cells[1,0] := '都道府県';
  plDropStringGrid1.Cells[2,0] := '調査結果';

  //ドロップ表示する時のリストを作成
  PickList1 := TStringList.Create;
  PickList1.BeginUpdate;
  with PickList1 do begin
    Add('東京都');
    Add('埼玉県');
    Add('神奈川県');
    Add('群馬県');
    Add('栃木県');
    Add('茨城県');
    Add('千葉県');
    Add('長野県');
    Add('山梨県');
    Add('岐阜県');
    Add('新潟県');
    Add('福島県');
    Add('山形県');
  end;
  PickList1.EndUpdate;

  //各種プロパティの設定(TplDropStringGridで追加したもの)
  plDropStringGrid1.InplaceButtonWidth := 17;
  plDropStringGrid1.DropListRowCount   := 10;
  plDropStringGrid1.DropListItemHeight := 16;
  plDropStringGrid1.DropListColor      := clWindow;
end;

//=============================================================================
//  フォーム破棄時の処理
//=============================================================================
procedure TForm1.FormDestroy(Sender: TObject);
begin
  FreeAndNil(PickList1);
end;

//=============================================================================
//  TplDropStringGridコンポのOnGetEditStyleイベント処理
//  編集時にボタンを表示するセルを指定する
//=============================================================================
procedure TForm1.plDropStringGrid1GetEditStyle(TSender: TObject; ACol,
  ARow: Integer; var EditStyle: TEditStyle);
begin
  if ACol = 1 then EditStyle := esPickList;
end;

//=============================================================================
//  TplDropStringGridコンポのOnGetPickListItemsイベント処理
//  ドロップダウンで表示するリストの項目の設定
//=============================================================================
procedure TForm1.plDropStringGrid1GetPickListItems(ACol, ARow: Integer;
  Items: TStrings);
begin
  if ACol = 1 then Items.Assign(PickList1);
end;

//=============================================================================
//  TplDropStringGridコンポのOnInplaceEditCloseイベント処理
//  セルの編集終了時に発生するイベント
//  以下の場合に発生する
//  (1) セルの編集を[Enter]キーで終了した場合
//  (2) セルの編集中に別のセルに移動した場合
//  親フォームが閉じる時は発生しない
//
//  CanNextCell
//    Trueの場合,このイベント実行後に編集を終了して,選択したセルに移動可能
//    デフォルトはTrue
//=============================================================================
procedure TForm1.plDropStringGrid1InplaceEditClose(Sender: TObject; ACol,
  ARow: Integer; Value: string; var CanNextCell: Boolean);
var
  LGrid   : TplDropStringGrid;
  TheText : String;
begin
  LGrid := TplDropStringGrid(Sender);

  if ACol = 1 then begin
    if Value <> '埼玉県' then begin
      CanNextCell := False;

      TheText := '埼玉県を選択してください.';
      MessageBox(Handle, PChar(TheText), 'お願い', MB_ICONINFORMATION);

      //該当セルのテキストを選択か非選択状態にするか
      //SetFosusを実行すると確実なようである
      //この動作はOptionsの値によっても異なる場合がある
      if CanNextCell = False then begin
        LGrid.SetFocus;
        LGrid.EditorMode := True;
        LGrid.InplaceEditor.SelectAll;

        //選択状態を解除する場合
        //LGrid.InplaceEditor.SelStart := 0;
      end;
    end;
  end;
end;

end.




05_フォームが閉じる時もセルの編集終了イベントを発生させる


前項のサンプルでは,フォームが閉じ時は,編集終了後のセルの値をチェックしません.したがってダイアログも表示しません.フォームを閉じる時にダイアログを表示しても,結局フォームが閉じてしまうのですから,意味がないと考えて,コンポーネントの内部でそのような処理をしています.
フォームを閉じる時にもダイアログを表示して,フォームを閉じないようにするには,
  • フォームを閉じてもいいかの判定用に,論理型のグローバル変数を用意する
  • その変数の値の初期値を True とする
  • フォームの OnCloseQuery イベントの CanClose 引数の値を,その変数の値とする
  • 入力した値に問題がある場合は,その変数の値を False にする
という処理を追加します.以下のサンプルは,そのような処理を追加したコードです.


図9
フォームを閉じる時も入力のチェックを実行
  • フォームを閉じることはできない

リスト5
セルに入力した値のチェック - セル編集終了時のイベントの使用例
フォームを閉じる時もセルの編集終了イベントを実行する例
unit Unit1;

interface

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

type
  TForm1 = class(TForm)
    Panel1: TPanel;
    plDropStringGrid1: TplDropStringGrid;
    procedure FormCreate(Sender: TObject);
    procedure plDropStringGrid1GetEditStyle(TSender: TObject; ACol,
      ARow: Integer; var EditStyle: TEditStyle);
    procedure FormDestroy(Sender: TObject);
    procedure plDropStringGrid1GetPickListItems(ACol, ARow: Integer;
      Items: TStrings);
    procedure plDropStringGrid1InplaceEditClose(Sender: TObject; ACol,
      ARow: Integer; Value: string; var CanNextCell: Boolean);
    procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
  private
    { Private 宣言 }
    PickList1 : TStringList;
    FCanFormClose : Boolean;
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

//=============================================================================
//  OnCloseQueryイベント処理
//  グローバル変数を使用して,入力ミスがある場合はフォームを閉じないようにする
//=============================================================================
procedure TForm1.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin
  CanClose := FCanFormClose;
end;

//=============================================================================
//  テスト用に列数と列幅等を設定し,テスト用のデータを各セルにセット
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
  //初期値はフォームが閉じることができる設定
  FCanFormClose := True;

  plDropStringGrid1.DefaultDrawing := True;
  //編集可能にしないとインプレイスエディタは起動しない
  plDropStringGrid1.Options := plDropStringGrid1.Options + [goEditing];

  plDropStringGrid1.FixedCols       := 1;
  plDropStringGrid1.FixedRows       := 1;
  plDropStringGrid1.ColCount        := 3;
  plDropStringGrid1.RowCount        := 4;
  plDropStringGrid1.DefaultColWidth := 120;

  //左端の列幅設定
  plDropStringGrid1.ColWidths[0]  := 10;

  //テスト用の値をセット(固定行)
  plDropStringGrid1.Cells[1,0] := '都道府県';
  plDropStringGrid1.Cells[2,0] := '調査結果';

  //ドロップ表示する時のリストを作成
  PickList1 := TStringList.Create;
  PickList1.BeginUpdate;
  with PickList1 do begin
    Add('東京都');
    Add('埼玉県');
    Add('神奈川県');
    Add('群馬県');
    Add('栃木県');
    Add('茨城県');
    Add('千葉県');
    Add('長野県');
    Add('山梨県');
    Add('岐阜県');
    Add('新潟県');
    Add('福島県');
    Add('山形県');
  end;
  PickList1.EndUpdate;

  //各種プロパティの設定(TplDropStringGridで追加したもの)
  plDropStringGrid1.InplaceButtonWidth := 17;
  plDropStringGrid1.DropListRowCount   := 10;
  plDropStringGrid1.DropListItemHeight := 16;
  plDropStringGrid1.DropListColor      := clWindow;
end;

//=============================================================================
//  フォーム破棄時の処理
//=============================================================================
procedure TForm1.FormDestroy(Sender: TObject);
begin
  FreeAndNil(PickList1);
end;

//=============================================================================
//  TplDropStringGridコンポのOnGetEditStyleイベント処理
//  編集時にボタンを表示するセルを指定する
//=============================================================================
procedure TForm1.plDropStringGrid1GetEditStyle(TSender: TObject; ACol,
  ARow: Integer; var EditStyle: TEditStyle);
begin
  if ACol = 1 then EditStyle := esPickList;
end;

//=============================================================================
//  TplDropStringGridコンポのOnGetPickListItemsイベント処理
//  ドロップダウンで表示するリストの項目の設定
//=============================================================================
procedure TForm1.plDropStringGrid1GetPickListItems(ACol, ARow: Integer;
  Items: TStrings);
begin
  if ACol = 1 then Items.Assign(PickList1);
end;

//=============================================================================
//  TplDropStringGridコンポのOnInplaceEditCloseイベント処理
//  セルの編集終了時に発生するイベント
//  以下の場合に発生する
//  (1) セルの編集を[Enter]キーで終了した場合
//  (2) セルの編集中に別のセルに移動した場合
//  親であるフォームが閉じる時は発生しない
//
//  CanNextCell
//    Trueの場合,このイベント実行後に編集を終了して,選択したセルに移動可能
//    デフォルトはTrue
//
//  グローバル変数を使用して,入力ミスがある場合はフォームを閉じないようにする
//=============================================================================
procedure TForm1.plDropStringGrid1InplaceEditClose(Sender: TObject; ACol,
  ARow: Integer; Value: string; var CanNextCell: Boolean);
var
  LGrid   : TplDropStringGrid;
  TheText : String;
begin
  LGrid := TplDropStringGrid(Sender);

  FCanFormClose := True;
  if ACol = 1 then begin
    if Value <> '埼玉県' then begin
      CanNextCell   := False;
      FCanFormClose := CanNextCell;

      TheText := '埼玉県を選択してください.';
      MessageBox(Handle, PChar(TheText), 'お願い', MB_ICONINFORMATION);

      //該当セルのテキストを選択か非選択状態にするか
      //SetFosusを実行すると確実なようである
      //この動作はOptionsの値によっても異なる場合がある
      if CanNextCell = False then begin
        LGrid.SetFocus;
        LGrid.EditorMode := True;
        LGrid.InplaceEditor.SelectAll;

        //選択状態を解除する場合
        //LGrid.InplaceEditor.SelStart := 0;
      end;
    end;
  end;
end;

end.




06_インプレイスエディタの縦方向のアライメントと左右のインデント


インプレイスエディタの縦方向のアライメント,つまり,文字列の上下方向の表示位置と,左右のインデントを設定するサンプルです.
TStringGrid のセルの文字列は,OnDrawCell イベントで描画位置を設定できますが,セルの編集を開始すると,デフォルトでは左上から文字列が表示されます.そこで,セルへの表示と編集時の表示位置を同じにできるようなプロパティを実装しました.

インプレイスエディタ関係のプロパティの設定は,インプレイスエディタが起動しているか,表示状態でないと設定できません.
TStringGrid 関係のコンポーネントでは,TDBGrid のように,カラムエディタ等を実装して列ごとにプロパティを設定できるものがあります.あるいは,列や行,セル毎にプロパティを設定できるものもあります.これらのプロパティは,クラス型等のメンバの値として保持していて,必要となった時に実際の設定を実行します.

しかし,本ページで使用しているコンポーネントは,そのような実装はしていません.イベント内で設定することになります.インプレスエディタの状態によって,プロパティが有効になるタイミングが違っています.本サンプルの縦方向のアライメントと左右のインデントの設定は,インプレイスエディタが入力可能な状態になった,OnInplaceEditFocused イベント内で設定すると有効になります.
その他のインプレイスエディタ関係のプロパティの設定については,サンプルコードのコメントで確認してください.

本サンプルでは日本語 IME の制御を行っています.日本語 IME の無効化に Win32NLSEnableIME という関数を使用しています.日本語 IME に関しては,以下の記事も参考にしてください.



図10
起動直後.編集状態にはなっていない状態
図11
左側の列のセルの値を編集中
  • 編集中でない時と文字列の表示位置が同じ
  • 編集中はセルの背景色を変更
  • 日本語 IME を自動的に起動
図12
右側の列のセルの値を編集中
  • 編集中でない時と文字列の表示位置が同じ
  • 編集中はセルの背景色を変更
  • 日本語 IME は自動的に無効にしている

リスト6
インプレイスエディタの縦方向のアライメントと左右のインデント
unit Unit1;

interface

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

type
  TForm1 = class(TForm)
    Panel1: TPanel;
    plDropStringGrid1: TplDropStringGrid;
    procedure FormCreate(Sender: TObject);
    procedure plDropStringGrid1DrawCell(Sender: TObject; ACol, ARow: Integer;
      Rect: TRect; State: TGridDrawState);
    procedure plDropStringGrid1GetEditText(Sender: TObject; ACol, ARow: Integer;
      var Value: string);
    procedure plDropStringGrid1DblClick(Sender: TObject);
    procedure plDropStringGrid1InplaceEditFocused(Sender: TObject; ACol,
      ARow: Integer; ARect: TRect; InpEdit: TplInplaceEdit);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

uses imm;

{$R *.dfm}

//=============================================================================
//  テスト用に列数と列幅等を設定し,テスト用のデータを各セルにセット
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
var
  i : Integer;
begin
  plDropStringGrid1.DefaultDrawing := True;
  //編集可能にしないとインプレイスエディタは起動しない
  plDropStringGrid1.Options := plDropStringGrid1.Options + [goEditing];

  plDropStringGrid1.Font.Name := 'MS Pゴシック';
  plDropStringGrid1.Font.Size := 10;

  plDropStringGrid1.FixedCols       := 1;
  plDropStringGrid1.FixedRows       := 1;
  plDropStringGrid1.ColCount        := 3;
  plDropStringGrid1.RowCount        := 4;

  plDropStringGrid1.DefaultRowHeight := 24;
  for i := 1 to plDropStringGrid1.RowCount - 1 do begin
    plDropStringGrid1.RowHeights[i] := 40;
  end;
  plDropStringGrid1.DefaultColWidth  := 120;

  //左端の列幅設定
  plDropStringGrid1.ColWidths[0]  := 10;

  //テスト用の値をセット(固定行)
  plDropStringGrid1.Cells[1,0] := '種類';
  plDropStringGrid1.Cells[2,0] := '調査結果';

  plDropStringGrid1.Cells[1,1] := '穀物類';
  plDropStringGrid1.Cells[1,2] := '果物類';
  plDropStringGrid1.Cells[1,3] := '野菜類';

  plDropStringGrid1.Cells[2,1] := '100';
  plDropStringGrid1.Cells[2,2] := '200';
  plDropStringGrid1.Cells[2,3] := '300';
end;

//=============================================================================
//  OnDblClickイベント処理
//  ダブルクリックで編集モードに入る場合,ここで設定可能なプロパティがある
//=============================================================================
procedure TForm1.plDropStringGrid1DblClick(Sender: TObject);
var
  InpEdit : TplInplaceEdit;
begin
  InpEdit := TplDropStringGrid(Sender).InplaceEditor;
  if (InpEdit <> nil) and (InpEdit.Visible) then begin
    InpEdit.SelStart   := 0;
  end;
end;

//=============================================================================
//  OnDrawCellイベント
//=============================================================================
procedure TForm1.plDropStringGrid1DrawCell(Sender: TObject; ACol, ARow: Integer;
  Rect: TRect; State: TGridDrawState);
var
  LGrid   : TplDropStringGrid;
  LRect   : TRect;
  LCanvas : TCanvas;
  LItem   : String;
  Flags   : Integer;
begin
  LCanvas := TplDropStringGrid(Sender).Canvas;
  LCanvas.Font.Color := clBlack;
  LGrid := TplDropStringGrid(Sender);
  LRect := Rect;

  if not (gdFixed in State) then begin
    if ACol = 1 then begin
      LCanvas.Brush.Style := bsSolid;
      LCanvas.FillRect(Rect);

      LCanvas.Brush.Style := bsClear;
      LItem      := LGrid.Cells[ACol, ARow];
      Flags      := DT_SINGLELINE or DT_LEFT or DT_VCENTER or DT_NOPREFIX;
      LRect.Left := LRect.Left + 5;
      DrawText(LCanvas.Handle, PChar(LItem), -1, LRect, Flags);
    end else
    if ACol = 2 then begin
      LCanvas.Brush.Style := bsSolid;
      LCanvas.FillRect(LRect);

      LCanvas.Brush.Style := bsClear;
      LItem       := LGrid.Cells[ACol, ARow];
      Flags       := DT_SINGLELINE or DT_RIGHT or DT_VCENTER or DT_NOPREFIX;
      LRect.Right := LRect.Right - 9;
      DrawText(LCanvas.Handle, PChar(LItem), -1, LRect, Flags);
    end;
  end;
end;

//=============================================================================
//  インプレイスエディタがセルのテキストを取得した時発生するイベント
//  インプレスエディタ関係では一番早く発生するイベント
//
//  背景やフォント,テキストの左右のアライメントはこのイベント内で設定する
//=============================================================================
procedure TForm1.plDropStringGrid1GetEditText(Sender: TObject; ACol,
  ARow: Integer; var Value: string);
var
  LGrid   : TplDropStringGrid;
  InpEdit : TplInplaceEdit;
begin
  LGrid   := TplDropStringGrid(Sender);
  InpEdit := LGrid.InplaceEditor;

  //このイベント内では,インプレイスエディタはまだ未表示状態
  if (InpEdit <> nil) then begin
    //描画に関係する設定
    //背景色やフォントの設定はこのイベントで実行した方がいいようだ
    InpEdit.Color       := $00EEEEEE;
    InpEdit.BorderStyle := bsSingle;
    InpEdit.Font.Size   := TplDropStringGrid(Sender).Font.Size;

    if ACol = 1 then begin
      LGrid.InplaceEditor.Alignment := taLeftJustify;
    end else
    if ACol = 2 then begin
      LGrid.InplaceEditor.Alignment := taRightJustify;
    end else begin
      //その他のセルはデフォルト値とする
      LGrid.InplaceEditor.Alignment := taLeftJustify;
    end;
  end;
end;

//=============================================================================
//  TplDropStringGridの追加したOnInplaceEditFocusedイベント
//  インプレイスエディタが入力フォーカスを受け取った時のイベント
//  縦方向のアライメントと左右のインデントはここで設定する
//=============================================================================
procedure TForm1.plDropStringGrid1InplaceEditFocused(Sender: TObject; ACol,
  ARow: Integer; ARect: TRect; InpEdit: TplInplaceEdit);
var
  LGrid  : TplDropStringGrid;
begin
  LGrid := TplDropStringGrid(Sender);

  //[F2]で編集モードに入った場合は無視されるようだ
  InpEdit.SelStart    := 0;
  InpEdit.AutoSelect  := False;

  if ACol = 1 then begin
    LGrid.EditLeftIndent    := LGrid.EditDefaultLeftIndent;
    LGrid.EditRightIndent   := LGrid.EditDefaultRightIndent;
    LGrid.EditVertAlignment := vaCenter;
  end else
  if ACol = 2 then begin
    LGrid.EditLeftIndent    := LGrid.EditDefaultLeftIndent;
    LGrid.EditRightIndent   := LGrid.EditDefaultRightIndent;
    LGrid.EditVertAlignment := vaCenter;
  end else begin
    //その他のセルはデフォルト値とする
    LGrid.EditLeftIndent    := LGrid.EditDefaultLeftIndent;
    LGrid.EditRightIndent   := LGrid.EditDefaultRightIndent;
    LGrid.EditVertAlignment := LGrid.EditDefaultVertAlignment;
  end;

  //imDisableを使用するとIMEの制御が不正になることがあるので注意
  //以下のコードには,usesにimmが必要
  if ACol = 1  then begin
    SetImeMode(InpEdit.Handle, imHira);
  end else
  if ACol = 2 then begin
    ImmAssociateContext(InpEdit.Handle, 0);
  end else begin
    ImmAssociateContext(InpEdit.Handle, 0);
  end;
end;

end.




07_インプレイスエディタに画像を表示


本コンポーネントのインプレイスエディタには TImage を配置しています.この TImage を使用したサンプルです.
左側の列では,ドロップダウンのリストを表示しますが.TColorBox のような動作をするようにしています.ドロップダウンのリストだけですと,TColorListBox の機能だけです.そこで,
  • ドロップダウンリストのウィンドの TCanvas
  • TStringGrid のグリッドの TCanvas
  • インプレイスエディタ上に配置した TImage の TCanvas
に色定数の文字列を表示して,その文字列の左側に,その色で塗り潰した矩形を描画します.オブジェクトインスペクタで Color プロパティの色を選択する操作のような感じになっています.


図13
左側の列でドロップダウンリストを表示
  • セルに,選択中の色の文字と,その色で塗り潰した矩形を描画する
  • この塗り潰している領域は TImage
図14
TColorListBox と TColorBox コンポーネント
  • 上が TColorListBox
  • 下が TColorBox
  • コンポーネントパレットの [Additional] タブにあるコンポーネント
  • FireMonkey には TColorComboBox というコンポーネントがある


右側の列のセルでは,編集状態になると,[開く] ビットマップの画像を右端に表示します.
その画像をクリックすると,セルに入力しているファイル名のファイルをメモ帳で表示します.


図15
右側のセルの場合
  • 編集時に画像を表示する
  • その画像をクリックするとメモ帳が起動する

リスト7
インプレイスエディタの TImage を使用したサンプル
unit Unit1;

interface

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

type
  TForm1 = class(TForm)
    Panel1: TPanel;
    plDropStringGrid1: TplDropStringGrid;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure plDropStringGrid1GetEditStyle(Sender: TObject; ACol,
      ARow: Integer; var EditStyle: TEditStyle);
    procedure plDropStringGrid1GetPickListItems(ACol, ARow: Integer;
      Items: TStrings);
    procedure plDropStringGrid1InplaceImageClick(Sender: TObject; ACol,
      ARow: Integer; ARect: TRect);
    procedure plDropStringGrid1DropListClick(Control: TWinControl; ACol,
      ARow: Integer; ARect: TRect; Index: Integer; ListItem: string;
      InpEdit: TplInplaceEdit);
    procedure plDropStringGrid1DrawCell(Sender: TObject; ACol, ARow: Integer;
      Rect: TRect; State: TGridDrawState);
    procedure plDropStringGrid1SetEditText(Sender: TObject; ACol, ARow: Integer;
      const Value: string);
    procedure plDropStringGrid1DropListDrawItem(Control: TWinControl;
      Index: Integer; Rect: TRect; State: TOwnerDrawState);
    procedure plDropStringGrid1InplaceEditFocused(Sender: TObject; ACol,
      ARow: Integer; ARect: TRect; InpEdit: TplInplaceEdit);
  private
    { Private 宣言 }
    PickList1 : TStringList;
    procedure GetColorStrProc(const S: string);
    function DrawColorRectangle(AGrid: TplDropStringGrid; ARect: TRect;
      StrColor: String): Integer;
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

uses ShellAPI;

{$R *.dfm}

//=============================================================================
//  テスト用に列数と列幅等を設定し,テスト用のデータを各セルにセット
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
  plDropStringGrid1.DefaultDrawing := True;
  //編集可能にしないとインプレイスエディタは起動しない
  plDropStringGrid1.Options := plDropStringGrid1.Options + [goEditing];

  plDropStringGrid1.FixedCols       := 1;
  plDropStringGrid1.FixedRows       := 1;
  plDropStringGrid1.ColCount        := 3;
  plDropStringGrid1.RowCount        := 4;
  plDropStringGrid1.ColWidths[1] := 160;
  plDropStringGrid1.ColWidths[2] := 120;

  //左端の列幅設定
  plDropStringGrid1.ColWidths[0]  := 10;

  //テスト用の値をセット(固定行)
  plDropStringGrid1.Cells[1,0] := '指定色';
  plDropStringGrid1.Cells[2,0] := 'ファイル名';

  //ドロップ表示する時のリストを作成
  //色の定数文字列をセット
  PickList1 := TStringList.Create;
  PickList1.Clear;
  PickList1.BeginUpdate;
  GetColorValues(GetColorStrProc);
  PickList1.EndUpdate;

  plDropStringGrid1.Cells[2,1] := 'Project1.dpr';
  plDropStringGrid1.Cells[2,2] := 'Unit1.pas';
  plDropStringGrid1.Cells[2,3] := 'Project1.dproj';

  //各種プロパティの設定(TplDropStringGridで追加したもの)
  plDropStringGrid1.DropListItemHeight := 17;
end;

//=============================================================================
//  フォーム破棄時の処理
//=============================================================================
procedure TForm1.FormDestroy(Sender: TObject);
begin
  FreeAndNil(PickList1);
end;

//=============================================================================
//  TplDropStringGridコンポのOnGetEditStyleイベント処理
//  編集時にボタンを表示するセルを指定する
//=============================================================================
procedure TForm1.plDropStringGrid1GetEditStyle(Sender: TObject; ACol,
  ARow: Integer; var EditStyle: TEditStyle);
begin
  if ACol = 1 then EditStyle := esPickList;
end;

//=============================================================================
//  TplDropStringGridコンポのOnGetPickListItemsイベント処理
//  ドロップダウンで表示するリストの項目の設定
//=============================================================================
procedure TForm1.plDropStringGrid1GetPickListItems(ACol, ARow: Integer;
  Items: TStrings);
begin
  if ACol = 1 then Items.Assign(PickList1);
end;

//=============================================================================
//  TStringGridコンポのOnDrawCellイベント処理
//  このイベントのコードがない場合はデフォルトの描画が行われる
//=============================================================================
procedure TForm1.plDropStringGrid1DrawCell(Sender: TObject; ACol, ARow: Integer;
  Rect: TRect; State: TGridDrawState);
var
  LGrid   : TplDropStringGrid;
  LCanvas : TCanvas;
  LRect   : TRect;
  LItem   : String;
  Flags   : Integer;
begin
  LGrid   := TplDropStringGrid(Sender);
  LCanvas := LGrid.Canvas;
  LRect   := Rect;
  LCanvas.Font.Color := clBlack;

  if not (gdFixed in State) then begin
    if ACol = 1 then begin
      LCanvas.Brush.Style := bsSolid;
      LCanvas.FillRect(Rect);

      //インプレイスエディタ上のTImageの位置とサイズを設定して塗り潰す
      LItem      := LGrid.Cells[ACol, ARow];
      LRect.Left := DrawColorRectangle(LGrid, Rect, LItem);

      //背景を透過にして文字列を描画
      LCanvas.Brush.Style := bsClear;
      Flags      := DT_SINGLELINE or DT_VCENTER or DT_NOPREFIX;
      DrawText(LCanvas.Handle, PChar(LItem), -1, LRect, Flags);
    end else
    if ACol = 2 then begin
      LCanvas.Brush.Style := bsSolid;
      LCanvas.FillRect(LRect);

      //背景を透過にして文字列を描画
      LCanvas.Brush.Style := bsClear;
      LItem      := LGrid.Cells[ACol, ARow];
      LRect.Left := LRect.Left + 6;
      Flags      := DT_SINGLELINE or DT_VCENTER or DT_NOPREFIX;
      DrawText(LCanvas.Handle, PChar(LItem), -1, LRect, Flags);
    end;
  end;
end;

//=============================================================================
//  ドロップダウンリストのカスタム描画用のイベント
//  TplDropStringGridで追加したイベント
//  TListBoxのOnDrawItemイベントに相当する
//  TColorListBoxのようなリストを作成
//
//  このイベントを作成すると,リストの項目を全て自前で描画することになる
//=============================================================================
procedure TForm1.plDropStringGrid1DropListDrawItem(Control: TWinControl;
  Index: Integer; Rect: TRect; State: TOwnerDrawState);
var
  LGrid       : TplDropStringGrid;
  LControl    : TCustomListBox;
  LCanvas     : TCanvas;
  LItem       : String;
  LRect       : TRect;
  LBackground : TColor;
  InpEditor   : TInplaceEdit;
  Flags       : Integer;
begin
  LGrid     := plDropStringGrid1;
  InpEditor := LGrid.InplaceEditor;
  if InpEditor <> nil then begin
    InpEditor.SelStart := 0;
  end;

  LControl := TCustomListBox(Control);
  LCanvas  := LControl.Canvas;
  LItem    := LControl.Items[Index];

  LCanvas.FillRect(Rect);
  LBackground := LCanvas.Brush.Color;

  if LGrid.Col = 1 then begin
    //各項目の左端に矩形を描き,項目値の色定数文字列の色で塗り潰す
    LRect       := Rect;
    LRect.Right := LRect.Bottom - LRect.Top + LRect.Left + 5;
    InflateRect(LRect, -3, -1);
    LCanvas.Brush.Color := StringToColor(LItem);
    LCanvas.FillRect(LRect);

    //枠線
    LCanvas.Brush.Color := clGray;
    LCanvas.FrameRect(LRect);

    //背景を透過して文字列を描画
    LCanvas.Brush.Color := LBackground;
    Rect.Left := LRect.Right + 5;
    Flags := DT_SINGLELINE or DT_VCENTER or DT_NOPREFIX;
    DrawText(LCanvas.Handle, LItem, Length(LItem), Rect, Flags);
  end;
end;

//=============================================================================
//  ドロップダウンのリストをクリックすると発生するイベント
//  TplDropStringGridで追加したイベント
//
//  このイベントはドロップダウンのウィンドウの以下の操作で発生する
//  (1) [Enter]キーで閉じた
//  (2) マウスクリックで閉じた
//  (3) 矢印キーで選択項目を変更した
//=============================================================================
procedure TForm1.plDropStringGrid1DropListClick(Control: TWinControl; ACol,
  ARow: Integer; ARect: TRect; Index: Integer; ListItem: string;
  InpEdit: TplInplaceEdit);
begin
  if ACol = 1 then begin
    //インプレイスエディタ上のTImageの位置とサイズを設定して塗り潰す
    DrawColorRectangle(plDropStringGrid1, ARect, ListItem);
    InpEdit.Text := ListItem;
  end;
end;

//=============================================================================
//  インプレイスエディタがセルの値を編集すると発生するイベント
//  編集が終了すると,その時の値がTStringGridのセルにセットされる
//
//  このイベントコードがあると,ドロップリストの項目選択だけでなく
//  セルに色定数文字列を入力してセットしても,左端の矩形の色がその色になる
//  セル内のダブルクリックでも値を変更した時にも対応可能となる
//=============================================================================
procedure TForm1.plDropStringGrid1SetEditText(Sender: TObject; ACol,
  ARow: Integer; const Value: string);
var
  LGrid   : TplDropStringGrid;
  InpEdit : TplInplaceEdit;
begin
  LGrid   := TplDropStringGrid(Sender);
  InpEdit := LGrid.InplaceEditor;

  if ACol = 1 then begin
    if (InpEdit <> nil) and (InpEdit.Visible) then begin
      //セルに入力した色定数文字列に該当する色で,左端に矩形を描画して塗り潰す
      DrawColorRectangle(LGrid, LGrid.CellRect(ACol, ARow), Value);
    end;
  end;
end;

//=============================================================================
//  TplDropStringGridの追加したOnInplaceEditFocusedイベント
//  インプレイスエディタが入力フォーカスを受け取った時のイベント
//  縦方向のアライメントと左右のインデントはここで設定する
//=============================================================================
procedure TForm1.plDropStringGrid1InplaceEditFocused(Sender: TObject; ACol,
  ARow: Integer; ARect: TRect; InpEdit: TplInplaceEdit);
var
  LGrid  : TplDropStringGrid;
  LBmp   : TBitmap;
  LImage : TImage;
begin
  LGrid := TplDropStringGrid(Sender);
  //全て上下中央寄せ表示とする
  LGrid.EditVertAlignment := vaCenter;
  InpEdit.AutoSelect := False;
  InpEdit.SelStart   := 0;

  if ACol = 1 then begin
    //インプレイスエディタ上のTImageの位置とサイズを設定して塗り潰す
    LGrid.EditLeftIndent      := DrawColorRectangle(LGrid, ARect, InpEdit.Text);
    LGrid.InplaceImage.Cursor := InpEdit.Cursor;
    LGrid.EditRightIndent     := LGrid.EditDefaultRightIndent;
  end else
  if ACol = 2 then begin
    //インプレイスエディタ上のTImageの位置とサイズを指定して画像を表示
    LImage := LGrid.InplaceImage;
    LBmp   := TBitmap.Create;
    try
      LBmp.LoadFromFile('100.bmp');
      LImage.Width  := LBmp.Width;
      LImage.Height := LBmp.Height;
      LImage.SetBounds(ARect.Right - ARect.Left - LImage.Width - 3,
                       ((ARect.Bottom - ARect.Top) - LImage.Height) div 2,
                       LImage.Width,
                       LImage.Height);
      LImage.Picture.Assign(LBmp);
    finally
      FreeAndNil(LBmp);
    end;
    LGrid.InplaceImage.Visible := True;
    LGrid.InplaceImage.Cursor  := crArrow;
    LGrid.EditLeftIndent       := 4;
    LGrid.EditRightIndent      := LImage.Width + 7;
  end else begin
    //その他のセルはデフォルト値にしておく
    //しておかないと,最後の設定が有効となる
    LGrid.InplaceImage.Visible := False;
    LGrid.EditLeftIndent  := LGrid.EditDefaultLeftIndent;
    LGrid.EditRightIndent := LGrid.EditDefaultRightIndent;
  end;
end;

//=============================================================================
//  TplDropStringGridのOnInplaceImageClickイベント
//  インプレイスエディタ上に配置したTImageをクリックした時のイベント
//=============================================================================
procedure TForm1.plDropStringGrid1InplaceImageClick(Sender: TObject; ACol,
  ARow: Integer; ARect: TRect);
var
  LGrid   : TplDropStringGrid;
  FilePath : String;
begin
  if ACol = 2 then begin
    LGrid   := TplDropStringGrid(Sender);
    FilePath := ExtractFilePath(Application.ExeName) + LGrid.Cells[ACol, ARow];
    ShellExecute(Handle, '', 'Notepad.exe', PChar(FilePath), nil, SW_SHOW);
  end;
end;

//-----------------------------------------------------------------------------
//  セルの左端に矩形を描き,引数で指定する色で塗り潰す
//  通常のセルとインプレイスエディタのセルの共有ルーチン
//  インプレイスエディタの場合は,インプレイスエディタ上のTImageを塗り潰す
//  通常のセルの場合は,TStringGridのCanvasに対してOnDrawCellイベント内から実行
//
//  戻り値は矩形の右端の少し右側の座標値.その位置から文字列を描画する
//  OnDrawCellイベントで右寄せや左寄せで描画した場合と,本コンポで設定した場合
//  とでは少し違いがある
//-----------------------------------------------------------------------------
function TForm1.DrawColorRectangle(AGrid: TplDropStringGrid; ARect: TRect;
  StrColor: String): Integer;
var
  LRect      : TRect;
  LImage     : TImage;
  LCanvas    : TCanvas;
  LSize      : Integer;
  LeftMargin : Integer;
begin
  LSize      := 16;
  LRect      := ARect;
  LeftMargin := 3;
  //描画の矩形領域を設定
  SetRect(LRect, LeftMargin,
                 ((LRect.Bottom - LRect.Top) - LSize) div 2,
                 LRect.Left + LSize + LeftMargin,
                 LRect.Top  + LSize);

  if (AGrid.InplaceEditor <> nil) and (AGrid.InplaceEditor.Visible) then begin
    //インプレイスエディタ上の塗り潰し矩形領域の計算
    LImage := AGrid.InplaceImage;
    LImage.SetBounds(LRect.Left + 3, LRect.Top, LSize, LSize);
    LCanvas := LImage.Canvas;
    LRect   := LImage.ClientRect;
    LImage.Visible := True;
    Result := LRect.Right + LeftMargin + 2 + AGrid.EditDefaultLeftIndent * 2;
  end else begin
    //グリッドのセルの塗り潰し矩形領域の計算
    LRect := Rect(ARect.Left + LRect.Left + LeftMargin,
                  ARect.Top  + LRect.Top,
                  ARect.Left + LRect.Left + LSize + LeftMargin,
                  ARect.Top  + LRect.Top  + LSize);
    LCanvas := AGrid.Canvas;
    Result  := LRect.Right + LeftMargin + 2;
  end;

  //塗り潰しの色を,色定数の文字列から取得
  //文字列にない色の場合は例外が発生する
  try
    LCanvas.Brush.Color := StringToColor(StrColor);
  except
    LCanvas.Brush.Color := AGrid.Color;
  end;

  //矩形の塗り潰しと枠線の描画
  LCanvas.Brush.Style := bsSolid;
  LCanvas.FillRect(LRect);
  LCanvas.Brush.Color := clGray;
  LCanvas.FrameRect(LRect);
end;

//-----------------------------------------------------------------------------
//  GetColorValuesのコールバック関数
//  ドロップダウンのリストに色定数の文字列を追加
//  Delphi専用のコールバックなのでクラスのメンバにできる
//  関数内関数にはできない
//-----------------------------------------------------------------------------
procedure TForm1.GetColorStrProc(const S: string);
begin
  PickList1.Add(S);
end;

end.




08_EditorMode プロパティによる編集モードの切り換え


本コンポーネントで実装したプロパティではありませんが,EditorMode プロパティの動作確認用のテストプログラムです.EditorMode は,編集モードを切り換えます.インプレスエディタの表示と非表示を切り換えます.本サンプルは,以下のような仕様となっています.
  • フォームを表示すると,特定のセルを編集モードにする
  • 左側の列のセルをクリックする編集モードになり,直ちに入力が開始できる
  • 右側の列のセルをクリックした時は,編集終了モードにする
つまり,右側の列のセルは,クリックしても,ダブルクリックしても,編集を開始できません.編集を開始するには,[F2] か [Enter] を使用します.
編集状態にある時に EditorMode := False を実行すると, セルの編集終了イベントが発生します.


図16
起動直後の画面
  • 特定のセルを編集開始状態にする
図17
左側の列のセル
  • クリックすると直ちに編集可能状態になる
図18
右側の列のセル
  • クリックしても,ダブルクリックしても編集を開始できない
  • この時,セルの編集終了イベントは発生する

リスト8
EditorMode プロパティによる編集モードの切り換えのテスト
unit Unit1;

interface

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

type
  TForm1 = class(TForm)
    Panel1: TPanel;
    plDropStringGrid1: TplDropStringGrid;
    procedure FormCreate(Sender: TObject);
    procedure plDropStringGrid1GetEditStyle(TSender: TObject; ACol,
      ARow: Integer; var EditStyle: TEditStyle);
    procedure FormDestroy(Sender: TObject);
    procedure plDropStringGrid1GetPickListItems(ACol, ARow: Integer;
      Items: TStrings);
    procedure plDropStringGrid1Click(Sender: TObject);
    procedure FormShow(Sender: TObject);
    procedure plDropStringGrid1GetEditText(Sender: TObject; ACol, ARow: Integer;
      var Value: string);
  private
    { Private 宣言 }
    PickList1 : TStringList;
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

//=============================================================================
//  テスト用に列数と列幅等を設定し,テスト用のデータを各セルにセット
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
  plDropStringGrid1.DefaultDrawing := True;
  //編集可能にしないとインプレイスエディタは起動しない
  plDropStringGrid1.Options := plDropStringGrid1.Options + [goEditing];

  plDropStringGrid1.FixedCols       := 1;
  plDropStringGrid1.FixedRows       := 1;
  plDropStringGrid1.ColCount        := 3;
  plDropStringGrid1.RowCount        := 4;
  plDropStringGrid1.DefaultColWidth := 120;

  //左端の列幅設定
  plDropStringGrid1.ColWidths[0]  := 10;

  //テスト用の値をセット(固定行)
  plDropStringGrid1.Cells[1,0] := '都道府県';
  plDropStringGrid1.Cells[2,0] := '調査結果';

  //ドロップ表示する時のリストを作成
  PickList1 := TStringList.Create;
  PickList1.BeginUpdate;
  with PickList1 do begin
    Add('東京都');
    Add('埼玉県');
    Add('神奈川県');
    Add('群馬県');
    Add('栃木県');
    Add('茨城県');
    Add('千葉県');
    Add('長野県');
    Add('山梨県');
    Add('岐阜県');
    Add('新潟県');
    Add('福島県');
    Add('山形県');
  end;
  PickList1.EndUpdate;

  //各種プロパティの設定(TplDropStringGridで追加したもの)
  plDropStringGrid1.InplaceButtonWidth := 17;
  plDropStringGrid1.DropListRowCount   := 10;
  plDropStringGrid1.DropListItemHeight := 18;
  plDropStringGrid1.DropListColor      := clWindow;
end;

//=============================================================================
//  フォーム表示開始時の処理
//  指定のセルを編集状態にする
//=============================================================================
procedure TForm1.FormShow(Sender: TObject);
begin
  plDropStringGrid1.Col := 2;
  plDropStringGrid1.Row := 2;
  plDropStringGrid1.SetFocus;
  plDropStringGrid1.EditorMode := True;
end;

//=============================================================================
//  フォーム破棄時の処理
//=============================================================================
procedure TForm1.FormDestroy(Sender: TObject);
begin
  FreeAndNil(PickList1);
end;

//=============================================================================
//  TplDropStringGridコンポのOnGetEditStyleイベント処理
//  編集時にボタンを表示するセルを指定する
//=============================================================================
procedure TForm1.plDropStringGrid1GetEditStyle(TSender: TObject; ACol,
  ARow: Integer; var EditStyle: TEditStyle);
begin
  if ACol = 1 then EditStyle := esPickList;
end;

//=============================================================================
//  インプレイスエディタがセルのテキストを取得した時発生するイベント
//  インプレスエディタ関係では一番早く発生するイベント
//
//  背景やフォント,テキストの左右のアライメントは,このイベント内で設定する
//=============================================================================
procedure TForm1.plDropStringGrid1GetEditText(Sender: TObject; ACol,
  ARow: Integer; var Value: string);
var
  LGrid   : TplDropStringGrid;
  InpEdit : TplInplaceEdit;
begin
  LGrid   := TplDropStringGrid(Sender);
  InpEdit := LGrid.InplaceEditor;

  //このイベント内では,インプレイスエディタはまだ未表示状態
  if (InpEdit <> nil) then begin
    //描画に関係する設定
    //背景色やフォントの設定はこのイベントで実行した方がいいようだ
    InpEdit.Color       := $00F4ECEC;
    InpEdit.BorderStyle := bsSingle;
    InpEdit.Font.Size   := TplDropStringGrid(Sender).Font.Size;
  end;
end;

//=============================================================================
//  TplDropStringGridコンポのOnGetPickListItemsイベント処理
//  ドロップダウンで表示するリストの項目の設定
//=============================================================================
procedure TForm1.plDropStringGrid1GetPickListItems(ACol, ARow: Integer;
  Items: TStrings);
begin
  if ACol = 1 then Items.Assign(PickList1);
end;

//=============================================================================
//  TStringGridコンポのOnClickイベント処理
//  セルをクリックすると発生するイベント
//=============================================================================
procedure TForm1.plDropStringGrid1Click(Sender: TObject);
var
  LGrid : TplDropStringGrid;
  LCol  : Integer;
begin
  LGrid := TplDropStringGrid(Sender);
  LCol  := LGrid.Col;
  if LCol = 1 then begin
    //セルをクリックするとただちに編集状態になる
    LGrid.EditorMode := True;
  end else begin
    //セルをクリックしても編集状態にはならない
    //[F2]か[Enter]であれば編集状態にできる
    LGrid.EditorMode := False;
  end;
end;

end.


ドロップダウンのリストには,TplInplaceEditList でキャストすればアクセスできます.
下のコードは,ドロップダウンを表示するように設定してあるセルに移動すると,自動的に編集モードにして,ドロップダウンのリストを表示します.


図19
ドロップダウンのリストを自動で表示
  • セルが編集モードになると自動でドロップダウンのリストを表示する


//=============================================================================
//  TStringGridコンポのOnClickイベント処理
//  セルをクリックすると発生するイベント
//=============================================================================
procedure TForm1.plDropStringGrid1Click(Sender: TObject);
var
  LGrid : TplDropStringGrid;
  LCol  : Integer;
  LList : TplInplaceEditList;
begin
  LGrid := TplDropStringGrid(Sender);
  LCol  := LGrid.Col;

  if LCol = 1 then begin
    //セルをクリックするとただちに編集状態にしてドロップダウンリストを表示
    LGrid.EditorMode := True;
    LList := TplInplaceEditList(LGrid.InplaceEditor);
    LList.DropDown;
  end;
end;




09_コンポーネントのソースコード


下の表は,標準の TStringGrid に追加したプロパティの一覧です.
インプレイスエディタの上下方向 (垂直方向) のアライメントと,インデントの設定は,下記のリンクのコンポーネントと実装方法は同じです.ただし,TStringGrid のインプレイスエディタは,元々マルチライン入力にしたエディットコントロールです.[Enter] では改行できませんが,[Alt] + [Enter] であれば改行できます.セルに値をセットする際,改行は無効となります.



凡例  published
 public
 ReadOnly

 InplaceEditor TplInplaceEdit インプレスエディタのオブジェクト
文字列の横方向位置,フォント,背景色,文字列の選択状態,キャレットの位置制御等が可能
 InplaceImage TImage インプレイスエディタ上に配置した TImage
インプレイスエディタに画像を表示できる
DropListRowCount Integer ドロップダウンリストの表示項目数
 DropListWidth Integer ドロップダウンリストのウィンドウの幅
 DropListItemHeight Integer ドロップダウンリストの各項目の高さ
 DropListColor TColor ドロップダウンリストのウィンドウの背景色
 InplaceButtonWidth Integer インプレイスボタンの幅
 EditLeftIndent Integer インプレイスエディタの左側のインデント
設定は OnInplaceEditFocused イベント内で
 EditRightIndent Integer インプレイスエディタの右側のインデント
設定は OnInplaceEditFocused イベント内で
 EditVertAlignment TplgVertAlignment インプレイスエディタの文字列の縦方向位置
設定は OnInplaceEditFocused イベント内で
 EditDefaultLeftIndent Integer 上記の左側のインデントのデフォルト値
設定は OnInplaceEditFocused イベント内で
EditDefaultRightIndent Integer 上記の右側のインデントのデフォルト値
設定は OnInplaceEditFocused イベント内で
 EditDefaultVertAlignment TplgVertAlignment 上記の縦方向アライメントのデフォルト値
設定は OnInplaceEditFocused イベント内で


標準の TStringGrid に,以下のイベントを追加しています.

OnGetEditStyle, OnGetPickListItems, OnEditButtonClick は元々 TStringGrid ではメソッドとして実装されている機能ですが,published のメンバとして使用可能にしています.
OnInplaceCanEditShow も内部では CanEditShow メソッドして実装されています.それ以外は,本コンポーネントで独自に追加したイベントです.

TValueListEditor コンポーネントで OnGetEditStyle に相当するのは,TItemProp.EditStyle プロパティです.この EditStyle の値で,どのセルにどのボタンを表示するかを指定します. OnGetPickListItems に相当するのは GetPickList です.OnEditButtonClick は,TValueListEditor にも標準で実装されています.



イベント名 機能
OnGetEditStyle セルの編集開始時に表示するボタンの指定
このイベントを作成しなければ全て通常のセルとなる
OnGetPickListItems ドロップダウンで表示するリストの項目を設定する
OnEditButtonClick 参照 (省略 [...]) ボタンをクリックした時のイベント
OnDropDownList ドロップダウンボタンをクリックした時のイベント
引数の DefaultAction を False にするとリストを表示しない
別の選択用のウィンドウを表示する等の用途に利用可能
OnDropListDrawItem ドロップダウンのリストの描画用イベント
TListBox の DrawItem と機能は同じ
このイベントを作成すると,デフォルトの描画は行われない
OnDropListClick 以下の場合に発生するイベント
リストを [Enter] キーで終了した時
リストをマウスクリックで終了した時
矢印キーで選択項目を変更した時
OnInplaceEditFocused インプレイスエディタに入力フォーカスが移動した時のイベント
縦方向のアライメントと左右のインデントは,このイベント内で設定する
OnInplaceCanEditShow セルの編集が開始された時に発生するイベント
OnInplaceEditClose セルの編集終了検出用のイベント.以下の場合に発生する
セルの編集を [Enter] キーで終了した
セルの編集中にマウスクリック等で,他のセルに移動した
OnInplaceImageClick インプレイスエディタ上の TImage をクリックした時のイベント


リスト9
作成した TplDropStringGrid コンポーネントのソースコード
unit plDropStringGrid;

interface

uses
  Windows, Messages, SysUtils, Classes, Controls, Grids, StdCtrls, Graphics,
  ExtCtrls, forms, Dialogs;

type
  TplDropStringGrid = class;
  TplgVertAlignment = (vaTop, vaCenter, vaBottom);

  //---------------------------------------------------------------------------
  //  TInplaceEditの継承クラス
  //  いくつかのprotectedプロパティを使用可能にした
  //  TInplaceEditに元々ないプロパティにアクセスするとボタンが表示されない
  //  サードパーティ製は,プロパティ設定用のクラスを使用しているようだ
  //---------------------------------------------------------------------------
  TplInplaceEdit = class(TInplaceEdit)
  private
    FGrid : TplDropStringGrid;
  public
    property Alignment;
    property Font;
    property AutoSelect;
    property BorderStyle;
    property BevelEdges;
    property BevelWidth;
    property BevelOuter;
    property BevelKind;
    property BevelInner;
    property Color;
    property ImeMode;
    property ReadOnly;
  end;

  //各種イベントの型の定義
  TGetEditStyleEvent = procedure(Sender: TObject; ACol, ARow: Integer;
    var EditStyle: TEditStyle) of Object;
  TOnGridEditButtonClickEvent = procedure(Sender: TObject;
    ACol, ARow: Integer; ARect: TRect) of Object;
  TOnGridDropDownEvent = procedure(Sender: TObject;
    ACol, ARow: Integer; ARect: TRect; var DefaultAction: Boolean) of Object;
  TOnDropListClickEvent = procedure(Control: TWinControl;
    ACol, ARow: Integer; ARect: TRect; Index: Integer; ListItem: String;
    InpEdit: TplInplaceEdit) of Object;
  TOnInplaceEditCloseEvent = procedure(Sender: TObject;
    ACol, ARow: Integer; Value: String; var CanNextCell: Boolean) of Object;
  TOnInplaceCanEditShowEvent = procedure(Sender: TObject;
    ACol, ARow: Integer; ARect: TRect; InpEdit: TplInplaceEdit) of Object;
  TOnInplaceEditFocusedEvent = procedure(Sender: TObject;
    ACol, ARow: Integer; ARect: TRect; InpEdit: TplInplaceEdit) of Object;

  //---------------------------------------------------------------------------
  //  TInplaceEditListをカスタマイズするための継承クラス
  //---------------------------------------------------------------------------
  TplInplaceEditList  = class(TInplaceEditList)
  private
    FGrid : TplDropStringGrid;
    procedure PickListDrawItemEvent(Control: TWinControl; Index: Integer;
      Rect: TRect; State: TOwnerDrawState);
    procedure PickListClickEvent(Sender: TObject);
  protected
    procedure CreateWnd; override;
    procedure WMMouseWheel(var Message: TWMMouseWheel); message WM_MOUSEWHEEL;
  public
    constructor Create(AOwner: TComponent); override;
    procedure DropDown; override;
  end;

  //---------------------------------------------------------------------------
  //  PickListと参照(省略記号)ボタンクリックを実装したTStringGrid
  //---------------------------------------------------------------------------
  TplDropStringGrid = class(TStringGrid)
  private
    FDropListRowCount     : Integer;
    FInplaceButtonWidth   : integer;
    FDropListWidth        : Integer;
    FDropListColor        : TColor;
    FDropItemHeight       : Integer;
    FOnEditButtonClick    : TOnGridEditButtonClickEvent;
    FOnGetEditStyle       : TGetEditStyleEvent;
    FOnGetPickListItems   : TOnGetPickListItems;
    FOnDropDownList       : TOnGridDropDownEvent;
    FOnDropListDrawItem   : TDrawItemEvent;
    FOnDropListClick      : TOnDropListClickEvent;
    FOnInplaceEditClose   : TOnInplaceEditCloseEvent;
    FOnInplaceImageClick  : TOnGridEditButtonClickEvent;
    FOnInplaceCanEditShow : TOnInplaceCanEditShowEvent;
    FOnInpaceEditFocused  : TOnInplaceEditFocusedEvent;

    FInpEdit      : TplInplaceEdit;
    FInplaceImage : TImage;

    FEditLeftIndent    : Integer;
    FEditRightIndent   : Integer;
    FEditVertAlignment : TplgVertAlignment;
    FEditTopIndent     : Integer;
    FEditBottomIndent  : Integer;
    FEditTextHeight    : Integer;
    FEditDefaultLeftIndent    : Integer;
    FEditDefaultRightIndent   : Integer;
    FEditDefaultTopIndent     : Integer;
    FEditDefaultBottomIndent  : Integer;
    FEditDefaultVertAlignment : TplgVertAlignment;

    FEditCol     : Integer;    //Grid.pasではInplaceColに相当
    FEditRow     : Integer;    //Grid.pasではInplaceRowに相当
    FCanCellChange     : Boolean;
    FIsParentFormClose : Boolean;
    FParentFormWndProc : TWndMethod;

    procedure SetDropListRowCount(Value: Integer);
    procedure SetOnEditButtonClick(Value: TOnGridEditButtonClickEvent);
    procedure SetOnGetPicklistItems(Value: TOnGetPickListItems);
    procedure SetInplaceButtonWidth(const Value: Integer);
    procedure ButtonClick(Sender: TObject);
    procedure ImageButtonClick(Sender: TObject);
    procedure SetOnDropListDrawItem(const Value: TDrawItemEvent);
    procedure SetOnDropListClick(const Value: TOnDropListClickEvent);
    procedure SubClassFormProc(var Message: TMessage);
    procedure SetIndentAndVertAlignment;
  protected
    function  CreateEditor: TInplaceEdit; override;
    function  GetEditStyle(ACol, ARow: Integer): TEditStyle; override;
    procedure SetParent(AParent: TWinControl); override;
    procedure WMCommand(var Message: TWMCommand); message WM_COMMAND;
    procedure WMLButtonUp(var Message: TWMLButtonUp); message WM_LBUTTONUP;
    function CanEditShow: Boolean; override;
    function SelectCell(ACol: Integer; ARow: Integer): Boolean; override;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;

    property InplaceEditor      : TplInplaceEdit read FInpEdit;
    property InplaceImage       : TImage  read FInplaceImage   write FInplaceImage;
    property DropListWidth      : Integer read FDropListWidth  write FDropListWidth;
    property DropListColor      : TColor  read FDropListColor  write FDropListColor;
    property DropListItemHeight : Integer read FDropItemHeight write FDropItemHeight;
    property InplaceButtonWidth : Integer read FInplaceButtonWidth
                                          write SetInplaceButtonWidth;
    //元のTInplaceEditにないプロパティ
    //インプレイスエディタの上下のアライメントと左右のインデント
    property EditLeftIndent : Integer
      read FEditLeftIndent  write FEditLeftIndent;
    property EditRightIndent : Integer
      read FEditRightIndent write FEditRightIndent;
    property EditVertAlignment : TplgVertAlignment
      read FEditVertAlignment  write FEditVertAlignment;
    property EditDefaultLeftIndent    : Integer        read FEditDefaultLeftIndent;
    property EditDefaultRightIndent   : Integer        read FEditDefaultRightIndent;
    property EditDefaultVertAlignment : TplgVertAlignment read FEditDefaultVertAlignment;
  published
    property DropListRowCount : Integer
      read FDropListRowCount write SetDropListRowCount;
    property OnEditButtonClick: TOnGridEditButtonClickEvent
      read FOnEditButtonClick write SetOnEditButtonClick;
    property OnGetEditStyle : TGetEditStyleEvent
      read FOnGetEditStyle write FOnGetEditStyle;
    property OnGetPickListItems : TOnGetPickListItems
      read FOnGetPickListItems write SetOnGetPickListItems;
    property OnDropDownList: TOnGridDropDownEvent
      read FOnDropDownList write FOnDropDownList;
    property OnDropListDrawItem: TDrawItemEvent
      read FOnDropListDrawItem write SetOnDropListDrawItem;
    property OnDropListClick : TOnDropListClickEvent
      read FOnDropListClick write SetOnDropListClick;
    property OnInplaceEditClose : TOnInplaceEditCloseEvent
      read FOnInplaceEditClose write FOnInplaceEditClose;
    property OnInplaceImageClick : TOnGridEditButtonClickEvent
      read FOnInplaceImageClick write FOnInplaceImageClick;
    property OnInplaceCanEditShow : TOnInplaceCanEditShowEvent
      read FOnInplaceCanEditShow write FOnInplaceCanEditShow;
    property OnInplaceEditFocused : TOnInplaceEditFocusedEvent
      read FOnInpaceEditFocused write FOnInpaceEditFocused;
  end;

procedure Register;

implementation

uses imm;

{ TplInplaceEditList }

//-----------------------------------------------------------------------------
//  Createメソッド
//-----------------------------------------------------------------------------
constructor TplDropStringGrid.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);

  FDropListRowCount   := 8;
  FInplaceButtonWidth := 17;
  FDropListWidth      := -1;
  FDropListColor      := clWindow;
  FEditDefaultLeftIndent    := 2;
  FEditDefaultRightIndent   := 2;
  FEditDefaultTopIndent     := 2;
  FEditDefaultBottomIndent  := 2;
  FEditDefaultVertAlignment := vaTop;

  FEditLeftIndent   := FEditDefaultLeftIndent;
  FEditRightIndent  := FEditDefaultRightIndent;
  FEditTopIndent    := FEditDefaultTopIndent;
  FEditBottomIndent := FEditDefaultBottomIndent;

  FEditCol := -1;
  FEditRow := -1;
  FCanCellChange     := True;
  FIsParentFormClose := False;
end;

//-----------------------------------------------------------------------------
//  SetParentメソッド
//-----------------------------------------------------------------------------
procedure TplDropStringGrid.SetParent(AParent: TWinControl);
var
  LParentForm : TCustomForm;
begin
  inherited;
  if AParent = nil then exit;
  FDropItemHeight := Canvas.TextHeight('gh');

  if not Assigned(FParentFormWndProc) then begin
    LParentForm            := GetParentForm(Self);
    FParentFormWndProc     := LParentForm.WindowProc;
    LParentForm.WindowProc := SubClassFormProc;
  end;
end;

//-----------------------------------------------------------------------------

//  Destoryメソッド
//  LParentForm(親フォーム)はローカルで検出して使用する
//  グローバルにしてしまうとAssignedでの検出ができない場合がある
//-----------------------------------------------------------------------------
destructor TplDropStringGrid.Destroy;
var
  LParentForm : TCustomForm;
begin
  //元に戻しておかないと親フォームの動作がおかしくなる
  LParentForm := GetParentForm(Self);
  if Assigned(LParentForm) then begin
    LParentForm.WindowProc := FParentFormWndProc;
  end;
  inherited;
end;

//-----------------------------------------------------------------------------
//  親フォームのサブクラス化
//  本コンポを配置しているフォームを閉じる時の処理用
//  親フォームが閉じる時は
//  親のOnCloseQueryイベントが設定されていればそれに従う
//  親のOnCloseQueryイベントがなければセル編集終了のイベントは実行しない
//-----------------------------------------------------------------------------
type
  TplForm = class(TCustomForm);

procedure TplDropStringGrid.SubClassFormProc(var Message: TMessage);
var
  LParentForm : TCustomForm;
begin
  LParentForm := GetParentForm(Self);

  if (Message.Msg = WM_CLOSE) then begin
    if Assigned(TplForm(LParentForm).OnCloseQuery) then begin
      FIsParentFormClose := False;
      EditorMode := False;
      EditorMode := True;
      if LParentForm.CloseQuery then begin
        FIsParentFormClose := True;
      end else begin
        FIsParentFormClose := False;
        exit;
      end;
    end;
  end;

  //元のウィンドウプロシージャーを実行
  FParentFormWndProc(Message);
  if (Message.Msg = WM_CLOSE) then begin
    if not Assigned(TplForm(LParentForm).OnCloseQuery) then begin
      FIsParentFormClose := True;
    end;
  end;
end;

//-----------------------------------------------------------------------------
//  TplStringGridのCreateEditorメソッド処理
//  インプレイスエディタが生成される時に自動的に実行されるメソッド
//  ここでTplInplaceEditListのスンスタンスを生成する
//
//  インプレイスエディタは,初めてセルを編集する時に生成される
//  その時にこのメソッドが実行される
//  それ以後は表示・非表示の状態の切り換えとなる
//  したがって,TplInplaceEditList.CreateWndも最初に実行されるだけ
//-----------------------------------------------------------------------------
function TplDropStringGrid.CreateEditor: TInplaceEdit;
begin
  //TInplaceEditにアクセスできるようにFInplaceEditorを設定
  Result := TInplaceEdit(TplInplaceEditList.Create(Self));
  FInpEdit := TplInplaceEdit(Result);
  FInpEdit.FGrid := Self;

  with TplInplaceEditList(Result) do begin
    if FInplaceButtonWidth > 0 then ButtonWidth  := FInplaceButtonWidth;
    if FDropListRowCount > 0   then DropDownRows := FDropListRowCount;
    OnGetPickListItems   := FOnGetPickListItems;
    OnEditButtonClick    := ButtonClick;
    //編集時のセルの背景色をドロップダウンリストの背景色と同じにしたい場合
    //Color := FDropListColor;
  end;
end;

//-----------------------------------------------------------------------------
//  TplStringGirdのGetEditStyleメソッド処理
//  ボタンのスタイルを決定する際に,自動的に実行されるメソッド
//  ここでボタンのスタイルを設定する
//-----------------------------------------------------------------------------
function TplDropStringGrid.GetEditStyle(ACol, ARow: Integer) : TEditStyle;
begin
  //デフォルトではインプレイスエディタが起動してもボタンを表示させない
  Result := esSimple;
  if Assigned(FOnGetEditStyle) then begin
    FOnGetEditStyle(Self, ACol, ARow, Result);
  end;
end;

//-----------------------------------------------------------------------------
//  DropList(TListBox)でドロップダウン表示する項目数の設定用
//-----------------------------------------------------------------------------
procedure TplDropStringGrid.SetDropListRowCount(Value: Integer);
begin
  FDropListRowCount := Value;
  if Assigned(FInpEdit) then begin
    TplInplaceEditList(FInpEdit).DropdownRows := Value;
  end;
end;

//-----------------------------------------------------------------------------
//  表示するボタンの幅の設定用
//-----------------------------------------------------------------------------
procedure TplDropStringGrid.SetInplaceButtonWidth(const Value: Integer);
begin
  FInplaceButtonWidth := Value;
  if Assigned(FInpEdit) then begin
    TplInplaceEditList(FInpEdit).ButtonWidth := Value;
  end;
end;

//-----------------------------------------------------------------------------
//  編集(省略記号)ボタンをクリックした時のイベント設定用
//  列と行の値を引数にするために別のイベントを設定して処理
//-----------------------------------------------------------------------------
procedure TplDropStringGrid.SetOnEditButtonClick(Value: TOnGridEditButtonClickEvent);
begin
  FOnEditButtonClick := Value;
end;

//-----------------------------------------------------------------------------
//  編集(省略記号)ボタンをクリックした時のイベント処理用
//  このイベント内でダイアログ等を表示する
//-----------------------------------------------------------------------------
procedure TplDropStringGrid.ButtonClick(Sender: TObject);
begin
  if Assigned(FInpEdit) then begin
    if Assigned(FOnEditButtonClick) then begin
      FOnEditButtonClick(Self, Col, Row, CellRect(Col, Row));
    end;
  end;
end;

//-----------------------------------------------------------------------------
//  DropList(PickList)のItemを設定するためのイベント設定用
//  このイベント内でドロップダウンで表示するリストをセットする
//  Items.Add あるいは Items.Assign()等でセットする
//-----------------------------------------------------------------------------
procedure TplDropStringGrid.SetOnGetPicklistItems(Value: TOnGetPicklistItems);
begin
  FOnGetPicklistItems := Value;
  if Assigned(FInpEdit) then begin
    TplInplaceEditList(FInpEdit).OnGetPickListitems := Value;
  end;
end;

//-----------------------------------------------------------------------------
//  DropList(PickList)のItemのカスタム描画用イベントの設定用
//  イベントハンドラをnilにした時の対応策
//  元のTplInplaceEditListのイベントハンドをnilにしない
//-----------------------------------------------------------------------------
procedure TplDropStringGrid.SetOnDropListDrawItem(const Value: TDrawItemEvent);
var
  LInpList : TplInplaceEditList;
begin
  FOnDropListDrawItem := Value;
  if Assigned(FInpEdit) then begin
    LInpList := TplInplaceEditList(FInpEdit);
    TListBox(LInpList.PickList).OnDrawItem := Value;
  end else begin
    FOnDropListDrawItem := Value;
  end;
end;

//-----------------------------------------------------------------------------
//  DropList(PickList)のItemをクリックした時のイベントの設定用
//  イベントハンドラをnilにした時の対応策
//  元のTplInplaceEditListのイベントハンドをnilにしない
//-----------------------------------------------------------------------------
procedure TplDropStringGrid.SetOnDropListClick(
  const Value: TOnDropListClickEvent);
var
  LInpList : TplInplaceEditList;
begin
  FOnDropListClick := Value;
  if Assigned(FInpEdit) then begin
    LInpList := TplInplaceEditList(FInpEdit);
    if Assigned(FOnDropListClick) then begin
      LInpList.OnClick := LInpList.PickListClickEvent;
    end else begin
      LInpList.OnClick := nil;
    end;
  end else begin
    FOnDropListClick := Value;
  end;
end;

//-----------------------------------------------------------------------------
//  インプレイスエディタ上に載せているTImageをクリックした時のイベント処理用
//-----------------------------------------------------------------------------
procedure TplDropStringGrid.ImageButtonClick(Sender: TObject);
begin
  if FInplaceImage.Visible = False then exit;
  if Assigned(FInpEdit) then begin
    if Assigned(FOnInplaceImageClick) then begin
      FOnInplaceImageClick(Self, Col, Row, CellRect(Col, Row));
      FInpEdit.SetFocus;
    end;
  end;
end;

//-----------------------------------------------------------------------------
//  SelectCellメソッド
//  入力セルの編集終了時に次のセルに移動させないようにする処理を追加
//  これは編集終了時用のイベントの引数で指定する
//  FIsParentFormCloseはセル編集終了時のイベント実行可否のフラグとしても利用
//-----------------------------------------------------------------------------
function TplDropStringGrid.SelectCell(ACol, ARow: Integer): Boolean;
begin
  if (FCanCellChange = False) then begin
    FIsParentFormClose := True;
    Result := False;
  end else begin
    Result := inherited SelectCell(ACol, ARow);
  end;
end;

//-----------------------------------------------------------------------------
//  編集セルの上下のアライメントと左右のインデントの設定用
//  セルのサイズとフォントのサイズから計算する
//  インプレイスエディタが生成されていて,フォントの設定されているのが条件
//  この条件に合うのは,OnInpaceEditFocusedイベント
//  OnGetEditTextでは早すぎる
//  OnInplaceCanEditShowでは既に編集状態に入ってしまっている
//-----------------------------------------------------------------------------
procedure TplDropStringGrid.SetIndentAndVertAlignment;
var
  LDevieceC   : HDC;
  LCanvas     : TCanvas;
  LCellRect   : TRect;
  LCellHeight : Integer;
begin
  if (FInpEdit <> nil) and (FInpEdit.Visible) then begin
    LCellRect := CellRect(Col, Row);
    LDevieceC := GetDC(FInpEdit.Handle);
    try
      LCanvas := TControlCanvas.Create;
      try
        LCanvas.Handle := LDevieceC;
        LCanvas.Lock;
        LCanvas.Font.Assign(FInpEdit.Font);
        FEditTextHeight := LCanvas.TextHeight('Hg');
        LCellHeight     := LCellRect.Bottom - LCellRect.Top;
        if FEditVertAlignment = vaCenter then begin
          FEditTopIndent := (LCellHeight - FEditTextHeight) div 2;
        end else
        if FEditVertAlignment = vaBottom then begin
          FEditTopIndent := LCellHeight - FEditTextHeight - FEditBottomIndent;
        end;
      finally
        LCanvas.Unlock;
        FreeAndNil(LCanvas);
      end;
    finally
      ReleaseDC(FInpEdit.Handle, LDevieceC);
    end;
  end;
end;

//-----------------------------------------------------------------------------
//  セル編集の通知メッセージの処理
//  通知メッセージEN_KILLFOCUSは以下の場合に発行される
//  (1) [Enter]キーの押下で編集を終了した場合
//  (2) セルの編集中に別のセルに移動した場合
//
//  EN_SETFOCUS通知メッセージは[Enter]で編集状態にした場合に発行される
//  マウス左ボタン押下でも発行される
//  ここで,上下のアライメントと左右のインデントの設定を行う
//-----------------------------------------------------------------------------
procedure TplDropStringGrid.WMCommand(var Message: TWMCommand);
var
  LInpRect       : TRect;
  LCanCellChange : Boolean;
begin
  if (FInpEdit <> nil) and (Message.Ctl = FInpEdit.Handle) then begin
    case Message.NotifyCode of
      EN_KILLFOCUS :
      begin
        LCanCellChange := True;

        //日本語IMEは閉じる
        SetImeMode(FInpEdit.Handle, imClose);

        //セルの編集終了時のイベント
        if FIsParentFormClose = False then begin
          if Assigned(FOnInplaceEditClose) then begin
            if (FEditCol = Col) and (FEditRow = Row) then begin
              FOnInplaceEditClose(Self, Col, Row, Cells[Col, Row], LCanCellChange);
              FCanCellChange := LCanCellChange;
            end;
          end;
        end;
      end;
      EN_SETFOCUS:
      begin
        //編集中のセルの番号
        FEditCol := Col;
        FEditRow := Row;
        FCanCellChange     := True;
        FIsParentFormClose := False;

        if Assigned(FOnInpaceEditFocused) then begin
          FOnInpaceEditFocused(Self, Col, Row, CellRect(Col, Row), FInpEdit);
        end;
        SetIndentAndVertAlignment;

        //マウスボタンを放すとここでの設定が無効になってしまう
        //そこで,WM_LBUTTONUPで再設定を実行している
        if (FInpEdit <> nil) and (FInpEdit.Visible) then begin
          SendMessage(FInpEdit.Handle, EM_GETRECT, 0, LPARAM(@LInpRect));
          SetRect(LInpRect, FEditLeftIndent + FEditDefaultLeftIndent,
                            FEditTopIndent,
                            LInpRect.Right - FEditRightIndent,
                            FEditTopIndent + FEditTextHeight);
          SendMessage(FInpEdit.Handle, EM_SETRECTNP, 0, LPARAM(@LInpRect));
        end;
      end;
    end;

    //終了時はインプレイスエディタも終了させる
    //この処理をしないとセル編集中の時にアプリが正常終了しない場合がある
    if Message.Msg = WM_DESTROY then begin
      if Assigned(FInpEdit) then begin
        FInpEdit.Destroy;
      end;
    end;
  end;

  inherited;
end;

//-----------------------------------------------------------------------------
//  セル編集開始時の左右のインデントと縦方向のアライメントの設定
//  [Enter]キーでセルが編集モードになっ場合は実行されない
//-----------------------------------------------------------------------------
procedure TplDropStringGrid.WMLButtonUp(var Message: TWMLButtonUp);
var
  LInpRect : TRect;
begin
  inherited;

  if (FInpEdit <> nil) and (FInpEdit.Visible) then begin
    SendMessage(FInpEdit.Handle, EM_GETRECT, 0, LPARAM(@LInpRect));
    SetRect(LInpRect, FEditLeftIndent + FEditDefaultLeftIndent,
                      FEditTopIndent,
                      LInpRect.Right - FEditRightIndent,
                      FEditTopIndent + FEditTextHeight);
    SendMessage(FInpEdit.Handle, EM_SETRECTNP, 0, LPARAM(@LInpRect));
  end;
end;

//-----------------------------------------------------------------------------
//  CanEditShowメソッド処理
//  セルが編集モードになった時に自動的に実行される関数
//  メソッドではなくイベントにした
//-----------------------------------------------------------------------------
function TplDropStringGrid.CanEditShow: Boolean;
begin
  Result := inherited CanEditShow;
  if Result = False then exit;

  if (FInpEdit <> nil) and (FInpEdit.Visible) then begin
    if Assigned(FOnInplaceCanEditShow) then begin
      FOnInplaceCanEditShow(Self, Col, Row, CellRect(Col, Row), FInpEdit);
    end;
  end;
end;

{ TplInplaceEditList }

//-----------------------------------------------------------------------------
//  TplInplaceEditListクラスのCreateメソッド処理
//-----------------------------------------------------------------------------
constructor TplInplaceEditList.Create(AOwner: TComponent);
begin
  inherited;
  FGrid := TplDropStringGrid(AOwner);
end;

//-----------------------------------------------------------------------------
//  TplInplaceEditListクラスのCreateWndメソッド処理
//  PickList(TCustomListBox)のスタイルを変更
//  ドロップダウンリストのクラス名はTPopUpListBox(TCustomListBox)
//
//  ItemHeightの値を変更するには
//  (1) このCreateWndメソッドでStyleをlbOwnerDrawVariableにする
//  (3) DropDownのinheritedの前でItemHeightの値を設定する
//  といいようである
//
//  CreateWndは初めてセルが編集状態になった時だけ実行される
//-----------------------------------------------------------------------------
procedure TplInplaceEditList.CreateWnd;
begin
  inherited;

  //InplaceImage(TImage)の生成
  if not Assigned(FGrid.InplaceImage) then begin
    FGrid.InplaceImage         := TImage.Create(FGrid);
    FGrid.InplaceImage.Parent  := FGrid.InplaceEditor;
    FGrid.InplaceImage.OnClick := FGrid.ImageButtonClick;
    FGrid.InplaceImage.Visible := False;
  end;

  //TPopupListboxの継承元はTCustomListBox
  with TListBox(PickList) do begin
    BevelKind   := bkFlat;
    BevelWidth  := 1;
    BevelInner  := bvNone;
    BevelOuter  := bvRaised;
    Style       := lbOwnerDrawVariable;

    if Assigned(FGrid.FOnDropListDrawItem) then begin
      OnDrawItem := PickListDrawItemEvent;
    end;
    if Assigned(FGrid.FOnDropListClick) then begin
      OnClick := PickListClickEvent;
    end;
  end;
end;

//-----------------------------------------------------------------------------
//  TplInplaceEditListクラスのDropDownメソッド処理
//  ドロップダウン(矢印)ボタンをクリックすると実行されるメソッド
//  PickList(TCustomListBox)の背景色と幅等の変更
//-----------------------------------------------------------------------------
procedure TplInplaceEditList.DropDown;
var
  LCol  : Integer;
  LRow  : Integer;
  LRect : TRect;
  Flag  : Boolean;
begin
  //Flag(DefaultActionをFalseにするとドロップダウンを表示しない
  //別のドロップダウン用のウインドウを使用する場合等に利用可能
  Flag := True;
  LCol := FGrid.Col;
  LRow := FGrid.Row;
  if FGrid.GetEditStyle(LCol, LRow) = esPickList then begin
    if Assigned(FGrid.OnDropDownList) then begin
      FGrid.OnDropDownList(FGrid, LCol, LRow, FGrid.CellRect(LCol, LRow), Flag);
    end;
    if Flag then begin

      TListBox(PickList).BevelKind  := bkFlat;
      //項目の高さの設定
      TListBox(PickList).ItemHeight := FGrid.DropListItemHeight;

      inherited;

      //背景色
      TListBox(PickList).Color := FGrid.DropListColor;
      //幅の設定
      LRect := FGrid.CellRect(LCol, LRow);
      if FGrid.DropListWidth <= 0 then begin
        TListBox(PickList).Width := LRect.Right - LRect.Left + 1;
      end else begin
        TListBox(PickList).Width := FGrid.DropListWidth;
      end;
    end;
  end;
end;

//-----------------------------------------------------------------------------
//  TplInplaceEditListクラスのPickListDrawItemEventメソッド処理
//  TplInplaceEditListで追加したメソッド
//  PickList(TCustomListBox)のOnDrawItemイベントを実行する
//  このイベントを設定するとデフォルトの描画は行われない
//  全て自前でコードを書くことになる
//-----------------------------------------------------------------------------
procedure TplInplaceEditList.PickListDrawItemEvent(Control: TWinControl;
  Index: Integer; Rect: TRect; State: TOwnerDrawState);
begin
  if Assigned(FGrid.FOnDropListDrawItem) then begin
    FGrid.FOnDropListDrawItem(Control, Index, Rect, State);
  end;
end;

//-----------------------------------------------------------------------------
//  TplInplaceEditListクラスのPickListClickメソッド処理
//  OnDropPickListClickイベント処理用
//  PickList(TCustomListBox)のOnClickイベントを実行する
//
//  以下の操作で発生する
//  (1) [Enter]キーで閉じた
//  (2) マウスクリックで閉じた
//  (3) 矢印キーで選択項目を変更した
//-----------------------------------------------------------------------------
procedure TplInplaceEditList.PickListClickEvent(Sender: TObject);
var
  LCol   : Integer;
  LRow   : Integer;
  LIndex : Integer;
  LItem  : String;
begin
  if Assigned(FGrid.FOnDropListClick) then begin
    LCol   := FGrid.Col;
    LRow   := FGrid.Row;
    LIndex := TListBox(PickList).ItemIndex;
    LItem  := TListBox(PickList).Items[LIndex];
    FGrid.FOnDropListClick(TCustomListBox(PickList),
                           LCol, LRow, FGrid.CellRect(LCol, LRow),
                           LIndex, LItem,
                           FGrid.InplaceEditor);
  end;
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;

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

end.