How to create a MFC dialog with a progress bar in

2019-04-06 04:48发布

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        .
    |               .

5条回答
放我归山
2楼-- · 2019-04-06 05:09
  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.

查看更多
Deceive 欺骗
3楼-- · 2019-04-06 05:16

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.

查看更多
时光不老,我们不散
4楼-- · 2019-04-06 05:16

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);
查看更多
成全新的幸福
5楼-- · 2019-04-06 05:25

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.

查看更多
聊天终结者
6楼-- · 2019-04-06 05:30

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.

查看更多
登录 后发表回答