如何在单独的线程中创建带有进度条的MFC对话框?

发布于 2024-08-10 01:33:09 字数 1855 浏览 2 评论 0原文

我的应用程序可能需要一段时间才能连接到数据库。此连接是通过单个库函数调用建立的,即我无法将进度更新放在那里并进行回调或类似的操作。

我的想法是在连接到数据库之前在单独的线程中创建一个带有进度条的对话框。此对话框将使用 CProgressCtrl::StepIt() 不断更改进度状态,以便用户看到正在发生的事情。
设置该对话框并执行其操作后,我想从主线程调用数据库连接函数。 连接功能完成后,我想停止进度条线程。

让我画一个图:

CMyApp::       ProgressThread
InitInstance()      .
    |               .
    |               .
    +-Create Dialog-+
    |               |
    |             Animate
 Connect          Progress
    to             Bar
    DB              |
    |               |
    +-Destroy Dlg---+
    |               .
    |               .

这可能吗?如果是,怎么办?

也许整个事情也可以使用计时器来完成。可能会简单得多,但我也无法让它发挥作用。

  1. 我知道 CProgressCtrl::SetMarquee() 可能完全符合我的需要,但我无法使用它,因为该应用程序不支持 Unicode。
  2. 我可以将数据库连接调用移至单独的线程中,但这样看起来会对代码进行大量更改,并对连接错误进行额外处理。

更新2
我按照 AlexEzh 和 Javier De Pedro 建议的方式工作:将数据库内容放入其自己的线程中。
最初我担心如何处理错误,但实际上它与以前非常相似。

  1. 在主线程中,我创建一个带有连接参数、结果标志和线程运行标志的结构。后者最初设置为true
  2. 我创建一个线程并将该结构作为参数传递。
  3. 我创建了一个在主线程中显示进度条的对话框。
  4. 另外,在主线程中,有一个循环在设置线程运行标志时运行。它调用CMyDialog::Animate(),后者调用CProgressCtrl::StepIt(),然后调用Sleep()
  5. 该线程执行数据库连接代码,并在完成后将运行标志设置为 false
  6. 当主线程退出循环时,它可以像以前一样处理错误。

缺点:将鼠标移动到窗口上不起作用。它是看不见的。因此,不能使用取消按钮或其他交互式对话框元素。不过,我可以忍受这一点。

既然您喜欢该图,那么它现在的样子如下:

CMyApp::        WorkerThread
InitInstance()      .
    |               .
    |               .
Create Dialog       .
    |               .
    +-Start Thread--+
    |               |
    |             Connect
 Animate            to
 Progress           DB
   Bar              |
    |               |
    +-Thread Ends---+
    |               .
 Destroy Dlg        .
    |               .

My application may take a while to connect to a database. This connection is made with a single library function call, i.e. I cannot put progress updates in there and make callbacks or something similar.

My idea was to create a dialog with a progress bar in a separate thread before connecting to the DB. This dialog will continually change the progress status with CProgressCtrl::StepIt() so the user sees something happening.
After that dialog is set up and doing its thing I want to call the DB connection function from the main thread.
After the connection function completed, I want to stop the progress bar thread.

Let me paint a picture:

CMyApp::       ProgressThread
InitInstance()      .
    |               .
    |               .
    +-Create Dialog-+
    |               |
    |             Animate
 Connect          Progress
    to             Bar
    DB              |
    |               |
    +-Destroy Dlg---+
    |               .
    |               .

Is that possible? If yes, how?

Maybe the whole thing would work using timers, too. Would probably be much simpler but I couldn't get that to work either.

  1. I am aware of CProgressCtrl::SetMarquee() which might do exactly what I need but I can't use it because the application does not have Unicode support.
  2. I could move the db connection call into a separate thread but that way it looks like a lot of changes to the code and extra handling of connection errors.

Update 2
I got it working the way AlexEzh and Javier De Pedro suggested: Put the DB stuf into its own thread.
initially I had concerns about how error handling could be done but it's actually quite similar to how it was before.

  1. In the main thread I create a struct with connection parameters, result flag and thread-running-flag. The latter is initially set to true.
  2. I create a thread and pass that struct as parameter.
  3. I create a dialog that displays a progress bar in the main thread.
  4. Also in the main thread there is a loop that runs while the thread-running-flag is set. It calls CMyDialog::Animate() which calls CProgressCtrl::StepIt() and then Sleep()s a bit.
  5. The thread executes the db-connection code and sets the running-flag to false when done.
  6. When the main thread exits the loop it can handle errors exactly as it did before.

Disadvantage: Moving the mouse over the window doesn't work. It's invisible. Thus no cancel-button or other interactive dialog elements can be used. I can live with that, however.

Since you liked the diagram, here is how it now looks like:

CMyApp::        WorkerThread
InitInstance()      .
    |               .
    |               .
Create Dialog       .
    |               .
    +-Start Thread--+
    |               |
    |             Connect
 Animate            to
 Progress           DB
   Bar              |
    |               |
    +-Thread Ends---+
    |               .
 Destroy Dlg        .
    |               .

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

念﹏祤嫣 2024-08-17 01:33:09

I hope this article about creating own-thread splash screen with progress bar could be helpful. I wrote it while solving the problem with thread locking at MFC message queue level.

那伤。 2024-08-17 01:33:09

将数据库连接逻辑移至单独的线程仍然会更安全。通过对话框线程上的数据库,您将能够重新绘制进度条,但不能重新绘制对话框中的其他控件。

It would still be safer to move the DB connection logic to the separate thread. With DB on the dialog thread, you will be able to repaint the progress bar but not other controls in the dialog.

情定在深秋 2024-08-17 01:33:09
  1. 使用 AfxBeginThread< 创建工作线程/a>.
  2. 在该线程中创建一个 CProgressCtrl 并调用创建,将对话框作为CProgressCtrl的父级传递,使用选取框样式作为进度控件。
  3. 在Thread中创建消息等待循环:

    MSG 味精;
    while(GetMessage(&Msg, NULL, 0, 0))
    {
    翻译消息(&msg);
    DispatchMessage(&msg);
    }

  4. 消息循环需要检查全局标志来判断是否退出循环。

  1. Create worker thread using AfxBeginThread.
  2. In that thread Create a CProgressCtrl and call Create, pass the dialog as the parent of the CProgressCtrl, use the marquee style for the progress control.
  3. In the Thread create a message waiting loop:

    MSG msg;
    while(GetMessage(&Msg, NULL, 0, 0))
    {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
    }

  4. The message loop need to check a global flag to see if to exit the loop.

沉溺在你眼里的海 2024-08-17 01:33:09

您是否尝试过将 SendMessagePBM_SETMARQUEE 一起使用,而不是 SetMarquee。我自己从未尝试过,但应该有效。

在我看来,实现您想要做的事情的最简单方法是在 ui 线程中建立 ProgressBar 和 DB 连接,并使用 OnTimer 在进度栏中调用 StepIt
您还可以在ui线程中创建进度条,并使用工作线程的自定义消息来修改进度状态。

不管怎样,我同意 AlexEzh 的观点,最好的方法是让整个非 UI 在工作线程中工作。

Have you tried to use SendMessage with PBM_SETMARQUEE instead of SetMarquee. I've never tried myself but it should work.

In my opinion the easiest way to achive what you want to do is making both the ProgressBar and DB connection in the ui thread and using OnTimer to call StepIt in the progress bar.
You can also create the progress bar in the ui thread and use a custom message for the working thread to modify the progress status.

Anyway, I agree with AlexEzh that the best way to do it is making the whole non-UI work in the working thread.

最偏执的依靠 2024-08-17 01:33:09

创建一个成员变量,

CProgressCtrl m_progress;

DoDataExcchange中的DDX_Control中添加m_progress,并使用进度条ID

在按钮单击功能下添加以下代码。

m_progress.setRange(0,100);
m_progress.SetPos(1);

Create a member variable as

CProgressCtrl m_progress;

add the m_progress in DDX_Control in DoDataExcchange with the progress bar ID

add the following code under Button click function.

m_progress.setRange(0,100);
m_progress.SetPos(1);
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文