【问题标题】:How to make ProgressBar with PBS_MARQUEE style work?如何使带有 PBS_MARQUEE 样式的 ProgressBar 工作?
【发布时间】:2017-09-28 07:46:52
【问题描述】:

我知道如何使用 PBS_MARQUEE 样式创建一个有效的 ProgressBar,但是在我想要选取框动画的情况下,我无法实现它,只要一些 long_operation() 运行,而不必从 @ 连续调用 SendMessage(hPB, PBM_STEPIT, 0, 0); 987654323@推进动画。

这是我失败的尝试之一:

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 控件发送/接收。

我觉得一定有一种直观的方法可以做到这一点,但我想不通。

这是怎么回事?

【问题讨论】:

  • 不太确定 std::thread 是否曾被指责为直观。但这就是让你的 UI 线程能够更新栏所需要的。沙漏光标而不是进度条非常直观。
  • long_operation 卸载到工作线程上。这可以通过多种方式完成,例如通过使用 std::threadstd::async 或 Windows 的本机线程实现 (CreateThread/_beginthreadex)。请特别注意,从long_operation 调用SendMessage 仅实现部分解决方案。例如,当用户尝试与之交互时,它仍会阻止发送其他消息,从而导致对话框显示为挂起。
  • 发生的情况是您正在 UI 线程中工作,这导致 UI 无响应。通过在后台线程中完成工作来解决该问题。
  • 您可以直接触发并忘记线程,然后立即将控制权交还给对话管理器以发送消息。线程完成后,它可以将自定义消息 (WM_APP + x) 发布到对话窗口,或发送它,以防它需要传回数据。
  • @Asesh:这仅适用于更改某些样式(如边框)​​。此处不适用。

标签: c++ winapi controls progress-bar


【解决方案1】:

如果您希望保留一个线程,则需要定期发送消息,以便在未发现时可以移动或刷新窗口。

// 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);
}

这可以通过添加 Abort 按钮、在 ProgressDlgProc 中处理 WM_COMMAND、设置一些标志 (Dll_globals::g_Abort?) 并提前离开 long_operation 来增强。

使用多线程,您必须建立协议以开始工作(这取决于您选择的 API)和信号完成(这可以通过带有自定义消息的 PostMessage 或什至带有足够控制 ID 的 WM_COMMAND 来完成)。即使在这种情况下,当用户在完成之前的调用之前再次启动相同的操作时也要小心潜在的问题。

【讨论】:

  • 这个建议的解决方案会破坏对话框中的键盘导航。键盘导航由IsDialogMessage API 调用实现。
  • 是的,您是对的,我们使用的是无模式对话框,因此需要 IsDialogMessage 来处理 kayboard 导航。忘了它。更新了代码。
  • 这个解决方案的问题是它可能会在用户与 UI 交互时产生延迟。 long_operation 可能无法在足够短的时间间隔内调用PumpMessages()。即使是这样,如果从内部循环调用 PumpMessages(),它也可能会影响性能。
猜你喜欢
  • 1970-01-01
  • 2023-02-06
  • 2014-08-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-03-30
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多