Delphi TListBox与TStringList同步

Delphi TListBox与TStringList同步,delphi,Delphi,我有一个后台线程将消息发送到主线程,主线程又将消息添加到TListBox中,就像日志一样 问题是,这个后台线程非常快,我真的不需要更新日志那么快。我想将消息添加到TStringList中,并设置一个计时器,每隔一秒左右更新TListBox 我试过使用: listBox1.Items := StringList1; 或 在OnTimer事件中,它可以正常工作。事实是,它从不让用户真正滚动或单击列表框,因为它每秒钟刷新一次 我使用的是Delphi XE4 是否有更优雅的方式将listbox的内容与

我有一个后台线程将消息发送到主线程,主线程又将消息添加到TListBox中,就像日志一样

问题是,这个后台线程非常快,我真的不需要更新日志那么快。我想将消息添加到TStringList中,并设置一个计时器,每隔一秒左右更新TListBox

我试过使用:

listBox1.Items := StringList1;

在OnTimer事件中,它可以正常工作。事实是,它从不让用户真正滚动或单击列表框,因为它每秒钟刷新一次

我使用的是Delphi XE4

是否有更优雅的方式将listbox的内容与此背景StringList(或任何其他必要的列表)同步?提前谢谢你

使用虚拟方法 将列表框的
Style
属性设置为
lbVirtual
,并分配
OnData
事件,让它请求绘制控件所需的字符串,而不是拥有在每次更新时重置整个控件的字符串。说明性代码:

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.
在本例中,我使用
TApplicationEvents
组件的
OnIdle
事件来模拟StringList的线程更新。请注意,现在您可以滚动并选择列表框中的项目,尽管计时器的更新间隔为1秒

同步项目计数 StringList项计数的更改也需要反映在ListBox中。这需要通过
ListBox1.Count:=FStrings.Count
完成,但随后将再次重置ListBox外观。因此,需要一个临时解决方案,暂时阻止它一起重新绘制/更新:

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;

什么Delphi版本?(新版本的功能会发生变化。)您是否有一些代码可以让您更清楚地了解您现在正在尝试的内容,但这些代码没有按照您希望的方式工作?使用虚拟列表视图难道我不能只使用ListBox1.BeginUpdate/EndUpdate而不使用SendMessages吗?嗯,
TListBox
没有这些例程,但是
Items
属性已被删除。虽然我没有考虑过,但现在看来
StdCtrls.TListBoxStrings.SetUpdateState
发送的是完全相同的
WM\u SETREDRAW
消息,我同意从语义上讲这是首选+1.
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;