我们应用程序中的某个表单显示模型的图形视图。用户可以在大量其他内容中启动模型转换,这可能需要相当长的时间。这种转换有时会在没有任何用户交互的情况下进行,有时则需要频繁的用户输入。除非需要用户输入,否则应禁用 UI(仅显示进度对话框)。
可能的方法:
- 忽略这个问题,只需将转换代码放在一个过程中并调用它。不好,因为在转换需要一些时间但不需要用户输入的情况下,应用程序似乎挂起。
- 在代码中添加回调:这很突兀——你必须在转换代码中放置很多这样的调用——而且是不可预测的——你永远无法确定你找到了正确的位置。
- 用 Application.ProcessMessages 撒上代码:与回调相同的问题。此外,您还会遇到 ProcessMessages 的所有问题。
- 使用线程:这使我们摆脱了 2. 和 3 的“突兀和不可预测”部分。但是,由于用户输入需要“编组”,因此需要做很多工作 - 调用 Synchronize,将任何需要的参数放入量身定制的记录等。调试也是一场噩梦,而且容易出错。
//编辑:我们当前的解决方案是线程。然而,由于用户输入,a** 很痛苦。在很多例程中可能会有很多输入代码。这让我感觉线程不是正确的解决方案。
我要让自己难堪,并发布我生成的 GUI 和工作代码的邪恶组合的大纲:
type
// Helper type to get the parameters into the Synchronize'd routine:
PGetSomeUserInputInfo = ^TGetSomeUserInputInfo;
TGetSomeUserInputInfo = record
FMyModelForm: TMyModelForm;
FModel: TMyModel;
// lots of in- and output parameters
FResult: Boolean;
end;
{ TMyThread }
function TMyThread.GetSomeUserInput(AMyModelForm: TMyModelForm;
AModel: TMyModel; (* the same parameters as in TGetSomeUserInputInfo *)): Boolean;
var
GSUII: TGetSomeUserInputInfo;
begin
GSUII.FMyModelForm := AMyModelForm;
GSUII.FModel := AModel;
// Set the input parameters in GSUII
FpCallbackParams := @GSUII; // FpCallbackParams is a Pointer field in TMyThread
Synchronize(DelegateGetSomeUserInput);
// Read the output parameters from GSUII
Result := GSUII.FResult;
end;
procedure TMyThread.DelegateGetSomeUserInput;
begin
with PGetSomeUserInputInfo(FpCallbackParams)^ do
FResult := FMyModelForm.DoGetSomeUserInput(FModel, (* the params go here *));
end;
{ TMyModelForm }
function TMyModelForm.DoGetSomeUserInput(Sender: TMyModel; (* and here *)): Boolean;
begin
// Show the dialog
end;
function TMyModelForm.GetSomeUserInput(Sender: TMyModel; (* the params again *)): Boolean;
begin
// The input can be necessary in different situations - some within a thread, some not.
if Assigned(FMyThread) then
Result := FMyThread.GetSomeUserInput(Self, Sender, (* the params *))
else
Result := DoGetSomeUserInput(Sender, (* the params *));
end;
你有什么意见吗?