Operations immediately after dialog box appears - mfc

I am working with MFC project. I have a situation where I want to do an extensive calculation as soon as the dialog box shows up.
I tried putting this calculations in OnInitDialog() and also tried putting it in the contructor. For obvious reasons, with this approach, it takes time for the dialog box to appear which is not intended.
Is there a way where I can do my calculations immediately after the dialog box shows up? If so which is the function that I need to fit my code into?

Yes, creating another thread from the constructor or OnInitDialog override is the approach for this problem. But for giving notification back to dialog box that Complex initialization is complete, you need to use SendMessage or PostMessage to let dialog-window know about this completion and update the UI appropriately. You would also need to have custom message-code (WM_USER+N, or WM_APP+N), along with an entry in message-map: ON_MESSAGE.

BOOL CMFC_dialogDlg::OnInitDialog()
{
AfxBeginThread(ThreadFunc,NULL/*Pass the parameter to the function*/);
}
UINT ThreadFunc(LPVOID param)
{
/*Do complex task*/
for(int i=0;i<1000 ;i++)
{
}
}

In this situation you'll need to do the caculations in a subthread.Usually I do this job using AfxBeginThread.

Related

Z-order issue with a modal dialog

I've created a dialog to an existing application (which I'm not aware of its entire structure), and the dialog is keep maintaining the highest Z-order OF ALL WINDOWS SHOWN IN THE OS instead of highest within the application.
Here's a rough sketch of how I'm implementing it:
Message map ON_COMMAND links the menu to the handler function OnTestDialog(), which looks like this:
void CSettle3DDoc::OnTestDialog(){
UpdateAllViews(NULL);
CDlg_Test_Dialog dlg;
dlg.DoModal();
}
CDlg_Test_Dialog is a subclass of ETSLayoutDialog, which you can find the code here: http://www.codeproject.com/Articles/116/Layout-Manager-for-Dialogs-Formviews-DialogBars-an
From there, ::OnInitDialog() calls on ETSLayoutDialog::OnInitDialog(), and initializes other variable that has noting to do with the Z-order.
After initialization, there are no other code that causes the Dialog's Z-order to change.
Staying on top of other windows within the application is acceptable, but blocking other applications' view is a bit annoying, and I'd like it to be fixed :(
Any insights would be appreciated.

How to sync a Progress Control with a set of data which is loading in a Dialog Box in Visual C++

I'm trying to add a progress control in my dialogs, that will step upon every iteration of a loop until completion. I've never played around with progress controls before, so I'm totally clueless as to where I should start. I've added a progress control resource view into my dialog, but it just shows up as an empty progress control. I'd like to have the progress control dynamically appear/update when after pressing a button an image from somewhere stars being loaded.
I am trying to add a progress control on a dialog box in Visual c++ environment.
After adding this tool the following code added to main.cpp :
void CPanoramicsampleDlg::OnNMCustomdrawProgress1(NMHDR *pNMHDR, LRESULT *pResult){
}
I can show procedure of loading in a Text control as follow:
sprintf_s(pack1,"Data%d%%",Event);
::SetWindowText(GetDlgItem(IDC_Static)->m_hWnd,pack1);
so in this way I can see loading process as %d in a text window but I don't know how to show loading procedure by progress control and how and where to define range or even progress bar handle for this progress control so on.
finally I would like to know is there any function for progress control for example:
::EnableWindow(GetDlgItem(IDC_Progress1)->m_hWnd);
You should first add a variable for the control, by right-clicking on the progress bar in the dialog editor, and choosing Add Variable... Your dialog class will then have an instance of a CProgressCtrl class on which you can then call the members that IInspectable has mentioned in his answer. Delete the OnNMCustomdrawProgress1 handler, you don't need it.
e.g.
m_progressCtrl.EnableWindow(TRUE);
m_progressCtrl.SetRange(0, 100);
m_progressCtrl.SetPos(75);
Then whatever that eventParam1 value is that you mention, proportion it between your start and stop value, and call SetPos with it.
Update after comment:
Do the SetRange() in the OnInitDialog() function. If you don't already have an OnInitDialog you need to override it, follow the instructions in the accepted answer of this question to do it.
VS 2008, MFC: add OnInitDialog - how?
As for where you put the SetPos(): You describe that you can already track loading progress in a text control using some or other eventParam1. That sounds like a handler or callback from what ever loading you're doing, and that is where you will instead SetPos() on the progress bar instance.
The CProgressCtrl Class provides the following members that you will have to use:
CProgressCtrl::SetRange: Allows you to set lower and upper bound. These values ideally reflect your starting state and finishing condition.
CProgressCtrl::SetPos: Used to update the current position. You would update the current position where you used to output progress in your edit control.
As an alternative to calling CProgressCtrl::SetPos with an explicit position value, you can set a step increment, calling CProgressCtrl::SetStep, and update the control with a call to CProgressCtrl::StepIt. If you know the step increment ahead of time, this is an easier way to go about updating the current position.
Additional information is available at the MSDN: Using CProgressCtrl.

Is there any function called after the OnInitDialog function in MFC?

I want to create a thread after the creation of a dialog box in MFC. Is there any function that Windows has provided and is automatically called after OnInitDialog so that I can create my thread inside it?
You can simply create your thread in the OnInitDialog function. There's no reason to overcomplicate things by going and searching for a different function, or splitting your initialization code up in two pieces. (There also isn't any such function, because there's no corresponding Windows message that is sent.)
If you want to get your dialog box on the screen before you create the thread, you can just show it manually using the ShowWindow function. For example:
ShowWindow(SW_SHOW);
RedrawWindow();
Also see this post by Raymond Chen: Waiting until the dialog box is displayed before doing something
OnInitDialog() is the main function called upon initialization (in reaction to WM_CREATE).
Why can't you create your thread in there?
I have changed the thread priority to below normal and when the thread executes for the first time I set the thread to normal priory. This works fine. Thanks for your response.
After many years of feeling unsatisifed with the OnTimer solution to draw first view graphics in an MFC dialog app (a favorite playground), this seemed like a nice simple solution:-
Add a WM_HSCROLL handler with class wizard.
At the end of OnInitDialog post a hscroll message with a NULL LPARAM
In handler detect the NULL, draw graphics.
a timer meant that the app was alive for some time before graphics happened, and apparently hscroll is prioritized to happen just after the WM_PAINT message which would erase a picture element to its blank state, deleting anything that was drawn during initdialog.
BOOL CSpecDlg::OnInitDialog()
{
...
PostMessage(WM_HSCROLL,0, (LPARAM)NULL);
return TRUE; // return TRUE unless you set the focus to a control
}
void CSpecDlg::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
// TODO: Add your message handler code here and/or call default
if (pScrollBar==NULL)
{
plot();
}
CDialogEx::OnHScroll(nSBCode, nPos, pScrollBar);
}

How to stop MFC from disabling my controls if I don't declare a message map entry for it's corresponding command?

I have the following problem: MFC is disabling my toolbar (a CToolbar) controls if I don't have a message map entry for it's corresponding message (let's say ID_MYBUTTON1). Is there a way around this? I had the same problems with the menu but I found that you could disable the auto disabling by setting CFrameWnd::m_bAutoMenuEnable to false but I cannot find a similar member for CToolbar.
I guess I could add handlers redirecting to an empty function but it would be nice if I could just stop this behavior without using "tricks".
Thanks
Add a ON_UPDATE_COMMAND_UI handler for each of the controls in your toolbar. Something like this:
ON_UPDATE_COMMAND_UI(ID_MYBUTTON1, uiButtonHandler);
void myToolBar::uiButtonHandler(CCmdUI* pCmdUI)
{
pCmdUI->Enable(TRUE); // Or whatever logic you want.
}
For details read the appropriate section in the MSDN.
Well like I said in reply to zdan's answer I found a way. Just override the OnUpdateCmdUI function in CToolBar like this
class MyToolBar : public CToolBar
{
public:
virtual void OnUpdateCmdUI(CFrameWnd* pTarget, BOOL bDisableIfNoHndler)
{ return CToolBar::OnUpdateCmdUI(pTarget, FALSE);}
}
the bDisableIfNoHndler flag is the one responsible for telling the toolbar to disable buttons if no handlers is found so I just force it to FALSE.
Though now I'm having another problem. The toolbar seem fines but it do not send the commands when I press a button. I'm not sure why because if I access the same commands from the menu it works fine. I'll try to see if this is related.
Thanks for your helps.
Update: Found my problem. Basically the problem was that my handlers to my commands were in MyFrame::PreTranslateMessage (after doing like suggested in this question's answer : How to redirect MFC messages to another object?) but the commands were not sent through this function (though when accessed from the menu they did). They were sent through MyFrame::OnCommand though so I just changed the code from PreTranslateMessage to OnCommand and now everything works fine. I don't know MFC enough to know why this was the case but now everything seems to works so thanks for the help everyone.

Managing Window Z-Order Like Photoshop CS

So I've got an application whose window behavior I would like to behave more like Photoshop CS. In Photoshop CS, the document windows always stay behind the tool windows, but are still top level windows. For MDI child windows, since the document window is actually a child, you can't move it outside of the main window. In CS, though, you can move your image to a different monitor fine, which is a big advantage over docked applications like Visual Studio, and over regular MDI applications.
Anyway, here's my research so far. I've tried to intercept the WM_MOUSEACTIVATE message, and use the DeferWindowPos commands to do my own ordering of the window, and then return MA_NOACTIVATEANDEAT, but that causes the window to not be activated properly, and I believe there are other commands that "activate" a window without calling WM_MOUSEACTIVATE (like SetFocus() I think), so that method probably won't work anyway.
I believe Windows' procedure for "activating" a window is
1. notify the unactivated window with the WM_NCACTIVATE and WM_ACTIVATE messages
2. move the window to the top of the z-order (sending WM_POSCHANGING, WM_POSCHANGED and repaint messages)
3. notify the newly activated window with WM_NCACTIVATE and WM_ACTIVATE messages.
It seems the cleanest way to do it would be to intercept the first WM_ACTIVATE message, and somehow notify Windows that you're going to override their way of doing the z-ordering, and then use the DeferWindowPos commands, but I can't figure out how to do it that way. It seems once Windows sends the WM_ACTIVATE message, it's already going to do the reordering its own way, so any DeferWindowPos commands I use are overridden.
Right now I've got a basic implementation quasy-working that makes the tool windows topmost when the app is activated, but then makes them non-topmost when it's not, but it's very quirky (it sometimes gets on top of other windows like the task manager, whereas Photoshop CS doesn't do that, so I think Photoshop somehow does it differently) and it just seems like there would be a more intuitive way of doing it.
Anyway, does anyone know how Photoshop CS does it, or a better way than using topmost?
I havn't seen anything remarkable about Photoshop CS that requries anything close to this level of hacking that can't instead be done simply by specifying the correct owner window relationships when creating windows. i.e. any window that must be shown above some other window specifies that window as its owner when being created - if you have multiple document windows, each one gets its own set of owned child windows that you can dynamically show and hide as the document window gains and looses activation.
You can try handle WM_WINDOWPOSCHANGING event to prevent overlaping another windows (with pseudo-topmost flag). So you are avoiding all problems with setting/clearing TopMost flag.
public class BaseForm : Form
{
public virtual int TopMostLevel
{
get { return 0; }
}
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool EnumThreadWindows(uint dwThreadId, Win32Callback lpEnumFunc, IntPtr lParam);
/// <summary>
/// Get process window handles sorted by z order from top to bottom.
/// </summary>
public static IEnumerable<IntPtr> GetWindowsSortedByZOrder()
{
List<IntPtr> handles = new List<IntPtr>();
EnumThreadWindows(GetCurrentThreadId(),
(hWnd, lparam) =>
{
handles.Add(hWnd);
return true;
}, IntPtr.Zero);
return handles;
}
protected override void WndProc(ref Message m)
{
if (m.Msg == (int)WindowsMessages.WM_WINDOWPOSCHANGING)
{
//Looking for Window at the bottom of Z-order, but with TopMostLevel > this.TopMostLevel
foreach (IntPtr handle in GetWindowsSortedByZOrder().Reverse())
{
var window = FromHandle(handle) as BaseForm;
if (window != null && this.TopMostLevel < window.TopMostLevel)
{
//changing hwndInsertAfter field in WindowPos structure
if (IntPtr.Size == 4)
{
Marshal.WriteInt32(m.LParam, IntPtr.Size, window.Handle.ToInt32());
}
else if (IntPtr.Size == 8)
{
Marshal.WriteInt64(m.LParam, IntPtr.Size, window.Handle.ToInt64());
}
break;
}
}
}
base.WndProc(ref m);
}
}
public class FormWithLevel1 : BaseForm
{
public override int TopMostLevel
{
get { return 1; }
}
}
So, FormWithLevel1 will be always over any BaseForm. You can add any number of Z-order Levels. Windows on the same level behave as usual, but will be always under Windows with level Current+1 and over Windows with level Current-1.
Not being familiar to Photoshop CS it is bit hard to know exactly what look and feel you are trying to achieve.
But I would have thought if you created a modeless dialog window as you tool window and you made sure it had the WS_POPUP style then the resulting tool window would not be clipped to the main parent window and Windows would automatically manage the z-Order making sure that the tool window stayed on top of the parent window.
And as the tool window dialog is modeless it would not interfere with the main window.
Managing Window Z-Order Like Photoshop CS
You should create the toolwindow with the image as the parent so that windows manage the zorder. There is no need to set WS_POPUP or WS_EX_TOOLWINDOW. Those flags only control the rendering of the window.
Call CreateWindowEx with the hwnd of the image window as the parent.
In reply to Chris and Emmanuel, the problem with using the owner window feature is that a window can only be owned by one other window, and you can't change who owns a window. So if tool windows A and B always need to be on top of document windows C and D, then when doc window C is active, I want it to own windows A and B so that A and B will always be on top of it. But when I activate doc window D, I would have to change ownership of tool windows A and B to D, or else they will go behind window D (since they're owned by window C). However, Windows doesn't allow you to change ownership of a window, so that option isn't available.
For now I've got it working with the topmost feature, but it is a hack at best. I do get some consolation in the fact that GIMP has tried themselves to emulate Photoshop with their version 2.6, but even their implementation occasionally acts quirky, which leads me to believe that their implementation was a hack as well.
Have you tried to make the tool windows topmost when the main window receives focus, and non-topmost when it loses focus? It sounds like you've already started to look at this sort of solution... but much more elaborate.
As a note, it seems to be quite well documented that tool windows exhibit unexpected behavior when it comes to z-ordering. I haven't found anything on MSDN to confirm it, but it could be that Windows manages them specially.
I would imagine they've, since they're not using .NET, rolled their own windowing code over the many years of its existence and it is now, like Amazon's original OBIDOS, so custom to their product that off-the-shelf (aka .NET's MDI support) just aren't going to come close.
I don't like answering without a real answer, but likely you'd have to spend a lot of time and effort to get something similar if Photoshop-like is truly your goal. Is it worth your time? Just remember many programmers over many years and versions have come together to get Photoshop's simple-seeming windowing behavior to work just right and to feel natural to you.
It looks like you're already having to delve pretty deep into Win32 API functions and values to even glimpse at a "solution" and that should be your first red flag. Is it eventually possible? Probably. But depending on your needs and your time and a lot of other factors only you could decide, it may not be practical.