2017-09-28 37 views
0

我知道如何創建一個帶有PBS_MARQUEE風格的工作ProgressBar,但是我只需要運行一些long_operation()而不需要從long_operation()連續調用SendMessage(hPB, PBM_STEPIT, 0, 0);來推進動畫,就可以實現它。如何使PBS_MARQUEE風格的ProgressBar工作?

這裏是我的失敗嘗試中的一種:

INT_PTR CALLBACK ProgressDlgProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { 
    switch(message) { 
     case WM_INITDIALOG: 
     { 
      HWND hProgressBar = GetDlgItem(hWnd, IDC_PROGRESS1);     
      LONG_PTR style_flags = GetWindowLongPtr(hProgressBar, GWL_STYLE); 
      SetWindowLongPtr(hProgressBar, GWL_STYLE, style_flags | PBS_MARQUEE); 

      SendMessage(hProgressBar, (UINT)PBM_SETMARQUEE, (WPARAM)1, (LPARAM)NULL); 

      break; 
     } 
    } 

    return FALSE; 
} 

void long_operation() { 
    for(int i = 0; i < 9; ++i) { 
     for(int j = 0; j < 99999999; ++j) 
     ; 
     Beep(5000, 100); 
    } 
} 

void do_operation() { 
    HWND hDlg = CreateDialog(Dll_globals::g_hInst, 
          MAKEINTRESOURCE(IDD_DIALOG4), // assume this contains a ProgressBar ctl 
          Dll_globals::g_hWndMain, ProgressDlgProc); 
    if(hDlg) { 
     ShowWindow(hDlg, SW_SHOW); 
     UpdateWindow(hDlg); 
    } 

    long_operation(); 
} 

我得到與上面的代碼是一個大帳篷進度條沒有任何動畫,同時繼續發出蜂鳴聲,然後一個正常的動畫字幕時停止。

據我所知,由於long_operation()阻塞了線程,消息隊列也被阻塞,並且默認的30ms更新消息不會被髮送到ProgressBar控件或被ProgressBar控件接收。

我覺得必須有一種直觀的方式來做到這一點,但我無法弄清楚。

這是怎麼回事?

+0

不太確定std :: thread是否曾被指責爲直覺。但是,這是保持你的UI線程能夠更新欄的原因。沙漏光標而不是進度條非常直觀。 –

+2

將'long_operation'卸載到工作線程上。這可以通過多種方式完成,例如,通過使用'std :: thread','std :: async'或Windows本地線程實現('CreateThread' /'_beginthreadex')。特別要注意的是,調用'long_operation'中的'SendMessage'只能實現部分解決方案。例如,當用戶嘗試與其交互時,它仍然阻止分派其他消息,導致對話框出現掛起。 – IInspectable

+0

發生了什麼事是您正在UI線程中工作,導致UI無響應。通過在後臺線程中完成工作來解決該問題。 –

回答

0

如果您希望保留一個線程,您需要定期泵送消息,以便在發現窗口時可以移動或刷新窗口。

// Process all queued messages. Handle keyboard dialog nawigation 
// for dialog_hwnd. For more then one modeless dialog modify code by 
// calling IsDialogMessage for each dialog. 
void PumpMesages(HWND dialog_hwnd) { 
    MSG msg; 
    while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE) != 0) { 
     // Handling modeless dialog nawigation. 
     if (dialog_hwnd && IsDialogMessage(dialog_hwnd)) 
      continue; 

     TranslateMessage(&msg); 
     DispatchMessage(&msg); 
    } 
} 

void long_operation(HWND dialog_hwnd, HWND progress_hwnd) { 
    for(int i = 0; i < 9; ++i) { 
     for(int j = 0; j < 99999999; ++j) 
     ; 

     // I'm not shure if this is needed. 
     // I have always used propper progress bar (PBM_SETPOS). 
     SendMessage(progress_hwnd, PBM_STEPIT, 0, 0); 
     // Periodically process messages. 
     PumpMessages(dialog_hwnd); 

     Beep(5000, 100); 
    } 
} 

void do_operation() { 
    HWND hDlg = CreateDialog(Dll_globals::g_hInst, 
          MAKEINTRESOURCE(IDD_DIALOG4), // assume this contains a ProgressBar ctl 
          Dll_globals::g_hWndMain, ProgressDlgProc); 
    if(hDlg==0) 
     return; 

    ShowWindow(hDlg, SW_SHOW); 

    // Disable input processing in main window if there is possibility 
    // of recursion. For example, user select Open file, we begin 
    // loading and inside PumpMessages, user can again select Open 
    // file so we will end with two progress dialogs and nested 
    // message loops. 
    EnableWindow(Dll_globals::g_hWndMain,FALSE); 

     long_operation(hDlg, GetDlgItem(hDlg, progress_bar_id_here)); 

    // Remember to enable input processing in main window. 
    EnableWindow(Dll_globals::g_hWndMain,TRUE); 
} 

這可以通過將中止按鈕,在ProgressDlgProc處理WM_COMMAND,設置一些標誌(Dll_globals :: g_Abort?)和早退long_operation得到增強。你必須建立開始工作的協議(這取決於你選擇哪個API)和信號完成(這可以通過帶有自定義消息的PostMessage或者具有足夠的控制ID的WM_COMMAND來完成)。即使在這種情況下,當用戶在先前的呼叫完成之前再次開始相同的操作時,請注意潛在的問題。

+0

這個建議的解決方案打破了對話框中的鍵盤導航。鍵盤導航由[IsDialogMessage](https://msdn.microsoft.com/en-us/library/windows/desktop/ms645498.aspx)API調用實現。 – IInspectable

+0

是的你是對的,我們正在使用無模式對話框,因此需要IsDialogMessage來處理滑板導航。忘了它。更新的代碼。 –

+0

該解決方案的問題在於,當用戶與UI進行交互時,它可能會產生滯後。 'long_operation'可能無法在足夠短的intervalls中調用'PumpMessages()'。即使如此,如果從內部循環調用PumpMessages(),它可能會影響性能。 – zett42