1

I need to bi-directionaly bind Synedit to column 'text' in database. It works with memo component, but for Synedit or i.e. Richedit is created only one-directional binding. Value is synchronized from database, but i don't know how to update back to database from Synedit.

I try in the Livebinding designer simple connect fields like on picture, but it i'm stuck in documentation.

scheme binding

I need automatically synchronized database field when i leave editor, like it works with Memo component.

Axis
  • 85
  • 8

1 Answers1

2

The reason that TSynMemo does not behave like TMemo is that 'out of the box' it has no support the LiveBindings observers necessary to make LB work. The way to overcome this is to derive a TSynMemo descendent which does support LB observers and use that instead.

Luckily, there is an Embarcadero wiki entry which explains how to add LB observers to components which lack them. Based on that, with a little preparatory settting up, the example code should do what you want:

  • On a new form, drop a TClientDataSet, TDataSource, TDBGrid and a TDNavigator and link them up in the usual way. Using a TClientDataSet is to allow a completely self-contained sample project. Note If you want to use FireDAC. you can equally well use a TFDMemTable instead of a TClientDataSet; the steps and code below will be identical, and I've tested that this FD alternative works.

  • Set up ID, Name (String(20)) and Memo fields on the TClientDataSet as shown in the code

  • Add a TBSynEdit connected via the TDataSource to the Memo Field. The purpose of this is to show that the live-bound TSynMemo which we'll add next performs in the same way

  • Add a TSynMemo, TMemo, TBindingList, TBindSourceDB, and TBindNavigator to the form

  • Use the quick-binding pop-up to add TLinkControlToFields to link the TSynEdit and the TMemo to the Memo field of the ClientDataSet

  • Connect the * property of the BindSourceDB to the * property of the TBindNavigator in the visual LB designer.

Now, add the code below to the Form. To save having to register our TSynMemo descendant as a component and install it in the IDE, the code declares it in the form unit as an Interposer class. It implements all the obsever methods that seem to be necessary to live-bind a TSynMemo.

Code

  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.
MartynA
  • 30,454
  • 4
  • 32
  • 73
  • I'm sorry but i can't reproduce it, too because i'm working with TFDconnection and TFDtable. Could you share this project? I understand that i need the Interposer and how to create, but you do it in main unit and use livebinding designer? – Axis Aug 20 '19 at 18:28
  • Using FireDAC components should make no difference to whether this answer works or not. Idon;t understand what you are asking when you say " but you do it in main unit and use livebinding designer?" – MartynA Aug 20 '19 at 18:44
  • Sorry for my english. Is all this code in main unit? Can i create this bidirectional connection using the Livebinding designer? Because it does not working for me. – Axis Aug 20 '19 at 18:51
  • 1
    I just updated the code to replace the TClientDataSet by a TFDMemTable, changed the BindSourceDB1 to point at it and the project still works fine, so I'd be grateful if you would accept this answer (by clicking the "tick" icon) because it *does* answer your q as posted. If you are having trouble applying it to an FDTable, you should post a new q asking about that. – MartynA Aug 20 '19 at 18:53
  • Yes, **all** the code is in the main unit, as the code clearly shows. Which bi-directional connection are you asking about? If you create a new project exactly following the steps in my answer, it will "just work". – MartynA Aug 20 '19 at 18:57
  • It works now for me if i create livebinding with memo1 or create empty livebinding from popup and then in Text editor of Form I manually set from Memo1 to SynMemo1. It is acceptable for me. That is why i ask, if You are using one unit, because i still cannot make connection via pop-up and i suspect Delphi that they suggests modified component only if it is in another unit. – Axis Aug 21 '19 at 15:31
  • Well, the only reason I used an interposer class in my answer was to make it self-contained and not require installing the TSynEdit descendant as a component. But you should be easily able to put it in a separate unit, install it in a package and then have TYourSynEdit on the palette. I can't see any reason why it wouldn't then fully support the IDE LB Visual designer. – MartynA Aug 21 '19 at 15:48