如何刷新 TListView 和 TFDMemTable 的 Livebinding?

How to refresh Livebinding for TListView and TFDMemTable?

我有一个与 TFDMemTable 实时绑定的 TListView。我还有一个 TButton,它将项目添加到 TFDMemTable 上,添加项目后,它显然显示在 TListView 中。 TListView 位于 TTabControl 的 TTabItem 之一。

我的问题是,当我在运行时更改选项卡并返回 TListView 选项卡添加更多项目时,添加更多项目后之前显示的数据将变为空。

我可以确认 TFDMemTable 中的数据仍然完好无损,包括新添加的数据。

我怀疑需要刷新实时绑定才能将所有数据返回到 TListView。

有谁知道如何在运行时刷新实时绑定?

P.S。我希望上面清楚地解释了我的问题。否则,如果您需要更多详细信息,请告诉我。

更新 1:最小可重现示例

这是我能为 MRE 做的最少的事情,不是我的案例的确切情况,但应该是同一个问题。您会注意到在 TabItem2 上添加列表后返回 TabItem1 在列表上添加更多项目,列表上的现有详细信息将消失。

FMX 程序

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 文件

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

错误在于我在FDMemTable (mtb3) 中创建的LookUp 字段。我避开了那些。相反,我创建了一个普通的数据字段并直接从 TListView (lsv2) 中获取数据,如下所示:

FMX 程序

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 文件

  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