先週のブログ(https://blogs.embarcadero.com/ja/using-delphis-expressions-engine-ja/)でDelphi RTLの式エンジン(Expressions Engine)について紹介しました。
今回のブログでは、前回の基本的なDelphi RTLの式エンジンに引き続き、バインディング式、プロパティおよびコンポーネントなど、さらに踏み込んだもう少し高度なバインディングをコード例を交えて紹介いたします。コンポーネントとプロパティに対して参照する式を作成することで、異なるコンポーネント間で、どのように連携するか解説していきます。今回は、単純に式を評価するのではなく、2つの別々の式を「バインディング」で関連付けていきます。
Table of Contents
2つのコンポーネントのバインディング
ここでは、例としてTSpinEditとTProgressBarを配置したフォームを用意します。マネージドバインディング式を使用すると、入力式(1つ以上の入力オブジェクトを含む) と出力式 (1つ以上の出力オブジェクトを含む) を提供することができます。
以下のコード例では、SpinEdit(入力オブジェクト)とProgressBar(出力オブジェクト)をバインドする方法を示しています。
各オブジェクトはAssociateメソッドによって、spinEdit1コンポーネントはspin1、ProgressBar1コンポーネントはprogressというように名前が関連付けられており、そしてTBindings.CreateAssociationScopeメソッドによってバインディング式が定義でき、オブジェクトとそのプロパティを参照できます。このコード例では、入力式はspin1.Value、出力式はprogress.Positionです。なお、バインディング式で使用する出力コンバータ(Output Converter)を指定することもできますが、ここでは省略しています。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
BindingExpression1 := TBindings.CreateManagedBinding( { inputs } [TBindings.CreateAssociationScope([ Associate(SpinEdit1, 'spin1') ])], 'spin1.Value', { outputs } [TBindings.CreateAssociationScope([ Associate(ProgressBar1, 'progress') ])], 'progress.Position', {OutputConverter} nil); |
上記のコードでは、BindingExpression1.Evaluateを実行すると、その都度 SpinEdit1のValueプロパティの値がProgressBar1のPositionにコピーされます。SpinEdit1のValueプロパティが更新される度にEvaluateを実行する方法でも良いですが、以下のコード例のようにSpinEditのValueプロパティが変更されたことをバインディングアーキテクチャにNotify(通知)することができます。システムは、そのプロパティを含む1つ以上の式があるかどうかを自動的に判断し、値を更新する仕組みです。
1 2 3 4 |
procedure TFormBindings.SpinEdit1Change(Sender: TObject); begin TBindings.Notify(Sender, 'Value'); end; |
上記のコードを実行したシンプルなアプリケーションのUI画面の例は、下図をご覧ください。
オブジェクトとUIコントロールのバインディング
ここでは、オブジェクトをUIコントロールにバインドする方法を説明します。今回の例では、まず以下のようなTMyObjectというオブジェクトを用意します。
1 2 3 4 5 6 7 8 |
type TMyObject = class private FName:string; public procedure SetName(const Value: string); property Name: string read FName write SetName; end; |
さらにフォーム上にTEditコンポーネントを配置します。そして以下のコードのようにバインディング式を定義します。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
BindingExpressionObj := TBindings.CreateManagedBinding( { inputs } [TBindings.CreateAssociationScope([ Associate(MyObj, 'obj') ])], 'obj.Name', { outputs } [TBindings.CreateAssociationScope([ Associate(Edit1, 'edit') ])], 'edit.Text', {OutputConverter} nil); |
上記のコード例では、MyObjはTMyObjectクラスのオブジェクトで、Nameプロパティは、Edit1コンポーネントのTextに関連付けられています。この場合も、式を更新すると、出力も更新されます。但し、このオブジェクトと、UIコントロールや UI バインディングの間には認識は無く、データモデルとUIの両者は互いに疎結合です。そのため、オブジェクトの変化を知らせる術は、値が変更されたことをシステムに通知する必要があります。下記のコード例では、データが変更されると、それを反映してUIが自動的に更新されます。
1 2 3 4 5 |
procedure TMyObject.SetName(const Value: string); begin FName := Value; TBindings.Notify(self, 'Name'); end; |
最後にボタンを押すと、MyObjectのNameプロパティを変更するために、Button1のOnClickイベントハンドラに以下のコードを追加します。
1 2 3 4 |
procedure TForm1.Button1Click(Sender: TObject); begin Myobj.Name := Myobj.Name + 'Monkey'; end; |
上記のコードを実行したシンプルなアプリケーションのUI画面の例は、下図の通りです。
ここで重要なのは、関連付けられている UI からデータモデルを完全に抽象化しているという点です。Delphiでバインディングを使用して実装されたパターンをどのように呼び出すかは開発者に委ねられるため、あくまで一例にはなりますが、少なくともバインディング式によってデータモデルとユーザーインターフェイス(UI)ビューの完全な分離と抽象化を実現しているのは明確です。
バインディング技術は、他にも…
これでようやく、双方向バインディングをどのように扱うか、また、特定のコンポーネントデザイナを使ってここで紹介してきたすべてのロジックを定義し、必要なコードを減らす方法について検討を始めることができます。Visual Live Bindingsの背後には多くの技術があり、それは潜在的な最終目的になりますが、エンバカデロや開発者の間でよく議論される内容でもあります。Live Bindingsは素晴らしくDelphi RTLのバインディング技術が力を発揮します。もし機会があれば、バインディング技術に関して更に深掘りした内容を紹介いたします。
関連情報
- チュートリアル:LiveBinding をプログラムで使用する
- アプリケーションを作成し uses 句を定義する
- バインド可能なオブジェクトを定義および実装する
- 作成したオブジェクトをプログラムでバインドする
Design. Code. Compile. Deploy.
Start Free Trial Upgrade Today
Free Delphi Community Edition Free C++Builder Community Edition