美好的一天的人。首先,我不是英语母语,我可能会有一些语法错误。
我需要那些做过某些事情或类似我的应用程序的人的建议,好吧,问题是我正在使用我的delphi形式的TProgressBar,另一个名为“TExcelApplication”的组件和一个TDBGrid。
当我导出DBGrid的内容时,应用程序“冻结”,所以我基本上为用户放置了ProgressBar,以查看进程完成了多少。我已经意识到,当TDBGrid检索并将每行导出到新的Excel工作簿时,您无法移动实际的表单,因此您必须等到该过程完成才能移动该表单。
那么,是否有可能做某事(我考虑过线程,但我不确定他们是否可以提供帮助)所以用户可以根据需要移动窗口吗?
非常感谢你花时间阅读并给我一个建议。我正在使用Delphi XE。
这是我用来导出行的代码:
with ZQDetalles do
begin
First;
while not EOF do
begin
i := i + 1;
workSheet.Cells.Item[i,2] := DBGridDetalles.Fields[0].AsString;
workSheet.Cells.Item[i,3] := DBGridDetalles.Fields[1].AsString;
workSheet.Cells.Item[i,4] := DBGridDetalles.Fields[2].AsString;
workSheet.Cells.Item[i,5] := DBGridDetalles.Fields[3].AsString;
workSheet.Cells.Item[i,6] := DBGridDetalles.Fields[4].AsString;
workSheet.Cells.Item[i,7] := DBGridDetalles.Fields[5].AsString;
workSheet.Cells.Item[i,8] := DBGridDetalles.Fields[6].AsString;
workSheet.Cells.Item[i,9] := DBGridDetalles.Fields[7].AsString;
Next;
barraProgreso.StepIt;
end;
end;
如果您想查看“导出”按钮的完整代码,请随时看到此链接:http://pastebin.com/FFWAPdey
答案 0 :(得分:4)
每当您在使用GUI的应用程序中执行需要花费大量时间的事情时,您希望将其放在单独的线程中,以便用户仍然可以操作表单。您可以声明一个简单的线程:
TWorkingThread = class(TThread)
protected
procedure Execute; override;
procedure UpdateGui;
procedure TerminateNotify(Sender: TObject);
end;
procedure TWorkingThread.Execute;
begin
// do whatever you want to do
// make sure to use synchronize whenever you want to update gui:
Synchronize(UpdateGui);
end;
procedure TWorkingThread.UpdateGui;
begin
// e.g. updating the progress bar
end;
procedure TWorkingThread.TerminateNotify(Sender: TObject);
begin
// this gets executed when the work is done
// usually you want to give some kind of feedback to the user
end;
// ...
// calling the thread:
procedure TSettingsForm.Button1Click(Sender: TObject);
var WorkingThread: TWorkingThread;
begin
WorkingThread := TWorkingThread.Create(true);
WorkingThread.OnTerminate := TerminateNotify;
WorkingThread.FreeOnTerminate := true;
WorkingThread.Start;
end;
这很简单,记得在想要更新线程中的可视元素时始终使用Synchronize。通常,您还需要注意,当用户现在仍然能够使用GUI时,用户无法再次调用该线程。
答案 1 :(得分:4)
如果行数很小(而且你知道你有多少行),你可以使用变体变量数组更快地(并一次性)传输数据,如下所示:
var
xls, wb, Range: OLEVariant;
arrData: Variant;
RowCount, ColCount, i, j: Integer;
Bookmark: TBookmark;
begin
// Create variant array where we'll copy our data
// Note that getting RowCount can be slow on large datasets; if
// that's the case, it's better to do a separate query first to
// ask for COUNT(*) of rows matching your WHERE clause, and use
// that instead; then run the query that returns the actual rows,
// and use them in the loop itself
RowCount := DataSet1.RecordCount;
ColCount := DataSet1.FieldCount;
arrData := VarArrayCreate([1, RowCount, 1, ColCount], varVariant);
// Disconnect from visual controls
DataSet1.DisableControls;
try
// Save starting row so we can come back to it after
Bookmark := DataSet1.GetBookmark;
try
{fill array}
i := 1;
while not DataSet1.Eof do
begin
for j := 1 to ColCount do
arrData[i, j] := DataSet1.Fields[j-1, i-1].Value;
DataSet1.Next;
Inc(i);
// If we have a lot of rows, we can allow the UI to
// refresh every so often (here every 100 rows)
if (i mod 100) = 0 then
Application.ProcessMessages;
end;
finally
// Reset record pointer to start, and clean up
DataSet1.GotoBookmark;
DataSet1.FreeBookmark;
finally
// Reconnect GUI controls
DataSet1.EnableControls;
end;
// Initialize an instance of Excel - if you have one
// already, of course the next couple of lines aren't
// needed
xls := CreateOLEObject('Excel.Application');
// Create workbook - again, not needed if you have it.
// Just use ActiveWorkbook instead
wb := xls.Workbooks.Add;
// Retrieve the range where data must be placed. Again, your
// own WorkSheet and start of range instead of using 1,1 when
// needed.
Range := wb.WorkSheets[1].Range[wb.WorkSheets[1].Cells[1, 1],
wb.WorkSheets[1].Cells[RowCount, ColCount]];
// Copy data from allocated variant array to Excel in single shot
Range.Value := arrData;
// Show Excel with our data}
xls.Visible := True;
end;
循环遍历数据的行和列仍然需要相同的时间,但实际将数据传输到Excel所需的时间大大减少,特别是如果有大量数据的话。