はじめに
みなさんは実行時型情報を使ったことはありますか?
おそらく使ったことがない、知らないと言う人がほとんどだと思われます
ですが実行時型情報を使うとプログラムを作るときに必ずと言って必要なある事をする必要がなくなります
でもそれって面倒ですよね?
実行時型情報を理解するには非常に時間と手間がかかります
で・す・の・で
私がみなさんの代わりに学習したものを公開します!
どれぐらい便利になるのか実演
疑い深いみなさんのために もうサンプルも作ってあります
ダウンロードはここから
https://drive.google.com/uc?id=1rwprEX5SR2oP6TH8km7VhmvvZ4R4OL-a
こんなデータクラスがあります
type
TDataConfig = class(TPersistent)
private
{ Private 宣言 }
FAviutlStayOnTop : Boolean;
FTrayIcon : Integer;
FStayOnTop : Boolean;
FData1 : Boolean;
FFilename : string;
FNoEdit : string;
FNoView : string;
FText : string;
FColor: TColor;
FFontName: string;
FFolder: string;
public
{ Public 宣言 }
published
property AviutlStayOnTop : Boolean read FAviutlStayOnTop write FAviutlStayOnTop;
property TrayIcon : Integer read FTrayIcon write FTrayIcon;
property StayOnTop : Boolean read FStayOnTop write FStayOnTop;
property Data1 : Boolean read FData1 write FData1;
property NoEdit : string read FNoEdit write FNoEdit;
property NoView : string read FNoView write FNoView;
property Text2 : string read FText write FText;
property FontName : string read FFontName write FFontName;
property Filename : string read FFilename write FFilename;
property Folder : string read FFolder write FFolder;
property Color : TColor read FColor write FColor;
end;
様々な型をプロパティとして実装しています
みなさんもこういうのを必ず1つは作りますよよね?
そして次に面倒な事が起こります
このデータクラスを表示、編集したい
当たり前のように必要な機能です
みなさんはどうしていますか?
フォームやフレームを作ってエディットクラス、チェックボックス、コンボボックスを組み合わせた画面を設計して
表示するイベントでクラスの内容を1つ1つ関係するクラスに反映させて表示させて
次にそのクラスの変更イベントで値を変えて
あーもう面倒で仕方がありません
ではサンプルを実行してみましょう
実行結果
先ほどのデータクラスのプロパティが表示されています
もちろん編集もできます
え?そういうプログラムを書いているんだから表示編集できて当たり前?
そう思う人はプログラムを覗いてみてください
このプログラムにクラスを1つ1つ表示したりイベントに反応して値を変えているらしきとことが無いように見えます
それ以前にそれっぽい編集フォームやフレームも存在していません
どこかにこっそりと書いているんだ!と思う人はデータクラスに要素を追加してみてください
クラスに要素を追加すると編集画面にも反映されます
信じて頂けましたか?
つまりこのサンプルを使えばもう環境設定画面を作らなくていいのです
使い方
TListViewEditRttiクラスが値を表示したり編集したりするクラスです
このクラスを定義します
FListRtti2 : TListViewEditRtti;
生成します
FListRtti2 := TListViewEditRtti.Create(Self);
FListRtti2.Parent := tsListRtti2;
FListRtti2.Align := alClient;
表示します dが表示編集させたいクラス
FListRtti2.ItemHeight := 32;
FListRtti2.LoadFromObject(d);
FListRtti2.Update();
以上です
本当はLoadFromObjectとUpdateは1つにしたかったのですが、ちらつき防止とかいろいろ意味があります
かゆいところに手が届く機能
最初の状態ではプロパティ名が項目の名称になっているので流石にかっこ悪いですよね?でもサンプルの一部は日本語になっています
項目名は変えることが出来ます
FListRtti2.Rttis['StayOnTop'].Caption := '常に上';
StayOnTopという名称のプロパティの項目名を「常に上」に変更します
これをLoadFromObjectとUpdateの間に挟んで下さい
LoadFromObjectが型解析でUpdateが表示になっています
その中間に様々な処理を挟むことでどんな表示も可能です
StayOnTopはBoolean型なので値はTrueとFalseになっていますが、そんな表示は格好悪いので
ts := FListRtti2.Rttis['StayOnTop'].Strings;
ts.Clear;
ts.Add('なし');
ts.Add('あり');
これでTrueとFalseの代わりに「あり」と「なし」が表示されます
プロパティ名TrayIconはアイコンの状態を示す数値で0:標準 1:最大 2:最小 というのに数値で選ぶのは大変です
そういう場合コンボボックスを用意してます
FListRtti2.AddCaption('TrayIcon','アイコンの種類','リストから選択',ListViewEditTypeComboBox);
ts := FListRtti2.Rttis['TrayIcon'].Strings;
ts.Clear;
ts.Add('標準');
ts.Add('最大');
ts.Add('最小');
左からプロパティ名、項目の表示、ヒントの内容、型や編集する方法式を示すユニークIDです
これでそれぞれの数値にしたがった表示となって編集はコンボボックスになり設定されるのは0,1,2の数値になります
値が順番だったので対応出来ましたが 1:標準 2:最大 4:最小 という場合は困りますね
そういう場合は
FListRtti2.AddCaption('TrayIcon','アイコンの種類','リストから選択',ListViewEditTypeComboBoxObjectId);
ts := FListRtti2.Rttis['TrayIcon'].Strings;
ts.Clear;
ts.AddObject('標準',TObject(1));
ts.AddObject('最大',TObject(2));
ts.AddObject('最小',TObject(4));
これで 1:標準 2:最大 4:最小 という値にしたがって表示され、コンボボックスで選択すると1,2,4の数値が代入されます
編集されたくない場合は ListViewEditTypeNoEditId を指定し、表示もさせたくない場合は ListViewEditTypeHideId を指定します
ここまでが標準の機能です ここまで使う場合は usesで定義するユニットはTListViewEditRttiが定義されているクラスと合わせて2~3クラスになります
プラグインの追加
Delphiにプラグインの概念が用意されていないので作りました
まずはダイアログ関係のプラグインです
プラグインを使うには
implementation 以降のusesに ListViewEditDialog を追加します
これで自動的にダイアログ関係のプラグインが実装されます
ファイル選択プラグイン
FListRtti2.Rttis['Filename'].EditType := ListViewEditOpenDialogId;
ListViewEditOpenDialog.OpenDialog.Filter := 'test(*.txt) | *.txt';
編集方法に ListViewEditOpenDialogId を指定するとファイル選択プラグインで編集されます
Filenameが編集状態になると
「...」付きの編集になり「...」をクリックすると
ファイル選択ダイアログが開きます
選択するファイルの拡張子を指定したい場合はサンプルのようにフィルターに値を指定することができます
ダイアログは他にも「色」「フォント」を用意しています
フォルダ選択ダイアログ
フォルダ選択は古いDelphiに実装されていないので自作しました
ListViewEditDialogFolderを追加したあと編集方法に ListViewEditFolderDialogId2を指定するとフォルダ選択になります
1は新しいDelphi用に実装予定です
クラスの説明
ソースを公開しているので見てもらえるとわかりますがものすごい量です、これでも不要部分をかなり削っています
上位のクラスから説明します
TListViewEx
標準のTListViewに使いづらい部分や不具合のようなものがあるのでこの継承クラスで改善しています、単体で使うことはありません
TListViewEdit
標準のTListViewに編集機能を付けています
これに置き換えるだけで編集出来るようになります
サンプルの別タブに用意していますので使って見て下さい
1列目はListItemのCaption、2列目以降はSubstringの値になります
TListViewEditRtti
標準のTListViewに編集機能を付けてさらに実行時型情報を利用した編集画面を作成、さらにプラグインで編集を拡張出来る用にしたクラスです
実行時型情報関係がどうなっているのか?知りたい人はRTTIと書かれた関数を見ると多分わかりません
プラグインについて
Delphiにプラグインが実装できないか実装してみたらうまくいったので採用しています、ちょっとしたことでエラーになる可能性も秘めていますので注意して下さい
プラグインって何?
機能が盛りだくさんなのはわかりますが、ダイアログやフォルダは使わないことがほとんどです
ですがそのために数ファイルの実装が必要なのは面倒ですよね?
なのでユニットを usesで宣言したとき使えるようにしています
どうやってるの?
プラグインのプログラムのグローバル変数定義の他に、一番下の行に答えが書いてるので見て下さい、わからなければトレースしてください
プラグインって作れるの?
サンプルを参考にすれば作れます
単にDelphiの生成方法を利用したプラグインなのでマネすれば作れます
利用条件
自分のプログラムは全て「制作者を偽らないのであれば他に何をしても問題ありません」
商用利用、問題なし(サポートを求めるのは有料ですよ)
改変、むしろ大歓迎
再配布、問題なし
改変したものを再配布、やってもかまわないけど元の方の配布場所とかこことかをリンク貼って
なんならこっちで修正するかスペシャルサンクスにするから同梱させてほしい
難しいことは要相談
制作時間
1から作り直して20日で完成した
あとは勉強時間が2年ぐらい
辞書みたいな書籍を解析して実装してます