LiveBindings 覚え書き

Live BindingsのサンプルとかがDBとの連携ばっかりで、自作Classとの連携とかのがあんま無かったので調べた事の覚え書き

フォームにTEdit2個とTCheckBox1個。TButtonを2個配置する。後は、TPrototypeBindSourceを1個配置。
データを保持するクラスとしてTUserDataを定義。

  TUserData = class(TObject)
  private
    FInputBool: Boolean;
    FInputString1: string;
    FInputString2: string;
    procedure SetInputBool(const Value: Boolean);
    procedure SetInputString1(const Value: string);
    procedure SetInputString2(const Value: string);
  public
    property InputBool: Boolean read FInputBool write SetInputBool;
    property InputString1: string read FInputString1 write SetInputString1;
    property InputString2: string read FInputString2 write SetInputString2;
  end;

{ TUserData }

procedure TUserData.SetInputBool(const Value: Boolean);
begin
  FInputBool := Value;
end;

procedure TUserData.SetInputString1(const Value: string);
begin
  FInputString1 := Value;
end;

procedure TUserData.SetInputString2(const Value: string);
begin
  FInputString2 := Value;
end;


TPrototypeBindSource に上記クラスのプロパティと連携する為にstringフィールド2個とBoolフィールドを1個作る。

そして、LiveBingindデザイナを使って関連付けを行う。
TPrototypeBindSource とTUserDataを関連付ける為に、TPrototypeBindSource.OnCreateAdapterでAdapterを作成する。
オブジェクトと関連付ける場合は、TObjectBindSourceAdapterを使う。Listと関連付ける場合はTListBindSourceAdapterを使う。

procedure TForm1.PrototypeBindSource1CreateAdapter(Sender: TObject;
  var ABindSourceAdapter: TBindSourceAdapter);
begin
  ABindSourceAdapter := TObjectBindSourceAdapter<TUserData>.Create(Self,
    FData, False);
end;

こちらによると、TPrototypeBindSource.OnCreateAdapterがFormのOnCreateよりも先に呼ばれる為に、TObjectBindSourceAdapterとかTListBindSourceAdapterに渡すオブジェクトはOnCreateで作成するんじゃなくて、コンストラクタで作成する必要がある模様。

constructor TForm1.Create(AOwner: TComponent);
begin
  // TPrototypeBindSource.OnCreateAdapter が OnCreateForm よりも先に発生する
  // ので、OnCreateFormではなく、コンストラクタで初期化を行う
  FData := TUserData.Create;
  FData.InputBool := True;
  FData.InputString1 := '1';
  FData.InputString2 := '2';
  inherited;
end;

これで、TPrototypeBindSourceを介してFormのEditやCheckboxとTUserDataが関連付けられました。
TUserDataに変更を行った後や、EditやCheckBoxのOnChangeイベントでTPrototypeBindSource.Refreshを呼ぶ事でフォームの表示とTUserDataの同期が取られます。

接続情報の確認

TFDConnection 接続エディタの情報タブに表示される情報。開発環境なら、TFDConnectionを配置すれば確認出来るんですが、実行環境でも確認出来ないかと調べて見た所
TFDConnection.GetInfoReport で同じ情報が取得出来るようです。
FormにTButtonとTMemoを配置して、Buttonのクリックイベントに下記コードを書けば取得出来ます

procedure TFDReportW.Button1Click(Sender: TObject);
begin
  Memo1.Clear;
  FDConnection1.Params.DriverID := '*****';
  FDConnection1.Params.UserName := '*****';
  FDConnection1.Params.Password := '*****';
  FDConnection1.Params.Database := '*****';

  FDConnection1.Connected := True;
  FDConnection1.GetInfoReport(Memo1.Lines);
  FDConnection1.Connected := False;
end;

Debug時のエラー

Delphiでプログラムを書いていざデバッグ実行を実行すると、ntdll.dllがどうのってエラーでIDEから落ちちゃってデバッグ不能って事態になったとき、レジストリエディタを起動して

DelphiXE8の場合
HKEY_CURRENT_USER\Software\Embarcadero\BDS\16.0\Debugging\Embarcadero Debuggers\Evaluators\comp32x.dll

の値を確認して、ここが1になっていたら-1に変更してみたらエラーが発生しなくなるかも
XE8以外のバージョンは16.0の部分を読み替えて下さい。

以下、このエラーに関するQP及びQC

https://quality.embarcadero.com/browse/RSP-9677

Report No: 111022            Status: Closed
Exception C0000026, Module ntdll.dll, Appcrash during start of application in debug mode
http://qc.embarcadero.com/wc/qcmain.aspx?d=111022
QCWIN:Defect_No=111022

GetItについて

久々の更新。

XE8に追加されたパッケージ管理ツールのGetIt。ワンタッチでパッケージのインストール/アンインストールが出来る中々便利なツールです。
起動してみると判りますが、Delphi用とC++Builder用のそれぞれのパッケージが並んでいるので同じ名前のパッケージが2個ずつ並んでいます。
DelphiC++Builder 単体で利用してる場合は、それぞれをインストールすれば良いので良いのですがRAD Studioを使ってる場合、両方で使いたいからDelphi用/C++Builder用の両方インストールすれば良いのかなとかって、両方インストールするとインストール自体は出来るんですが再起動とかした時にどちらかのパッケージがロード出来ませんてエラーに遭遇することになります。
RAD Studioの場合は、Delphi用をインストールすればとりあえずC++Builderでも使えるっぽいのでDelphi用のみをインストールしておけば良いのではないかと。

64bitプログラムからのmdbへのアクセス その2

前回Microsoft Access データベース エンジン 2010の32bit版と64bit版の共存は出来なそうという結論で終わっていたのですが、その後FireDACのマニュアルを見てる時にMSAccessへの接続のページでインストーラに/passive オプションを付けてインストールしたら良いという記述を見つけまして。検索してみたらこういうページも発見。

さっそく実験してみた所、32bit版officeがインストールされた私の環境に64bit版のデータベースエンジンをインストールする事が出来ました。
MDBにアクセスするテストプログラムを作って確認してみましたが、32bit/64bitどちらのプログラムからもMDBにアクセスする事が出来ました。

SQLiteのDBファイル作成

ちょっと調べたので、忘れた時にまたはまらないようにメモ。
XE3からSQLiteに対応したわけですが、DBファイルを作成する方法がコマンドラインで作る意外にないのかなと調べました。

ここに説明がありますが、SQLConnectionのパラメータでFailIfMissingにFalseを指定して接続したら、指定したファイルが存在しない場合にファイルを作成してDBをオープンしてくれるようです。

SQLiteもDBXpressを普段使わないので正解にたどり着くのに中々苦労しました。

SelectDirectory と特殊フォルダ

タイムラインに「SelectDirectoryでルートをデスクトップからコンピュータに変更したい」ってのが流れてきたので調べた事のメモ。

SelectDirectoryは内部でSHBrowseForFolderを呼んでる。

SHBrowseForFolderのルートの指定は、BROWSEINFO構造体のpidlRootに値を指定します。このpidlRootがNilの場合はデスクトップがルートとして表示されます。

SelectDirectory関数の内部でどういう処理になっているかと言うと引数Rootに値が渡された場合DesktopのIShellFolderインターフェースを取得して、そのParseDisplayNameメソッドに引数Rootを渡してITEMIDLISTを取得しています。

ParseDisplayNameのヘルプによると、引数pszDisplayNameに「::{GUID}」の形で値を渡すことで特殊フォルダのITEMIDLISTを取得出来るようです。
SelectDirectoryの引数RootがpszDisplayNameに渡されるのでRootに「::{GUID}」を渡す事でフォルダ選択ダイアログのルートに特殊フォルダが指定出来るようです。

DelphiXE以降(2010は確認していないので不明)だと、ShlObj.pasにマイコンピュータ等のGUIDが定義されています。という事で下記のようにSelectDirectoryを呼ぶ事でコンピューターをルートとしてフォルダ選択ダイアログが表示されます。その他マイドキュメント(CLSID_MyDocuments)やコントロールパネル(CLSID_ControlPanel)なんかも定義されています。

ルートの指定を文字列でしないとダメなので色々調べましたが、ITEMIDLISTを引数に取るバージョンの関数とかをコピーして作った方が後々便利かもなーとか思いました。

uses
  FileCtrl, ShlObj;
procedure TForm1.Button1Click(Sender: TObject);
var
  SelectDir: String;
begin
  SelectDirectory('', '::' + GUIDToString(CLSID_MyComputer), SelectDir);
end;