はじめに
DelphiのRTTI機能を活用して、任意の TPersistent
クラスの published
プロパティを編集可能にするカスタムUI「TListViewRTTI」のサンプルを紹介します。
VCL標準の Object Inspector
に近い機能を TListView
ベースで実現し、RTTIによる動的プロパティ編集の仕組みをシンプルに体験できます。
🧩 背景と目的
Delphiでは設計時に Object Inspector
によってプロパティを編集できますが、実行時に任意のオブジェクトの published
プロパティを編集可能にするUIは標準では提供されていません。
TListViewRTTI
はその実現を目的としたコンポーネントで、次のような用途に活用できます:
- 設定画面(コンフィグUI)
- 動的なプロパティバインディング
- プラグインや拡張オブジェクトの編集UI
🚀 特徴と応用例
-
TPersistent
を継承したあらゆるデータ型に対応 -
published
のプロパティを自動列挙 - 項目ごとのラベル・色・ReadOnly制御も可能
- カスタム設定画面・構成ファイルの編集などに応用可能
🧱 使用サンプル
編集対象のクラス(TPersistent
を継承)を用意します。
type
TPersistentData = class(TPersistent)
published
property Enabled : Boolean;
property Level : Integer;
property NoEdit : string;
property NoView : string;
property Text : string;
property FontName : string;
property Filename : string;
property Folder : string;
property Color : TColor;
end;
このクラスをフォーム側で TListViewRTTI
にバインドします。
FData := TPersistentData.Create;
FListView := TListViewRTTI.Create(Self);
FListView.Parent := Panel1;
FListView.Align := alClient;
FListView.LoadFromObject(FData);
表示結果
この状態で編集操作が可能です。データを読み込んだり編集結果をデータに戻したりする処理は一切不要です。
すべてこのクラス内にて自動で行われます。
並び順
表示する順序が気になる人もいるかと思います。
表示順はRTTINamesプロパティで設定した項目順になるのと
それよりも先にLoadFromObjectを実行した場合はpublished節での宣言した順になります。
カスタマイズ
最小限の実装でも編集は可能ですがカスタマイズも可能です
項目毎にカスタマイズする場合RTTINamesプロパティを通じて行います。
property RTTINames[Name : string] : TListViewRTTIItem read GetRTTINames;
Nameにはバインドしたクラスのpublishedで宣言したプロパティを指定します。
プロパティ名を引数として渡すことに違和感があるかもしれません。
これは実行時型情報という仕組みを使用しています。
✏️ 表示名(Caption)のカスタマイズ
プロパティ名が要素の名称として使われると見づらいので設定します。
下記の様に記述します。
描画後に設定した場合はRefreshで再描画を行います。
FListView.RTTINames['Enabled'].Caption := '有効';
FListView.RTTINames['Level'].Caption := 'レベル';
FListView.RTTINames['NoEdit'].Caption := '編集不可';
FListView.RTTINames['Text'].Caption := 'テキスト';
FListView.RTTINames['Color'].Caption := '色';
FListView.RTTINames['FontName'].Caption := 'フォント';
FListView.RTTINames['Filename'].Caption := 'ファイル名';
FListView.RTTINames['Folder'].Caption := 'フォルダ名';
FListView.Refresh;
これでわかりやすくなりました。
🎨 項目の色変更
各項目毎に様々な設定を用意していますがわかりやすく色を変えてみます。
FListView.RTTINames['Enabled'].ColorFont := clBlack;
FListView.RTTINames['Enabled'].ColorBack := clMoneyGreen;
FListView.RTTINames['Level'].ColorFont := clWhite;
FListView.RTTINames['Level'].ColorBack := clNavy;
FListView.RTTINames['NoEdit'].ColorFont := clBlack;
FListView.RTTINames['NoEdit'].ColorBack := clSkyBlue;
FListView.RTTINames['Text'].ColorFont := clWhite;
FListView.RTTINames['Text'].ColorBack := clWebPink;
FListView.RTTINames['Color'].ColorFont := clBlack;
FListView.RTTINames['Color'].ColorBack := clSkyBlue;
FListView.RTTINames['FontName'].ColorFont := clWhite;
FListView.RTTINames['FontName'].ColorBack := clTeal;
FListView.RTTINames['Filename'].ColorFont := clBlack;
FListView.RTTINames['Filename'].ColorBack := clYellow;
FListView.RTTINames['Folder'].ColorFont := clBlack;
FListView.RTTINames['Folder'].ColorBack := clGray;
FListView.Refresh;
表示結果
編集方法(シンプル)
真偽型だがTrueやFalseは嫌だ
数値型だが選択式にしたい
そういう場合は編集方法を指定することが出来ます
また、ユーザーには設定させたくない項目にListViewEditPluginReadOnlyIdを
そもそも表示自体させたくない項目にListViewEditPluginHideIdを指定すると
編集不可、非表示になります。
FListView.RTTINames['NoView'].EditType := ListViewEditPluginHideId;
FListView.RTTINames['NoEdit'].EditType := ListViewEditPluginReadOnlyId;
FListView.RTTINames['Enabled'].Strings.Clear;
FListView.RTTINames['Enabled'].Strings.Add('いいえ');
FListView.RTTINames['Enabled'].Strings.Add('はい');
FListView.RTTINames['Level'].EditType := ListViewEditPluginComboBoxId;
FListView.RTTINames['Level'].Strings.Clear;
FListView.RTTINames['Level'].Strings.Add('弱');
FListView.RTTINames['Level'].Strings.Add('中');
FListView.RTTINames['Level'].Strings.Add('強');
FListView.Refresh;
表示結果
編集方法(ダイアログ系)
各項目は文字列としてTEditで編集したものをまた元の型に変換しています。
真偽型はComboBoxで編集が行われます。
数値型や文字列型もそれは色であったりフォント名、ファイル名として編集したい場合があります。
編集方法を変更する場合EditYpeを指定します。
FListView.RTTINames['Filename'].EditType := ListViewEditPluginOpenDialogId;
FListView.RTTINames['Folder'].EditType := ListViewEditFolderDialogId2;
FListView.RTTINames['Color'].EditType := ListViewEditPluginColorDialogId;
FListView.RTTINames['FontName'].EditType := ListViewEditPluginFontDialogId;
FListView.Refresh;
表示結果
ダイアログ系は編集モードに入ると左側に「..」が表示され、それをクリックすると変更用のダイアログが表示されます。
フォルダ選択ダイアログは新しいDelphiにはあるのですが、念のため依存しない独自のダイアログを採用していますので IDの後ろに「2」が付いています。
プラグインの追加
編集方法毎にプラグインとなっています。
プラグインは自作可能です。
基本プラグインのListViewEditPluginLibユニットを読むとわかります。
プラグイン本体を参照するグローバル変数とそのプラグインIDの2つが必要なのと
それらを初期化し登録するinitialization節に同じようにコードを書くだけです。
🔚 補足・今後の展望
- オブジェクトリストへの対応
- 標準プラグインの追加
📂 配布
GitHubにて公開中
📝 ライセンス
MIT ライセンス
不足している情報や補足は、この記事のコメント・編集などで随時更新予定です。