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