簡體   English   中英

在Delphi中取消IProgressDialog

[英]Cancel IProgressDialog in Delphi

我正在進行長時間的操作,並且想出了一種向用戶展示它的好方法是使用帶有IProgressDialog對象的系統進度對話框。

我只找到了幾個用法示例,這是我的實現。 我遇到的問題是應用程序仍然沒有響應(我知道我可能需要使用線程),但是“取消”按鈕根本不起作用(這可能是第一個問題的結果。)

我正在Windows 8.1下使用Delphi XE。

編輯 :我已經在評估HasUserCancelled之前添加了Application.ProcessMessages調用,但是它似乎沒有多大幫助(對話框仍然無法處理單擊“ 取消”按鈕的操作。)

var
  i, procesados: Integer;
  IDs: TList<Integer>;
  pd: IProgressDialog;
  tmpPtr: Pointer;
begin
    procesados := 0;
    try
      tmpPtr := nil;
      CoCreateInstance(CLSID_ProgressDialog, nil, CLSCTX_INPROC_SERVER,
        IProgressDialog, pd);
      // also seen as  pd := CreateComObject(CLSID_ProgressDialog) as IProgressDialog;

      pd.SetTitle('Please wait');
      pd.SetLine(1, PWideChar(WideString('Performing a long running operation')),
        false, tmpPtr);
      pd.SetAnimation(HInstance, 1001); // IDA_OPERATION_ANIMATION ?
      pd.Timer(PDTIMER_RESET, tmpPtr);
      pd.SetCancelMsg(PWideChar('Cancelled...'), tmpPtr);
      pd.StartProgressDialog(Handle, nil, PROGDLG_MODAL or
        PROGDLG_NOMINIMIZE, tmpPtr);

      pd.SetProgress(0, 100);
      IDs := GetIDs; // not relevant, returns List<Integer>
      try
        for i in IDs do
        begin
          try
            Application.ProcessMessages;
            if pd.HasUserCancelled then 
              Break;  // this never happens
            Inc(procesados);
            pd.SetProgress(procesados, IDs.Count);

            LongRunningOp(id);
          except
            // ?
          end;
        end;
      finally
        IDs.Free;
      end;
    finally
      pd.StopProgressDialog;
      // pd.Release; doesn't exist
    end;
  end;
end;

您將需要使用希望應用程序響應的線程。 取消按鈕不起作用的原因是循環中未處理消息。 將諸如Application.ProcessMessages之類的內容放入循環中將使其能夠響應單擊“取消”按鈕,但是線程仍然是更好的選擇。

您應該將帶有LongRunninOp(id)的循環放入線程中,然后使用Synchronize反饋到UI。 像這樣:

procedure TMyThread.Execute;
var
  i: Integer;
begin
  for i in IDs do
  begin
    try
      // If pd.HasUserCancelled is thread safe then this will work
      // if Terminated or pd.HasUserCancelled then 
      //   Break;
      // If pd.HasUserCancelled is not thread safe then you will need to do something like this
      Synchronize(
        procedure
        begin
          if pd.HasUserCancelled then 
            Terminate():
        end);
      if Terminated then 
        Break;
      Synchronize(
        procedure
        begin
          MainForm.pd.SetProgress(I, IDs.Count);             
        end);

      LongRunningOp(id);
    except
      // ?
    end;
  end;
end;

使用線程時,您需要確保您不會從主線程和后台線程訪問諸如ID之類的東西。 我也不喜歡MainForm.pd.SetProgress類型的調用,但是我把它放在這里告訴您發生了什么。 在您調用的主窗體上最好有一個方法。

在上面的代碼中,當從主線程調用MyThread.Terminate()時,對Terminated的檢查將返回true。 這是您應在事件處理程序中為“取消”按鈕添加的內容。 這表明線程應該關閉。 理想情況下,也應在LongRunningOp調用內進行此檢查,以防止在調用Terminate時延遲響應。

正如Remy所指出的,您可以使用線程OnTerminate事件告訴主表單線程何時完成,終止時間或線程結束時的結束時間。

我剛剛用以下代碼進行了測試,它可以按預期工作:

var
  iiProgressDialog: IProgressDialog;
  pNil: Pointer;
begin
  pNil := nil;
  iiProgressDialog := CreateComObject(CLASS_ProgressDialog) as IProgressDialog;
  iiProgressDialog.SetTitle('test');
  iiProgressDialog.StartProgressDialog( Handle, nil, PROGDLG_NOMINIMIZE, pNil);
  repeat
    Application.ProcessMessages;
  until iiProgressDialog.HasUserCancelled > 0;
  iiProgressDialog.StopProgressDialog;
end;

按取消將終止循環。 之所以看不到相同的效果,是因為LongRunningOp花費的時間太長。 在該例程中,沒有任何東西可以處理消息。 如果要在單個線程中運行,則也需要在該例程中定期調用Application.ProcessMessages。

以下組件也可以為您提供幫助:

http://www.bayden.com/delphi/iprogressdialog.htm

它將IProgressDialog包裝到Delphi組件中。 它創建一個監視取消單擊的線程,如果單擊該按鈕,則將觸發事件。

Windows通過在窗口之間傳遞消息來工作。 與某些操作系統不同,這些消息不是注入的,而是必須從消息隊列中輪詢的。 這是Application.ProcessMessages調用所做的。

這有點像合作多任務。 結果,在您的處理循環運行時,在該線程中創建並運行的任何窗口控件(如此對話框)都無法處理事件。

有兩種方法。 第一種是為您的處理創建一個線程,然后等待它返回。 安排起來可能有點復雜,並且可能需要檢查跨線程問題。

第二種方法是在處理循環中調用Application.ProcessMessages-這將強制對消息進行處理,並且您可以獲取click事件等。

但是 ,由於在循環內可能發生事件,因此可以在循環內銷毀您正在使用的對象。 例如,如果您的處理循環由於單擊按鈕而在表單上運行,並且用戶關閉該表單並銷毀了該表單(例如自動釋放表單),則該表單對象及其所有控件將不再有效對象-引用它們將導致內存訪問沖突和異常。 最簡單的解決方案是使用標志,並且永遠不要在處理循環中關閉表單。

無論哪種方式,您都有責任管理所有事物的生命周期,並且每種方法都有必須由您處理的問題。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM