Pregunta Sincronización TListBox con TStringList


Tengo un hilo de fondo que envía mensajes al hilo principal que, a su vez, agrega los mensajes a un TListBox como un registro.

El tema es que este hilo de fondo es muy rápido y no necesito actualizar el registro tan rápido. Me gustaría agregar los mensajes a un TStringList y establecer un temporizador para actualizar el TListBox cada segundo más o menos.

He intentado usar:

listBox1.Items := StringList1;

o

listBox1.Items.Assign(StringList1);

En el evento OnTimer y funciona. La cosa es que nunca permite al usuario desplazarse o hacer clic en el cuadro de lista, porque se actualiza cada segundo.

Estoy usando Delphi XE4

¿Existe una forma más elegante de sincronizar el contenido del cuadro de lista con esta StringList de fondo (o cualquier otra lista si es necesario)? ¡Gracias de antemano!


5
2017-09-12 15:34


origen


Respuestas:


Usa un enfoque virtual

Selecciona el Style propiedad del ListBox a lbVirtual, y asignar la OnData evento para permitirle solicitar las cadenas necesarias para pintar el control, en lugar de ser el propietario de las cadenas, lo que restablece todo el control en cada actualización. Código ilustrativo:

unit Unit1;

interface

uses
  Windows, Messages, Classes, Controls, Forms, AppEvnts, StdCtrls, ExtCtrls;

type
  TForm1 = class(TForm)
    ApplicationEvents1: TApplicationEvents;
    ListBox1: TListBox;
    Timer1: TTimer;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure ListBox1Data(Control: TWinControl; Index: Integer;
      var Data: String);
    procedure ApplicationEvents1Idle(Sender: TObject; var Done: Boolean);
    procedure Timer1Timer(Sender: TObject);
  private
    FStrings: TStringList;
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
begin
  FStrings := TStringList.Create;
  FStrings.CommaText := 'A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z';
  ListBox1.Count := FStrings.Count;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  FStrings.Free;
end;

procedure TForm1.ListBox1Data(Control: TWinControl; Index: Integer;
  var Data: String);
begin
  Data := FStrings[Index];
end;

procedure TForm1.ApplicationEvents1Idle(Sender: TObject; var Done: Boolean);
begin
  FStrings[Random(FStrings.Count)] := Chr(65 + Random(26));
end;

procedure TForm1.Timer1Timer(Sender: TObject);
begin
  ListBox1.Invalidate;
end;

end.

En este ejemplo, uso el OnIdle evento de un TApplicationEvents componente para simular las actualizaciones de la lista StringList. Tenga en cuenta que ahora puede desplazarse y seleccionar elementos en el cuadro de lista, a pesar del intervalo de actualización de 1 segundo del temporizador.

Sincronizar el recuento de elementos

Los cambios en el recuento de elementos de StringList también deben reflejarse en el ListBox. Esto debe hacerse por ListBox1.Count := FStrings.Count, pero luego la apariencia de ListBox se restablecerá nuevamente. Por lo tanto, se necesita una solución para evitar temporalmente que se vuelva a dibujar / actualizar por completo:

procedure TForm1.Timer1Timer(Sender: TObject);
begin
  if Random(2) = 0 then
  begin
    FStrings.Add('A');
    SyncListCounts;
  end
  else
    ListBox1.Invalidate;
end;

procedure TForm1.SyncListCounts;
var
  SaveItemIndex: Integer;
  SaveTopIndex: Integer;
begin
  ListBox1.Items.BeginUpdate;
  try
    SaveItemIndex := ListBox1.ItemIndex;
    SaveTopIndex := ListBox1.TopIndex;
    ListBox1.Count := FStrings.Count;
    ListBox1.ItemIndex := SaveItemIndex;
    ListBox1.TopIndex := SaveTopIndex;
  finally
    ListBox1.Items.EndUpdate;
  end;
end;

11
2017-09-12 16:35