Delphi Synedit 的双向绑定

Delphi bidirectional binding for Synedit

我需要将 Synedit 双向绑定到数据库中的列 'text'。它适用于备忘录组件,但对于 Synedit 或即 Richedit 仅创建单向绑定。 值是从数据库同步的,但我不知道如何从 Synedit 更新回数据库。

我在 Livebinding 设计器中尝试了简单的连接字段,就像图片上那样,但我卡在了文档中。

我需要在离开编辑器时自动同步数据库字段,就像它与 Memo 组件一起工作一样。

TSynMemo 不像 TMemo 的原因是 'out of the box' 它不支持 LiveBindings observers 使 LB 工作所必需的。克服这个问题的方法是派生一个 TSynMemo 支持 LB 观察器并使用它的后代。

幸运的是,有一个Embarcadero wiki entry 这解释了如何将 LB 观察器添加到缺少它们的组件中。在此基础上,通过一些准备工作, 示例代码应该做你想做的事:

  • 在一个新窗体上,放下一个 TClientDataSet、TDataSource、TDBGrid 和一个 TDNavigator,然后 link 按照通常的方式将它们组合起来。使用 TClientDataSet 是为了允许一个完全独立的示例项目。 注意如果你想使用FireDAC。您同样可以使用 TFDMemTable 而不是 TClientDataSet;下面的步骤和代码将是相同的,我已经测试过这个 FD 替代方案有效。

  • 如代码所示在TClientDataSet上设置ID、Name (String(20))和Memo字段

  • 将通过 TDataSource 连接的 TBSynEdit 添加到备忘录字段。这样做的目的 是为了表明我们接下来将添加的实时绑定 TSynMemo 以相同的方式执行

  • 在表单中添加 TSynMemo、TMemo、TBindingList、TBindSourceDB 和 TBindNavigator

  • 使用快速绑定弹出窗口将 TLinkControlToFields 添加到 link TSynEdit 和 TMemo 到 ClientDataSet

  • 的备注字段
  • 在可视化LB设计器中将BindSourceDB的*属性连接到TBindNavigator的*属性

现在,将下面的代码添加到表单中。为了节省必须注册我们的 TSynMemo descendant 作为组件并将其安装在 IDE 中,代码声明了它 在表单单元中作为 Interposer class。它实现了所有观察者方法 这似乎是实时绑定 TSynMemo 所必需的。

代码

  type
    TSynMemo = class(SynMemo.TSynMemo)
    private
      procedure ObserverToggle(const AObserver: IObserver; const Value: Boolean);
    protected
      procedure DoChange; override;
      function CanObserve(const ID: Integer): Boolean; override;                       { declaration is in System.Classes }
      procedure ObserverAdded(const ID: Integer; const Observer: IObserver); override; { declaration is in System.Classes }
    end;

    TForm2 = class(TForm)
      ClientDataSet1: TClientDataSet;
      DataSource1: TDataSource;
      DBGrid1: TDBGrid;
      DBNavigator1: TDBNavigator;
      SynMemo1: TSynMemo;
      ClientDataSet1ID: TIntegerField;
      ClientDataSet1Name: TStringField;
      ClientDataSet1Memo: TMemoField;
      BindingsList1: TBindingsList;
      BindNavigator1: TBindNavigator;
      Memo1: TMemo;
      BindSourceDB1: TBindSourceDB;
      LinkControlToField1: TLinkControlToField;
      LinkControlToField2: TLinkControlToField;
      DBSynEdit1: TDBSynEdit;
      procedure FormCreate(Sender: TObject);
    public
    end;

  [...]implementation[...]

  function TSynMemo.CanObserve(const ID: Integer): Boolean;
  { Controls which implement observers always override TComponent.CanObserve(const ID: Integer). }
  { This method identifies the type of observers supported by TObservableTrackbar. }
  begin
    case ID of
      TObserverMapping.EditLinkID,      { EditLinkID is the observer that is used for control-to-field links }
      TObserverMapping.ControlValueID:
        Result := True;
    else
      Result := False;
    end;
  end;

  procedure TSynMemo.DoChange;
  begin
    inherited;
    TLinkObservers.ControlChanged(Self);
  end;

  procedure TSynMemo.ObserverAdded(const ID: Integer; const Observer: IObserver);
  begin
    if ID = TObserverMapping.EditLinkID then
      Observer.OnObserverToggle := ObserverToggle;
  end;

  procedure TSynMemo.ObserverToggle(const AObserver: IObserver; const Value: Boolean);
  var
    LEditLinkObserver: IEditLinkObserver;
  begin
    EXIT;  //  do nothing
  end;

  const
    sDfm = 'DFM';

  procedure TForm2.FormCreate(Sender: TObject);
  begin
    ClientDataSet1.IndexFieldNames := 'ID';
    ClientDataSet1.CreateDataSet;

    ClientDataSet1.InsertRecord([1, 'Row1', 'Memo1']);
    ClientDataSet1.InsertRecord([2, 'Row2', 'Memo two']);

  end;

  initialization

    Data.Bind.Components.RegisterObservableMember(TArray<TClass>.Create(
      TSynMemo
      ),
      'Lines.Text', sDfm);

  finalization

    Data.Bind.Components.UnregisterObservableMember(TArray<TClass>.Create(TSynMemo));

  end.