0
votes

How do I get controls other than TStringGrid to work properly with Livebindings when the Dataset property of their Datasource is set at runtime? I am able get a TStringGrid to recognize the dataset fields, but not other controls such as TEdit.

Here are details of my project. I have created a datamodule and a form. The datamodule which has the dataset objects and the form has a datasource along with UI controls. To reduce dependencies between modules, I do not want the form unit to use the datamodule unit. Rather, I want to set the datasource.dataset at runtime. If I use a TStringGrid, this strategy works, but with TEdit, it does not. I have tried manually creating the fields in LiveBindings, but these do not map to the dataset. If I were using VCL, this would be a non-issue, but I have been unable to figure out the best way to accomplish this with Livebindings. FYI, this is a FMX project.

I also noticed that if I "used" the datamodule to create the Livebinding connections and subsequently unused it, the fields show up on the BindSourceDb and read (invalid). When I run the application and set the dataset property at runtime, the TEdit controls are able to find the fields an will work fine. Any suggestions how to do this without using and using modules?

2
By "use" I mean I add the file to one of the uses section and "unuse" means I removed it.Terry Thompson
By "use" I mean I add the file to the uses section under implementation and "unuse" means I removed it. I am programming to interfaces. The form has an interface property called ViewModel which I set immediately after the form is created. In the ViewModel set procedure, there is code which reads Datasource.Dataset := ViewModel.Dataset. The Datasource is already attached to a BindSourceDb. If I were doing this with a data aware application, all of the controls attached to the datasource would automatically connect and populate with data. How do I make this work with Livebindings?Terry Thompson

2 Answers

0
votes

The following project works fine for me using LiveBindings. I did what I understood you to be describing, namely

  • The Form unit, DynLiveBindingsu, initally Used the datamodule unit DynDMu. In that condition, I added the live bindings components to the form and "wired them up", starting by populating a BindingList object, and then using it to add a TLinkGridToDataSource and TLinkControlToField and setting their properties.

  • Then, I removed the reference to DynDMu from the form's Uses list, compiled and ran the project and the StringList1 and Edit1 are correctly bound to the dataset in the datamodule.

Obviously I can't see your project so have no idea why you are getting a problem, but at least this answer gives you a working project to use as the basis of a comparison with yours.

Notice, btw, that there is no TDataSource involved, either in the datamodule or the form.

I'm using Delphi Seattle, btw.

DataModule:

unit DynDMu;
[...]
type
  TDataModule1 = class(TDataModule)
    CDS1: TClientDataSet;
    CDS1Name: TStringField;
    CDS1ID: TIntegerField;
    procedure DataModuleCreate(Sender: TObject);
  end;

[...]

procedure TDataModule1.DataModuleCreate(Sender: TObject);
begin
  CDS1.CreateDataSet;
  CDS1.InsertRecord([1, 'One']);
  CDS1.InsertRecord([2, 'Two']);
  CDS1.InsertRecord([3, 'Three']);
end;

Form unit

unit DynLiveBindingsu;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Data.Bind.EngExt,
  Vcl.Bind.DBEngExt, Vcl.Bind.Grid, System.Rtti, System.Bindings.Outputs,
  Vcl.Bind.Editors, Data.Bind.Components, Data.Bind.DBScope, Data.Bind.Grid,
  Data.DB, Vcl.StdCtrls, Vcl.Grids, Data.Bind.Controls, Vcl.ExtCtrls,
  Vcl.Buttons, Vcl.Bind.Navigator;

type
  TForm1 = class(TForm)
    StringGrid1: TStringGrid;
    Edit1: TEdit;
    BindingsList1: TBindingsList;
    LinkGridToDataSource1: TLinkGridToDataSource;
    BindSourceDB1: TBindSourceDB;
    BindNavigator1: TBindNavigator;
    LinkControlToField1: TLinkControlToField;
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

end.

Form DFM

object Form1: TForm1
[...]
  object StringGrid1: TStringGrid
  [...]
  end
  object Edit1: TEdit
  [...]
  end
  object BindNavigator1: TBindNavigator
    Left = 16
    Top = 150
    Width = 240
    Height = 25
    DataSource = BindSourceDB1
    Orientation = orHorizontal
    TabOrder = 2
  end
  object BindingsList1: TBindingsList
    Methods = <>
    OutputConverters = <>
    Left = 216
    Top = 184
    object LinkGridToDataSource1: TLinkGridToDataSource
      Category = 'Quick Bindings'
      DataSource = BindSourceDB1
      GridControl = StringGrid1
      Columns = <
        item
          MemberName = 'ID'
        end
        item
          MemberName = 'Name'
        end>
    end
    object LinkControlToField1: TLinkControlToField
      Category = 'Quick Bindings'
      DataSource = BindSourceDB1
      FieldName = 'Name'
      Control = Edit1
      Track = False
    end
  end
  object BindSourceDB1: TBindSourceDB
    DataSet = DataModule1.CDS1
    ScopeMappings = <>
    Left = 296
    Top = 184
  end
end
0
votes

Martyn,

This did not address my question. I did not want to place the datamodule unit in my uses section at all. Instead i wanted to "inject" the dataset by setting datasource.dataset.

I was able to find a solution. It involves causing the linkControltoField1 property to "refresh" by setting its active property to false and then to true after the dataset property is set. Here is my code. (D10.1 Berlin)

unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, System.Rtti, System.Bindings.Outputs,
  Vcl.Bind.Editors, Data.Bind.EngExt, Vcl.Bind.DBEngExt, FireDAC.Stan.Intf,
  FireDAC.Stan.Option, FireDAC.Stan.Param, FireDAC.Stan.Error, FireDAC.DatS,
  FireDAC.Phys.Intf, FireDAC.DApt.Intf, FireDAC.Stan.Async, FireDAC.DApt,
  FireDAC.UI.Intf, FireDAC.VCLUI.Wait, FireDAC.Stan.Def, FireDAC.Stan.Pool,
  FireDAC.Phys, FireDAC.Phys.MSAcc, FireDAC.Phys.MSAccDef, Data.Bind.Controls,
  Vcl.ExtCtrls, Vcl.Buttons, Vcl.Bind.Navigator, Data.DB, FireDAC.Comp.Client,
  FireDAC.Comp.UI, FireDAC.Comp.DataSet, Data.Bind.Components, Vcl.StdCtrls,
  Data.Bind.DBScope, Data.Bind.ObjectScope, Vcl.Bind.Grid, Data.Bind.Grid,
  Vcl.Grids;

type
  TForm1 = class(TForm)
    BindSourceDB1: TBindSourceDB;
    Edit1: TEdit;
    BindingsList1: TBindingsList;
    DataSource1: TDataSource;
    FDGUIxWaitCursor1: TFDGUIxWaitCursor;
    NavigatorBindSourceDB1: TBindNavigator;
    StringGrid1: TStringGrid;
    LinkGridToDataSourceBindSourceDB1: TLinkGridToDataSource;
    Button1: TButton;
    Button2: TButton;
    Button3: TButton;
    LinkControlToField1: TLinkControlToField;
    procedure Button1Click(Sender: TObject);
  private
    function GetDataset: Tdataset;
    procedure SetDataset(const Value: Tdataset);
    { Private declarations }
  public
    { Public declarations }
    property Dataset: Tdataset read GetDataset write SetDataset;
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
begin
  BindSourceDb1.DataSource := DataSource1;
  LinkControlToField1.DataSource := BindSourceDB1;
  LinkControlToField1.Active := False;   //in order for this to work you must
  LinkControlToField1.Active := True;    // set link to false then to true

end;

function TForm1.GetDataset: Tdataset;
begin
  Result := Datasource1.DataSet;
end;

procedure TForm1.SetDataset(const Value: Tdataset);
begin
  Datasource1.DataSet := Value;
  BindSourceDb1.DataSource := DataSource1;
  LinkControlToField1.DataSource := BindSourceDB1;
  LinkControlToField1.Active := False;   //in order for this to work you must
  LinkControlToField1.Active := True;    // set link to false then to true
end;

end.

Here is the dfm file in text.

object Form1: TForm1
  Left = 0
  Top = 0
  Caption = 'Form1'
  ClientHeight = 299
  ClientWidth = 635
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'Tahoma'
  Font.Style = []
  OldCreateOrder = False
  PixelsPerInch = 96
  TextHeight = 13
  object Edit1: TEdit
    Left = 184
    Top = 88
    Width = 121
    Height = 21
    TabOrder = 0
  end
  object NavigatorBindSourceDB1: TBindNavigator
    Left = 280
    Top = 24
    Width = 240
    Height = 25
    DataSource = BindSourceDB1
    Orientation = orHorizontal
    TabOrder = 1
  end
  object StringGrid1: TStringGrid
    Left = 168
    Top = 112
    Width = 320
    Height = 120
    ColCount = 1
    FixedCols = 0
    RowCount = 2
    TabOrder = 2
    ColWidths = (
      64)
    RowHeights = (
      24
      24)
  end
  object Button1: TButton
    Left = 208
    Top = 256
    Width = 75
    Height = 25
    Caption = 'Button1'
    TabOrder = 3
    OnClick = Button1Click
  end
  object Button2: TButton
    Left = 352
    Top = 256
    Width = 75
    Height = 25
    Caption = 'Button2'
    TabOrder = 4
  end
  object Button3: TButton
    Left = 464
    Top = 256
    Width = 75
    Height = 25
    Caption = 'Button3'
    TabOrder = 5
  end
  object BindSourceDB1: TBindSourceDB
    ScopeMappings = <>
    Left = 432
    Top = 128
  end
  object BindingsList1: TBindingsList
    Methods = <>
    OutputConverters = <>
    Left = 20
    Top = 5
    object LinkGridToDataSourceBindSourceDB1: TLinkGridToDataSource
      Category = 'Quick Bindings'
      DataSource = BindSourceDB1
      GridControl = StringGrid1
      Columns = <>
    end
    object LinkControlToField1: TLinkControlToField
      Category = 'Quick Bindings'
      FieldName = 'LastName'
      Control = Edit1
      Track = True
    end
  end
  object DataSource1: TDataSource
    Left = 496
    Top = 184
  end
  object FDGUIxWaitCursor1: TFDGUIxWaitCursor
    Provider = 'Forms'
    Left = 384
    Top = 64
  end
end

The datamodule is unremarkable. Simply a FDTable and FDconnection. I attached them to the dbDemos database. Note Unit1 does not use Unit2.

unit Unit2;

interface

uses
  System.SysUtils, System.Classes, FireDAC.Stan.Intf, FireDAC.Stan.Option,
  FireDAC.Stan.Param, FireDAC.Stan.Error, FireDAC.DatS, FireDAC.Phys.Intf,
  FireDAC.DApt.Intf, FireDAC.Stan.Async, FireDAC.DApt, FireDAC.UI.Intf,
  FireDAC.Stan.Def, FireDAC.Stan.Pool, FireDAC.Phys, FireDAC.Phys.MSAcc,
  FireDAC.Phys.MSAccDef, FireDAC.VCLUI.Wait, Data.DB, FireDAC.Comp.Client,
  FireDAC.Comp.DataSet;

type
  TDataModule2 = class(TDataModule)
    FDTable1: TFDTable;
    FDConnection1: TFDConnection;
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  DataModule2: TDataModule2;

implementation

{%CLASSGROUP 'Vcl.Controls.TControl'}

{$R *.dfm}

end.

Last, here is the program unit. Here is where I set the dataset property.

program Project1;

uses
  Vcl.Forms,
  Unit1 in 'Unit1.pas' {Form1},
  Unit2 in 'Unit2.pas' {DataModule2: TDataModule};

{$R *.res}

begin
  Application.Initialize;
  Application.MainFormOnTaskbar := True;
  Application.CreateForm(TDataModule2, DataModule2);
  Application.CreateForm(TForm1, Form1);

  Form1.Dataset := Datamodule2.FDTable1;   //Injecting the dataset property

  Application.Run;
end.