MFC SDI app: do work automatically after UI is shown - mfc

I have an existing (legacy) SDI app, and I want to call some functions immediately after the UI is shown, i.e. when it would normally sit idle and wait for user input.
Normally I'd put the calls in CView::OnInitialUpdate, e.g.:
CMyView::OnInitialUpdate()
{
CView::OnInitialUpdate();
//...
LoadStartupData(); //<-- this is what I want to call
}
... but this gets executed before the UI is shown. For this I want to show some UI feedback while it runs (it takes a while and should probably be in its own thread, but that's a different problem); the function also shows a dialog at the end of it, so I do need the UI to be visible.
What's the "best practice" on how to do this?

Use PostMessage in your init code with a WM_APP+x message.
When the message pump runs your message is delivered to the window and you can do your lazy init.
Use a ON_MESSAGE(MY_WM_APP_INIT,OnMyInit) entry in the message map.
Other way would be to use CWinApp::OnIdle, but this isn't view specific.
Or use a "one time" Timer... start a timer handle it and kill it.

Related

QT C++ How to lock application or disable clickable for 3-5 minute

I want to disable click for whole application while receiving data from serial connection. I want to print information on the screen with a message box and turn off clicking for a certain time, how can I do it?
Grabbing mouse for widgets or setKeepMouseGrab for qml may be useful. Add a dummy widget which gets the mouse events. Make it grab the events when you start receiving data.
'Blocking' an application is never a good idea, because your OS will think the application unresponsive and shoot it down forcibly.
What you want, is a modal dialog (which runs on the main thread) with a progress indicator and no clickable interfaces.
A very basic code to achieve that would be:
void startTransfer()
{
ProgressBarDialog dlg; //Inherits from QDialog
//Create asynchronous transfer task.
dlg.setModal(true);
//Connect task and modal dialog
dlg.exec();
}
exec will 'block' all interaction with the other windows while the transfer is running. In fact, exec will only return, once the dialog is done with its job.

C++ Modal dialog box continuing adding texts

I have dialog box and in it, it has OK and Cancel buttons then it also has a ListBox to display text in two columns. I would like to continue adding text into the ListBox after the dialog box is shown. How can I do that? Because after I call DoModal() to show the dialog box, the code does not continue to execute. Or should I create two threads (one is display dialog box while another thread continues adding text to dialog box)?
Make your dialog 'pull' the data it needs, maybe polling the data source with window messages every second, or every 100ms or so. Or, if you go the 'two threads' route (the better but more complicated option), have your data source post a window message to your dialog when there is new data, and then have the dialog fetch the data it needs. The reason for this is that it's much easier to use the existing CDialog infrastructure to get a window that behaves like an actual dialog, compared to building a modal window that acts like a dialog but isn't really.
If you do go the two threads route, your division of labor should be: one thread that does all the UI work (including showing the dialog), and one that 'generates' data and lets the UI know when there is new data. So the worker thread should not do anything related to the UI, nor call any methods on the dialog directly - you can't access windows from several threads. The only cross-thread window communication should happen through window message (i.e., use ::SendMessage()). So certainly don't do something like myDialog->m_theList.AddString("blah") from another thread, or something like it.
Showing a dialog box modally halts further execution until you close the box. Instead of showing it modal, show it normal but make it always on top so you can continue executing the code after the call to DoModal(). Alternatively, populate the box with all the info it will need before you call DoModal().
If you decide to take the "two threads" approach you will discover that the controls on MFC dialogs should not be updated or accessed from a thread other than the one that created the dialog. Even if you have pointers to these controls available in another thread it is not thread-safe to access them. This rule applies whether or not the dialog is modal.
Instead, your second thread would need to PostMessage or SendMessage to the dialog window, so the updates occur on the thread that created the dialog (most likely the main UI thread of the application).

Modeless dialogs and accelerators

I want to perform some action when the user presses CTRL+S inside a modeless dialog.
Accelerators would be perfect for this, except that I don't have control over the thread's message loop (think plugin), so I can't call TranslateAccelerator.
A nested message loop is not an option because the main application does a lot of processing in between calls to PeekMessage.
Is there some way I can 'force' the existing message loop to handle my accelerator?
Is there any other way besides accelerators to catch CTRL+S?
I thought about using a Window hooks on WH_GETMESSAGE, which gets called before returning from GetMessage or PeekMessage. But I'm not sure what would happen after I successfully called TranslateAcellerator, I can't let the application know I handled it.
WH_MSGFILTER would require the app to implement a call to CallMsgFilter, which it doesn't from a quick glance with a debugger.
My last idea was subclassing the control which is what I'm trying to avoid. That would require some mechanism to signal the keypress event to the parent window, which I don't think is a great design. Also, if I add more controls I would have to subclass every single one.
Thanks for any hints.
This is a fairly infamous interop problem, it rears its ugly head also when using Winforms to implement UI in a native program. Microsoft's recommendation is a rough-and-tumble one: start your own thread to display the window so you can pump your own message loop. You'll need SetParent() to avoid Z-order problems. Scary stuff, do consider a modal dialog thrice before committing to this.
Can you use WM_GETDLGCODE? http://msdn.microsoft.com/en-us/library/ms645425(v=vs.85).aspx

Create two windows in one application?

It might be a simple question, but I don't know where to start the search for the answer. How do I create two individual windows interface in one application using native winapi? Do I put two CreateWindow() functions using same HINSTANCE? What if I want a login screen windows and the content page such that login screen comes first, and after I press the button, the login screen is destroyed, and the content page appears. How do I do such trick?
I was thinking of using DestroyWindow and then CreateWindow inside the button click message. However, this would mean the main while loop (for translate/dispatch msg) in WinMain will exit its loop and cause the whole program to exit. Another way is to pre-create it in WinMain, but how would I notify the WinMain if the button was clicked and enter the second loop instead of exiting the program?
You're over-thinking it. To create two windows, call CreateWindow twice. It's just that simple.
Calling DestroyWindow does not cause your program to exit its message pump. Calling PostQuitMessage is what does that. So don't do that.
When the button is clicked, destroy the one window and create the other. There are no tricks. The message pump delivers messages to all windows (unless you're doing it wrong by explicitly requesting messages for one window, but you shouldn't do that).

Application wide periodic tasks with Dialog Based MFC application

In Single Document Interface (SDI) or Multiple Document Interface (MDI) MFC application, I created an application wide timer in the View. The timer will tick as long as the application is running and trigger some periodic actions.
How can I do the same with Dialog Based MFC application?
Should I create Thread's Timer (SetTimer with NULL HWND) and pass a callback function to it?
Should I create worker threads? My experience with other projects was when I tried to display some feedback GUI from non-GUI/worker threads, I need to roll out my own "delegate"/command pattern and a "delegate invoker"/command invoker. The worker thread will send message (I think using message is safer than direct function call when dealing across thread-boundary, CMIIW) to the UI-thread. and the UI-thread will be the "delegate"/command invoker. Failing to do this and to make sure that the windows/dialogs have the correct parent will result in bizzare behaviors such as the Application suddenly disappears to the background; Window/Dialog that is shown behind the current window/dialog and causing the current window to be unresponsive/unclickable. Probably I was doing something wrong but there were so much problems when dealing with threads.
Are there best practices for this?
A timer works as well in a dialog-based application as an SDI or MDI app. OTOH, timers are (mostly) a leftover from 16-bit Windows. If you want to do things periodically, a worker thread is usually a better way to do it (and yes, Windows Mobile supports multiple threads).
Edit: in a dialog-based application, the main dialog exists for (essentially) the entire life of the application. Unless you really need the timer during the milliseconds between application startup and dialog creation or dialog destruction and application exit, just attach it to the dialog. Otherwise, you can attach it to the main window -- which MFC creates and destroys, even though it's never displayed.
If you use the MFC Wizard to create the Dialog based app, you probably have a hidden view window as well as a dialog window. The view window creates the dialog with DoModal(), which runs the dialog in the same thread, effectively suspending the view window.
While the dialog is open, the view window will not process any events. So, if the view window owns the timer, it will not process the timer events.
The simplest solution is to create the timer in the dialog and let the dialog handle the timer messages.
IMO, use the Timer if it solves the problem. As you've mentioned a Worker Thread interacting with the UI, in MFC, can be more trouble than its worth sometimes.
If the problem is simple enough for a timer to suffice, thats what i'd use (Remember KISS)
SetTimer does not have to be handed a window to work, it can call a callback method.
You can use that in your application - declare in your CWinApp (or anywhere really)
static void CALLBACK OnTimer(HWND, UINT, UINT, DWORD);
Then in the InitInstance call SetTimer(0, [eventid], [time period], OnTimer);
In OnTimer you can get back to the CWinApp instance via AfxGetApp() or theApp since there is only one.
Second attempt: my previous answer was dne in a hurry and was not correct.
Your basic vanilla MFC Dialog app only uses one thread. The main thread starts with a class derived from CWinApp. In the InitInstance() method it launches the dialog using CDialog::DoModal(). This function doesn't return until the dialog is closed.
While the dialog is running, the CWinApp class does not process any messages, so won't see a WM_TIMER.
There are many ways around this.
Let the first dialog own the timer and make all other dialogs children of it. This might be OK, depending on your dialog requirements, but it might be too restrictive.
Launch the first Dialog as modeless, i.e. use Create() instead of DoModal(). Create() returns straight away (putting the Dialog into a different thread). You can then create a message loop in the CWinApp class and process timers there. You'll have to use thread timers instead of window timers as the CWinApp class doesn't have a window. (or you could create a hidden window if that is more convenient).
You can hack the dialog's mesage loop and make it pass messages to the CWinApp class' message handler. That is quite complex and not for the faint hearted.
You can create a dedicated timer thread. You'd probably do that from the CWinApp class before it creates the dialog, but other strategies are possible.
Do any of those schemes sound like they fit your needs? If not, maybe you can explain your needs more fully and we might be able to come up with something appropriate.