delphifiremonkeylivebindingstlistviewfdmemtable

How to refresh Livebinding for TListView and TFDMemTable?


I have a TListView livebinded with TFDMemTable. I also have a TButton that adds the item on the TFDMemTable which obviously shown in the TListView after adding the item. The TListView is located in one of the TTabItem of TTabControl.

My problem is, when I changed the tab at runtime and go back to TListView tab to add more item, the previously shown data will become empty after adding more item.

I can confirm that the data in the TFDMemTable are still intact including the newly added ones.

I suspect the livebinding needs to be refreshed in order to get all the data back to TListView.

Does anyone have any idea on how to refresh the livebinding at runtime?

P.S. I hope the above explains my issue clearly. Otherwise, please let me know if you need more details.

UPDATE 1: MINIMUM REPRODUCIBLE EXAMPLE

Here's the least that I can do for the MRE, not the exact scenario of my case but should be the same issue. You will come to notice after adding list on the TabItem2 and you go back to TabItem1 to add more item on the lists, the existing detail on the list will be gone.

FMX Procedure

unit TabbedFormwithNavigation;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
  FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.TabControl, FMX.StdCtrls, FMX.Controls.Presentation,
  FMX.Gestures, System.Actions, FMX.ActnList, FMX.ListView.Types,
  FMX.ListView.Appearances, FMX.ListView.Adapters.Base, REST.Types,
  FireDAC.Stan.Intf, FireDAC.Stan.Option, FireDAC.Stan.Param,
  FireDAC.Stan.Error, FireDAC.DatS, FireDAC.Phys.Intf, FireDAC.DApt.Intf,
  System.Rtti, System.Bindings.Outputs, Fmx.Bind.Editors, Data.Bind.EngExt,
  Fmx.Bind.DBEngExt, Data.Bind.Components, Data.Bind.DBScope, Data.DB,
  FireDAC.Comp.DataSet, FireDAC.Comp.Client, REST.Response.Adapter, REST.Client,
  Data.Bind.ObjectScope, FMX.ListView;

type
  TTabbedwithNavigationForm = class(TForm)
    TabControl1: TTabControl;
    TabItem1: TTabItem;
    TabItem2: TTabItem;
    GestureManager1: TGestureManager;
    ActionList1: TActionList;
    NextTabAction1: TNextTabAction;
    PreviousTabAction1: TPreviousTabAction;
    lsv1: TListView;
    rsc1: TRESTClient;
    rsq1: TRESTRequest;
    rsp1: TRESTResponse;
    rsd1: TRESTResponseDataSetAdapter;
    mtb1: TFDMemTable;
    bdr1: TBindSourceDB;
    bdl1: TBindingsList;
    lsv2: TListView;
    rsc2: TRESTClient;
    rsq2: TRESTRequest;
    rsp2: TRESTResponse;
    rsd2: TRESTResponseDataSetAdapter;
    mtb2: TFDMemTable;
    bdr2: TBindSourceDB;
    lcf1: TLinkListControlToField;
    btn1: TButton;
    lsv3: TListView;
    mtb3: TFDMemTable;
    strngfldmtb3brandname: TStringField;
    strngfldmtb3brand: TStringField;
    bdr3: TBindSourceDB;
    lcf3: TLinkListControlToField;
    lcf2: TLinkListControlToField;
    pnl1: TPanel;
    lbl1: TLabel;
    pnl2: TPanel;
    lbl2: TLabel;
    pnl3: TPanel;
    lbl3: TLabel;
    procedure GestureDone(Sender: TObject; const EventInfo: TGestureEventInfo; var Handled: Boolean);
    procedure FormCreate(Sender: TObject);
    procedure FormKeyUp(Sender: TObject; var Key: Word; var KeyChar: Char; Shift: TShiftState);
    procedure lsv1ItemClick(const Sender: TObject; const AItem: TListViewItem);
    procedure btn1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  TabbedwithNavigationForm: TTabbedwithNavigationForm;

implementation

{$R *.fmx}

procedure TTabbedwithNavigationForm.btn1Click(Sender: TObject);
var
  brandname : string;
begin
  brandname := mtb2.Lookup('name', lsv2.Items[lsv2.ItemIndex].Text, 'name');
//  ShowMessage(lsv2.Items[lsv2.ItemIndex].Text);
  if mtb3.Locate('brandname', brandname, []) = False then
  begin
    mtb3.DisableControls;
    mtb3.Append;
    mtb3.FieldByName('brandname').AsString := brandname;
    mtb3.EnableControls;
    mtb3.Post;
  end;
end;

procedure TTabbedwithNavigationForm.FormCreate(Sender: TObject);
begin
  { This defines the default active tab at runtime }
  TabControl1.ActiveTab := TabItem1;

  rsq1.Execute;
end;

procedure TTabbedwithNavigationForm.FormKeyUp(Sender: TObject; var Key: Word; var KeyChar: Char; Shift: TShiftState);
begin
  if Key = vkHardwareBack then
  begin
    if (TabControl1.ActiveTab = TabItem1) then
    begin
      Key := 0;
    end;
  end;
end;

procedure TTabbedwithNavigationForm.GestureDone(Sender: TObject; const EventInfo: TGestureEventInfo; var Handled: Boolean);
begin
  case EventInfo.GestureID of
    sgiLeft:
      begin
        if TabControl1.ActiveTab <> TabControl1.Tabs[TabControl1.TabCount - 1] then
          TabControl1.ActiveTab := TabControl1.Tabs[TabControl1.TabIndex + 1];
        Handled := True;
      end;

    sgiRight:
      begin
        if TabControl1.ActiveTab <> TabControl1.Tabs[0] then
          TabControl1.ActiveTab := TabControl1.Tabs[TabControl1.TabIndex - 1];
        Handled := True;
      end;
  end;
end;

procedure TTabbedwithNavigationForm.lsv1ItemClick(const Sender: TObject;
  const AItem: TListViewItem);
var
  SearchItem : String;
begin
  //place the equivalent api for the meta click
  SearchItem := lsv1.Items[lsv1.ItemIndex].Text;
  rsc2.BaseURL := 'https://nm5c906csg.execute-api.ap-southeast-1.amazonaws.com/v0/dbqueries?search-item=' + SearchItem;

  //execute api request for the searches
  rsq2.Execute;
  TabControl1.TabIndex := 1;
end;

end.

FMX File

object TabbedwithNavigationForm: TTabbedwithNavigationForm
  Left = 0
  Top = 0
  Caption = 'Form56'
  ClientHeight = 596
  ClientWidth = 405
  FormFactor.Width = 320
  FormFactor.Height = 480
  FormFactor.Devices = [Desktop]
  OnCreate = FormCreate
  OnKeyUp = FormKeyUp
  DesignerMasterStyle = 0
  object TabControl1: TTabControl
    Touch.GestureManager = GestureManager1
    OnGesture = GestureDone
    Align = Client
    FullSize = True
    Size.Width = 405.000000000000000000
    Size.Height = 596.000000000000000000
    Size.PlatformDefault = False
    TabHeight = 49.000000000000000000
    TabIndex = 1
    TabOrder = 0
    TabPosition = PlatformDefault
    Sizes = (
      405s
      547s
      405s
      547s)
    object TabItem1: TTabItem
      CustomIcon = <
        item
        end>
      IsSelected = False
      Size.Width = 201.000000000000000000
      Size.Height = 49.000000000000000000
      Size.PlatformDefault = False
      StyleLookup = 'tabitemfavorites'
      TabOrder = 0
      Text = 'TabItem1'
      ExplicitSize.cx = 101.000000000000000000
      ExplicitSize.cy = 49.000000000000000000
      object lsv1: TListView
        ItemAppearanceClassName = 'TListItemAppearance'
        ItemEditAppearanceClassName = 'TListItemShowCheckAppearance'
        HeaderAppearanceClassName = 'TListHeaderObjects'
        FooterAppearanceClassName = 'TListHeaderObjects'
        ItemIndex = 0
        Align = Client
        Size.Width = 405.000000000000000000
        Size.Height = 487.000000000000000000
        Size.PlatformDefault = False
        TabOrder = 0
        OnItemClick = lsv1ItemClick
      end
      object pnl1: TPanel
        Align = Top
        Size.Width = 405.000000000000000000
        Size.Height = 60.000000000000000000
        Size.PlatformDefault = False
        TabOrder = 1
        object lbl1: TLabel
          Align = Client
          StyledSettings = [Family, FontColor]
          Margins.Left = 60.000000000000000000
          Margins.Right = 60.000000000000000000
          Size.Width = 285.000000000000000000
          Size.Height = 60.000000000000000000
          Size.PlatformDefault = False
          TextSettings.Font.Size = 14.000000000000000000
          TextSettings.Font.StyleExt = {00070000000000000004000000}
          TextSettings.HorzAlign = Center
          Text = 'Please select an item here to filter out items for TabItem2.'
          TabOrder = 0
        end
      end
    end
    object TabItem2: TTabItem
      CustomIcon = <
        item
        end>
      IsSelected = True
      Size.Width = 202.000000000000000000
      Size.Height = 49.000000000000000000
      Size.PlatformDefault = False
      StyleLookup = 'tabitemcontacts'
      TabOrder = 0
      Text = 'TabItem2'
      ExplicitSize.cx = 102.000000000000000000
      ExplicitSize.cy = 49.000000000000000000
      object lsv2: TListView
        ItemAppearanceClassName = 'TImageListItemBottomDetailAppearance'
        ItemEditAppearanceClassName = 'TImageListItemBottomDetailShowCheckAppearance'
        HeaderAppearanceClassName = 'TListHeaderObjects'
        FooterAppearanceClassName = 'TListHeaderObjects'
        ItemIndex = 0
        Align = Top
        Position.Y = 60.000000000000000000
        Size.Width = 405.000000000000000000
        Size.Height = 221.000000000000000000
        Size.PlatformDefault = False
        TabOrder = 0
      end
      object btn1: TButton
        Align = Top
        StyledSettings = [Family, FontColor]
        Position.Y = 281.000000000000000000
        Size.Width = 405.000000000000000000
        Size.Height = 40.000000000000000000
        Size.PlatformDefault = False
        TabOrder = 1
        Text = 'SELECT'
        TextSettings.Font.Size = 14.000000000000000000
        TextSettings.Font.StyleExt = {00070000000000000004000000}
        OnClick = btn1Click
      end
      object lsv3: TListView
        ItemAppearanceClassName = 'TImageListItemBottomDetailAppearance'
        ItemEditAppearanceClassName = 'TImageListItemBottomDetailShowCheckAppearance'
        HeaderAppearanceClassName = 'TListHeaderObjects'
        FooterAppearanceClassName = 'TListHeaderObjects'
        Align = Client
        Size.Width = 405.000000000000000000
        Size.Height = 166.000000000000000000
        Size.PlatformDefault = False
        TabOrder = 2
      end
      object pnl2: TPanel
        Align = Top
        Size.Width = 405.000000000000000000
        Size.Height = 60.000000000000000000
        Size.PlatformDefault = False
        TabOrder = 3
        object lbl2: TLabel
          Align = Client
          StyledSettings = [Family, FontColor]
          Margins.Left = 30.000000000000000000
          Margins.Right = 30.000000000000000000
          Size.Width = 345.000000000000000000
          Size.Height = 60.000000000000000000
          Size.PlatformDefault = False
          TextSettings.Font.Size = 14.000000000000000000
          TextSettings.Font.StyleExt = {00070000000000000004000000}
          TextSettings.HorzAlign = Center
          Text = 
            'Select an item below then click the "SELECT" button to list down' +
            ' the items selected.'
          TabOrder = 0
        end
      end
      object pnl3: TPanel
        Align = Bottom
        Position.Y = 487.000000000000000000
        Size.Width = 405.000000000000000000
        Size.Height = 60.000000000000000000
        Size.PlatformDefault = False
        TabOrder = 4
        object lbl3: TLabel
          Align = Client
          StyledSettings = [Family]
          Size.Width = 405.000000000000000000
          Size.Height = 60.000000000000000000
          Size.PlatformDefault = False
          TextSettings.Font.StyleExt = {00070000000000000004000000}
          TextSettings.FontColor = claRed
          TextSettings.HorzAlign = Center
          Text = 
            'The error comes when you go back to TabItem1 and select another ' +
            'item, the existing details on the list will be gone.'
          TabOrder = 0
        end
      end
    end
  end
  object GestureManager1: TGestureManager
    Sensitivity = 80.000000000000000000
    Left = 48
    Top = 185
    GestureData = <
      item
        Control = TabControl1
        Collection = <
          item
            GestureID = sgiLeft
          end
          item
            GestureID = sgiRight
          end>
      end>
  end
  object ActionList1: TActionList
    Left = 48
    Top = 120
    object NextTabAction1: TNextTabAction
      Category = 'Tab'
    end
    object PreviousTabAction1: TPreviousTabAction
      Category = 'Tab'
    end
  end
  object rsc1: TRESTClient
    Accept = 'application/json, text/plain; q=0.9, text/html;q=0.8,'
    AcceptCharset = 'utf-8, *;q=0.8'
    BaseURL = 
      'https://bs3winlz02.execute-api.ap-southeast-1.amazonaws.com/v0/d' +
      'bqueries'
    Params = <>
    Left = 136
    Top = 120
  end
  object rsq1: TRESTRequest
    Client = rsc1
    Params = <>
    Response = rsp1
    SynchronizedEvents = False
    Left = 136
    Top = 184
  end
  object rsp1: TRESTResponse
    ContentType = 'application/json'
    Left = 136
    Top = 248
  end
  object rsd1: TRESTResponseDataSetAdapter
    Active = True
    Dataset = mtb1
    FieldDefs = <>
    Response = rsp1
    Left = 136
    Top = 312
  end
  object mtb1: TFDMemTable
    Active = True
    FieldDefs = <
      item
        Name = 'meta'
        DataType = ftWideString
        Size = 255
      end>
    IndexDefs = <>
    FetchOptions.AssignedValues = [evMode]
    FetchOptions.Mode = fmAll
    ResourceOptions.AssignedValues = [rvSilentMode]
    ResourceOptions.SilentMode = True
    UpdateOptions.AssignedValues = [uvCheckRequired, uvAutoCommitUpdates]
    UpdateOptions.CheckRequired = False
    UpdateOptions.AutoCommitUpdates = True
    StoreDefs = True
    Left = 136
    Top = 376
  end
  object bdr1: TBindSourceDB
    DataSet = mtb1
    ScopeMappings = <>
    Left = 136
    Top = 440
  end
  object bdl1: TBindingsList
    Methods = <>
    OutputConverters = <>
    Left = 20
    Top = 5
    object lcf1: TLinkListControlToField
      Category = 'Quick Bindings'
      DataSource = bdr1
      FieldName = 'meta'
      Control = lsv1
      FillExpressions = <>
      FillHeaderExpressions = <>
      FillBreakGroups = <>
    end
    object lcf3: TLinkListControlToField
      Category = 'Quick Bindings'
      DataSource = bdr3
      FieldName = 'brandname'
      Control = lsv3
      FillExpressions = <
        item
          SourceMemberName = 'brand'
          ControlMemberName = 'Detail'
        end>
      FillHeaderExpressions = <>
      FillBreakGroups = <>
    end
    object lcf2: TLinkListControlToField
      Category = 'Quick Bindings'
      DataSource = bdr2
      FieldName = 'name'
      Control = lsv2
      FillExpressions = <
        item
          SourceMemberName = 'brand'
          ControlMemberName = 'Detail'
        end>
      FillHeaderExpressions = <>
      FillBreakGroups = <>
    end
  end
  object rsc2: TRESTClient
    Accept = 'application/json, text/plain; q=0.9, text/html;q=0.8,'
    AcceptCharset = 'utf-8, *;q=0.8'
    BaseURL = 
      'https://nm5c906csg.execute-api.ap-southeast-1.amazonaws.com/v0/d' +
      'bqueries?search-item=sage'
    Params = <>
    Left = 200
    Top = 120
  end
  object rsq2: TRESTRequest
    Client = rsc2
    Params = <>
    Response = rsp2
    SynchronizedEvents = False
    Left = 200
    Top = 184
  end
  object rsp2: TRESTResponse
    ContentType = 'application/json'
    Left = 200
    Top = 248
  end
  object rsd2: TRESTResponseDataSetAdapter
    Active = True
    Dataset = mtb2
    FieldDefs = <>
    Response = rsp2
    Left = 200
    Top = 312
  end
  object mtb2: TFDMemTable
    Active = True
    FieldDefs = <
      item
        Name = 'brand'
        DataType = ftWideString
        Size = 255
      end
      item
        Name = 'name'
        DataType = ftWideString
        Size = 255
      end
      item
        Name = 'description'
        DataType = ftWideString
        Size = 255
      end>
    IndexDefs = <>
    FetchOptions.AssignedValues = [evMode]
    FetchOptions.Mode = fmAll
    ResourceOptions.AssignedValues = [rvSilentMode]
    ResourceOptions.SilentMode = True
    UpdateOptions.AssignedValues = [uvCheckRequired, uvAutoCommitUpdates]
    UpdateOptions.CheckRequired = False
    UpdateOptions.AutoCommitUpdates = True
    StoreDefs = True
    Left = 200
    Top = 376
  end
  object bdr2: TBindSourceDB
    DataSet = mtb2
    ScopeMappings = <>
    Left = 200
    Top = 440
  end
  object mtb3: TFDMemTable
    Active = True
    FetchOptions.AssignedValues = [evMode]
    FetchOptions.Mode = fmAll
    ResourceOptions.AssignedValues = [rvSilentMode]
    ResourceOptions.SilentMode = True
    UpdateOptions.AssignedValues = [uvCheckRequired, uvAutoCommitUpdates]
    UpdateOptions.CheckRequired = False
    UpdateOptions.AutoCommitUpdates = True
    Left = 264
    Top = 376
    object strngfldmtb3brandname: TStringField
      FieldName = 'brandname'
      Size = 200
    end
    object strngfldmtb3brand: TStringField
      FieldKind = fkLookup
      FieldName = 'brand'
      LookupDataSet = mtb2
      LookupKeyFields = 'name'
      LookupResultField = 'brand'
      KeyFields = 'brandname'
      Size = 200
      Lookup = True
    end
  end
  object bdr3: TBindSourceDB
    DataSet = mtb3
    ScopeMappings = <>
    Left = 264
    Top = 440
  end
end

Solution

  • The error lies on the LookUp field I created in the FDMemTable (mtb3). I avoided those. Instead, I create a normal data field and directly took the data from TListView (lsv2) as shown below:

    FMX Procedure

    procedure TTabbedwithNavigationForm.btn1Click(Sender: TObject);
    var
      brandname, brand : string;
    begin
      brandname := mtb2.Lookup('name', lsv2.Items[lsv2.ItemIndex].Text, 'name');
      brand := mtb2.Lookup('name', lsv2.Items[lsv2.ItemIndex].Text, 'brand');
    
      if mtb3.Locate('brandname', brandname, []) = False then
      begin
        mtb3.DisableControls;
        mtb3.Append;
        mtb3.FieldByName('brandname').AsString := brandname;
        mtb3.FieldByName('brand').AsString := brand;           //manually coded instead of lookup field in the fdmemtable (mtb3)
        mtb3.EnableControls;
        mtb3.Post;
      end;
    end;
    

    FMX File

      object mtb3: TFDMemTable
        Active = True
        FieldDefs = <
          item
            Name = 'brandname'
            DataType = ftString
            Size = 200
          end
          item
            Name = 'brand'
            DataType = ftString        // defined as data instead of lookup
            Size = 100
          end>
        IndexDefs = <>
        FetchOptions.AssignedValues = [evMode]
        FetchOptions.Mode = fmAll
        ResourceOptions.AssignedValues = [rvSilentMode]
        ResourceOptions.SilentMode = True
        UpdateOptions.AssignedValues = [uvCheckRequired, uvAutoCommitUpdates]
        UpdateOptions.CheckRequired = False
        UpdateOptions.AutoCommitUpdates = True
        StoreDefs = True
        Left = 264
        Top = 376
        object strngfldmtb3brandname: TStringField
          FieldName = 'brandname'
          Size = 200
        end
        object strngfldmtb3brand: TStringField
          FieldName = 'brand'
          Size = 100
        end
      end