{ これは Delphi Advent Calendar 2013 の 12/17 分の記事です }
前回はプロパティエディタの記事でしたので、今回はコンポーネントエディタの作り方です。
まずおさらいです。
- コンポーネント
- コンポーネントエディタ
- プロパティエディタ
これらのファイル構成と関係は以下の図のようになります。

通常、コンポーネントとコンポーネントエディタ、プロパティエディタは "同じ製作者によるもの" なので、一緒に配布される事が多いのですが、プロパティエディタは先述の通り "後でポン付け" が可能です…しかしながら、コンポーネントエディタの場合には必ずしもポン付けできるとは限りません。
ちょっと例を出してみましょう。
例えば TStringGrid (VCL) ですが、動的にデータを表示させるのではなく、単なる表としてデータを表示させたい事もあると思います。しかしながら、オブジェクトインスペクタで TStringGrid の値を設定する事はできません…なので、TStringGrid 用のコンポーネントエディタを作ってみたいと思います。
では、コンポーネントエディタを…イヤマテ。それはいいけど、DFM にデータを保持できましたっけ?そうなんです、TStringGrid はデータを DFM に保持できないのです。これではコンポーネントエディタを作った所で意味がありません。
まずは TStringGrid を派生させて DFM にデータを保持できるようにしなければなりません。
{*******************************************************} { } { TEzStringGrid コンポーネント } { } {*******************************************************} unit uEzStringGrid;
interface
uses Classes, Vcl.Grids;
type TEzStringGrid = class(TStringGrid) private procedure ReadCellData(Reader: TReader); procedure WriteCellData(Writer: TWriter); protected procedure DefineProperties(Filer: TFiler); override; end;
implementation
{ TEzStringGrid }
procedure TEzStringGrid.DefineProperties(Filer: TFiler); // セルデータの読み書きを登録 begin inherited; Filer.DefineProperty('CellData', ReadCellData, WriteCellData, True); end;
procedure TEzStringGrid.ReadCellData(Reader: TReader); // DFM からセルデータを読み込み var Col, Row: Integer; begin Reader.ReadListBegin; try while not Reader.EndOfList do begin Col := Reader.ReadInteger; // 列 Row := Reader.ReadInteger; // 行 Cells[Col, Row] := Reader.ReadString; // セルデータ end; finally Reader.ReadListEnd; end; end;
procedure TEzStringGrid.WriteCellData(Writer: TWriter); // DFM へセルデータを書き込む var Col, Row: Integer; begin Writer.WriteListBegin; try for Row := 0 to RowCount - 1 do for Col := 0 to ColCount - 1 do if Cells[Col, Row] <> '' then begin Writer.WriteInteger(Col); // 列 Writer.WriteInteger(Row); // 行 Writer.WriteString(Cells[Col, Row]); // セルデータ end; finally Writer.WriteListEnd; end; end;
end.
具体的な実装はこのようになります。オリジナルのデータを DFM へ吐くようにするには DefineProperties() をオーバーライドします。TEzStringGrid という安直なコンポーネント名にツッコミを入れるのはやめてくださいね (^^;A
次に TEzStringGrid の設計時パッケージと実行時パッケージを作ってコンポーネントとして登録されるようにするのですが、既に別のスレッドで書いているので省略します。
[コンポーネントの作り方の概要]
http://ht-deko.minim.ne.jp/delphiforum/?vasthtmlaction=viewtopic&t=22
[LightReport2 Unicode 版を 64bit 対応にしてみる]
http://ht-deko.minim.ne.jp/delphiforum/?vasthtmlaction=viewtopic&t=1222
コンポーネントとして登録できるようになったら、次はいよいよコンポーネントエディタです。
{*******************************************************} { } { TEzStringGrid コンポーネントエディタ } { } {*******************************************************} unit uEzStringGridCompEditor;
interface
uses Vcl.Controls, Vcl.Dialogs, Vcl.Forms, Vcl.Grids, DesignEditors;
type
TEzStringGridEditor = class(TComponentEditor) // procedure Copy; override; procedure Edit; override; procedure ExecuteVerb(Index: Integer); override; function GetVerb(Index: Integer): string; override; function GetVerbCount: Integer; override; end;
implementation
uses uEzStringGrid, frmuEzStringGridEditor;
resourcestring StrMSG_CLEAR = 'クリアしてよろしいですか?'; StrMENU_EDIT = '編集(&E)...'; StrMENU_CLEAR = 'クリア(&C)';
{ procedure TEzStringGridEditor.Copy; // クリップボードを拡張 // http://docwiki.embarcadero.com/RADStudio/ja/%E3%82%AF%E3%83%AA%E3%83%83%E3%83%97%E3%83%9C%E3%83%BC%E3%83%89%E5%BD%A2%E5%BC%8F%E3%82%92%E8%BF%BD%E5%8A%A0%E3%81%99%E3%82%8B begin inherited; // Do Something end; }
procedure TEzStringGridEditor.Edit; // コンポーネントの編集 // http://docwiki.embarcadero.com/RADStudio/ja/%E3%83%80%E3%83%96%E3%83%AB%E3%82%AF%E3%83%AA%E3%83%83%E3%82%AF%E6%99%82%E3%81%AE%E5%8B%95%E4%BD%9C%E3%82%92%E5%A4%89%E6%9B%B4%E3%81%99%E3%82%8B var Grid: TEzStringGrid; begin Application.CreateForm(TEzStringGridForm, EzStringGridForm); try Grid := TEzStringGrid(Self.Component); if EzStringGridForm.ShowModal(Grid) = mrOK then Designer.Modified; finally EzStringGridForm.Free; end; end;
procedure TEzStringGridEditor.ExecuteVerb(Index: Integer); // コマンドの実行 // http://docwiki.embarcadero.com/RADStudio/ja/%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89%E3%82%92%E5%AE%9F%E8%A3%85%E3%81%99%E3%82%8B var Col, Row: Longint; Grid: TEzStringGrid; begin case index of 0:begin // 編集 Edit; end; 1:begin // クリア if not MessageDlg(StrMSG_CLEAR, mtConfirmation, [mbYES, mbNo], 0) = mrYES then Exit; Grid := TEzStringGrid(Component); for Row := 0 to Grid.RowCount - 1 do for Col := 0 to Grid.ColCount - 1 do Grid.Cells[Col, Row] := ''; Designer.Modified; end; end; end;
function TEzStringGridEditor.GetVerb(Index: Integer): string; // メニューのキャプションを返す // http://docwiki.embarcadero.com/RADStudio/ja/%E3%83%A1%E3%83%8B%E3%83%A5%E3%83%BC%E9%A0%85%E7%9B%AE%E3%82%92%E6%8C%87%E5%AE%9A%E3%81%99%E3%82%8B begin case Index of 0: result := StrMENU_EDIT; 1: result := StrMENU_CLEAR; end; end;
function TEzStringGridEditor.GetVerbCount: Integer; // 追加メニュー (コマンド) 数を返す // http://docwiki.embarcadero.com/RADStudio/ja/%E3%83%A1%E3%83%8B%E3%83%A5%E3%83%BC%E9%A0%85%E7%9B%AE%E3%82%92%E6%8C%87%E5%AE%9A%E3%81%99%E3%82%8B begin result := 2; end;
end.
コンポーネントエディタは TComponentEditor から派生して、
- Copy()
- Edit()
- ExecuteVerb()
- GetVerb()
- GetVerbCount()
これらをオーバライドして実装するだけです。Edit() メソッドがコンポーネントエディタのキモで、TComponentEditor.Component プロパティに対して読み書きして Designer.Modified() メソッドを呼び出せば変更が反映されます。ソースコードのコメントにそれぞれのメソッドのドキュメントへのリンクがありますので、詳細はそちらを確認してください。
{*******************************************************} { } { TEzStringGrid コンポーネント/エディタ登録用ユニット } { } {*******************************************************} unit uEzStringGridReg;
interface
uses SysUtils, Classes, Types;
procedure register;
implementation
uses Vcl.Controls, DesignIntf, uEzStringGrid, // TEzStringGrid コンポーネント uEzStringGridCompEditor; // TEzStringGrid コンポーネントエディタ
procedure register; begin RegisterComponents('Additional', [TEzStringGrid]); // コンポーネントの登録 RegisterComponentEditor(TEzStringGrid, TEzStringGridEditor); // コンポーネントエディタの登録 end;
end.
最後にコンポーネント登録用ユニットに RegisterComponentEditor() でコンポーネントエディタを登録して完了です。パッケージをインストールし、コンポーネントパレットにある EzStringGrid を貼り付けてコンポーネントをダブルクリックするか、EzStringGrid を右クリックして [編集(E)...] を選択するとコンポーネントエディタが起動します。

繰り返しますが、DFM に保存されるプロパティだけを設定するコンポーネントエディタであれば "ポン付け" が可能ですが、そうでないものに関しては DefineProperties() をオーバーライドする必要があります。実際、コンポーネントエディタだけを作って登録もできるのですが DFM に値が保持されないので意味はありません。
この記事で紹介した EzStringGrid のアーカイブは以下にあります。
ダウンロード:
http://ht-deko.minim.ne.jp/software/EzStringGrid_100.zip
このコンポーネントエディタのデザイナ部分は "Delphi 2.0J コンポーネントセット 服部誠著 (ISBN: 4756110576)" に含まれる TComboGrid のデザイナをインスパイアした劣化コピーですので、正直ライセンス的に問題があるかもしれません。気になる方は (気になるでしょうけど) コンポーネントエディタの作り方のサンプル程度の利用に留めておくか、"Delphi 2.0J コンポーネントセット" を入手してください。
コンポーネントとコンポーネントエディタ/プロパティエディタをまとめて一つのユニットにする事もできますが、それだと設計時/実行時パッケージを分離できなくなります。コンポーネント (or プロパティ) エディタとエディタ用のフォームをまとめる事もできますが、メンテナンス性を考えるとできるだけユニットは分割した方がいいように思います。
EzStringGrid のアーカイブを解凍し EzStringGrid.groupproj を Delphi で開いて (XE2 以降で開けると思います)、最初の方にあった図と見比べてみればコンポーネントとパッケージ、コンポーネントエディタ / プロパティエディタの関係が見えてくると思います。
コンポーネントの作り方についてのドキュメントについては以下にリンクを貼っておきますので確認してみてください。
See Also:
[コンポーネント開発者ガイド (DocWiki)]
http://docwiki.embarcadero.com/RADStudio/ja/%E3%82%B3%E3%83%B3%E3%83%9D%E3%83%BC%E3%83%8D%E3%83%B3%E3%83%88%E9%96%8B%E7%99%BA%E8%80%85%E3%82%AC%E3%82%A4%E3%83%89%EF%BC%9A%E3%82%A4%E3%83%B3%E3%83%87%E3%83%83%E3%82%AF%E3%82%B9
[プロパティエディタの追加 (DocWiki)]
http://docwiki.embarcadero.com/RADStudio/ja/%E3%83%97%E3%83%AD%E3%83%91%E3%83%86%E3%82%A3%E3%82%A8%E3%83%87%E3%82%A3%E3%82%BF%E3%81%AE%E8%BF%BD%E5%8A%A0
[コンポーネント エディタの追加 (DocWiki)]
http://docwiki.embarcadero.com/RADStudio/ja/%E3%82%B3%E3%83%B3%E3%83%9D%E3%83%BC%E3%83%8D%E3%83%B3%E3%83%88_%E3%82%A8%E3%83%87%E3%82%A3%E3%82%BF%E3%81%AE%E8%BF%BD%E5%8A%A0
[コンポーネントの登録 (DocWiki)]
http://docwiki.embarcadero.com/RADStudio/ja/%E3%82%B3%E3%83%B3%E3%83%9D%E3%83%BC%E3%83%8D%E3%83%B3%E3%83%88%E3%81%AE%E7%99%BB%E9%8C%B2
[パッケージとコンポーネントの操作 (DocWiki)]
http://docwiki.embarcadero.com/RADStudio/ja/%E3%83%91%E3%83%83%E3%82%B1%E3%83%BC%E3%82%B8%E3%81%A8%E3%82%B3%E3%83%B3%E3%83%9D%E3%83%BC%E3%83%8D%E3%83%B3%E3%83%88%E3%81%AE%E6%93%8D%E4%BD%9C%EF%BC%9A%E6%A6%82%E8%A6%81
[Delphi 関連書籍一覧]
http://ht-deko.minim.ne.jp/Delphi/books.html
|