我正在使用 aComboBox來顯示許多專案的串列(別擔心,專案之間的差異允許通過AutoComplete :-) 快速選擇。
此串列是在創建表單(OnCreate事件)期間創建的,但為了使表單在填充時不會凍結,我添加了一個TThread執行填充的操作TStringList,然后將其分配給ComboBox.Items.
它可以作業,但是當顯示表單時,直到執行緒完成并且 Combo 中的內容已經顯示后,它才會正確繪制。不應該使用執行緒保存這個嗎?
這是我的代碼:(它目前不是在 IDE 中撰寫的,所以可能有拼寫錯誤......)
type
TLoadList = class(TThread)
public
constructor Create;
destructor Destroy; override;
private
SL: TStrings;
procedure UpdateCombo;
procedure Execute; override;
end;
implementation
uses uMain, HebNumFunc; //The main form unit
constructor TLoadList.Create;
begin
FreeOnTerminate := True;
SL := TStringList.Create;
inherited Create(True);
end;
procedure TLoadList.UpdateCombo;
begin
MainFrm.YListCombo.Items := SL;
end;
procedure TLoadList.Execute;
begin
for var I: Integer := 1 to 6000 do SL.Add(HebNumber(I)); //HebNumber This is a function that converts the value of the number to a Hebrew alphabetic value
Synchronize(UpdateCombo);
end;
destructor TLoadList.Destroy;
begin
SL.Free;
inherited;
end;
HebNumber 函式在 HebNumFunc 單元中以這種方式宣告:
function HebNumber(const Number: Integer): string;
const
Letters1: Array of String = ['','?','?','?','?','?','?','?','?','?'];
Letters10: Array of String = ['','?','?','?','?','?','?','?','?','?'];
Letters100: Array of String = ['','?','?','?','?','??','??','??','??','???'];
function _ThousandsConv(const Value: Integer): string;
var
Input, Hundreds, Tens, Some: Integer;
begin
if Value <= 0 then Exit('');
if Value = 1 then Exit(Format('%s'' ', [Letters1[Value]]));
if Value in [2..9] then Exit(Format('%s"? ', [Letters1[Value]]));
if Value >= 10 then
begin
Input := Value;
Hundreds := Input div 100;
Input := Input mod 100;
Tens := Input div 10;
Some := Input mod 10;
Result := Format('%s%s%s"? ', [Letters100[Hundreds], Letters10[Tens], Letters1[Some]]);
end;
end;
var
Input, Thousands, Hundreds, Tens, Some: Integer;
begin
Input := Number;
Thousands := Input div 1000;
if Thousands > 999 then Exit('?????');
Input := Input mod 1000;
Hundreds := Input div 100;
Input := Input mod 100;
Tens := Input div 10;
Some := Input mod 10;
if (Thousands > 0) and (Hundreds Tens Some = 0) then
Exit(Format('%s???', [_ThousandsConv(Thousands - 1)]));
Result := Format('%s%s%s%s', [_ThousandsConv(Thousands),
Letters100[Hundreds], Letters10[Tens], Letters1[Some]]);
if Result.Contains('??') then Exit(Result.Replace('??', '??'));
if Result.Contains('??') then Result := Result.Replace('??', '??');
end;
我在 OnCreate 事件中這樣呼叫它:
var
LoadList: TLoadList;
begin
LoadList := TLoadList.Create;
LoadList.Start;
{...}
end;
除了呼叫創建和運行執行緒之外,沒有其他OnCreate事件會導致表單著色延遲。也沒有任何額外的操作在其中執行。
uj5u.com熱心網友回復:
您的代碼沒有任何問題。向 ComboBox 添加 6000 個專案非常慢,并且可能會干擾 Form 的初始繪制,因為向 ComboBox 添加專案也在主 (GUI) 執行緒中運行。
您不能在后臺執行緒中移動該分配,因為使用 GUI 控制元件必須在主執行緒中完成。
您看到的問題的發生是因為執行緒內的代碼運行得非常快,并且會在表單有機會正確繪制自身之前與主執行緒同步。
可能的解決方案很少:
因為后臺執行緒中的代碼運行速度很快,所以洗掉執行緒并直接在
OnCreate事件處理程式中填充 ComboBox。將代碼從
OnCreate移至OnShow事件處理程式,以便在更接近顯示表單的時間運行代碼。然而,這個事件可以被觸發多次,所以你需要額外的布爾欄位來填充 ComboBox 一次。
但是,這仍然不足以防止故障。為此,Sleep()在執行緒中添加一個以稍微減慢其執行速度并為 Form 提供一些時間來完成其初始繪制。然后你可以呼叫Synchronize()來填充ComboBox。
您可能需要對休眠期進行試驗,因為較慢的計算機將需要更多時間來完成表單的繪制,但不要休眠太久,因為在這種情況下用戶將能夠訪問空的 ComboBox。
procedure MainFrm.FormShow(Sender: TObject);
begin
if not FFilled then
begin
FFilled := True;
TThread.CreateAnonymousThread(
procedure
var
sl: TStringList;
begin
sl := TStringList.Create;
try
for var I: Integer := 1 to 6000 do
sl.Add(HebNumber(I));
// Sleep for some small amount of time before synchronizing
// with the main thread to allow form to fully paint itself
Sleep(100);
TThread.Synchronize(nil,
procedure
begin
YListCombo.Items := sl;
end);
finally
sl.Free;
end;
end).Start;
end;
end;
注意:我使用匿名執行緒來填充 ComboBox,因為這種方法不需要創建額外的類,并且代碼更簡單。但是,如果您不想,則不必更改代碼的那部分。
uj5u.com熱心網友回復:
感謝大家的回復和評論,對我幫助很大!
我最終使用的代碼(基于@Dalija Prasnikar 的回答)是這樣的:
procedure TMyForm.FillYListCombo;
var
SL: TStrings;
begin
SL := TStringList.Create;
try
for var I: Integer := 1 to 6000 do
SL.Add(HebNumber(I));
//Waiting for the form to finish loading...
WaitForInputIdle(GetCurrentProcess, 1000);
TThread.Synchronize(nil,
procedure
begin
YListCombo.Items.Assign(SL);
end);
finally
SL.Free;
end;
end;
procedure TMyForm.FormCreate(Sender: TObject);
begin
TThread.CreateAnonymousThread(FillYListCombo).Start;
{...}
end;
我不確定這是否是最好的方法,但對我來說效果很好!
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/536567.html
上一篇:OpenSSLSSL_CTX_NEW回傳“nullsslmethodpassed”
下一篇:OUT引數不符合檔案
