Position a modeless dialog over a modal dialog?

Position a modeless dialog over a modal dialog?

Post by Wood » Wed, 03 Feb 2010 07:04:40


I have a model dialog, F. F has a modeless dialog, A, which is
intended to show on top of F (user drags A to different locations of
F).

When F is instantiated by its parent dialog using DoModal, it creates
the modeless dialog A:

A.Create(IDD_A,this);

I attempted to position A using code in F. Guide2 is a static control
in F at the desired initial position of A:

CRect r;
A.GetWindowRect(&r); // to get the dimensions of A,
needed for MoveWindow
int w=r.Width(),h=r.Height();
CStatic *pStatic;
pStatic=(CStatic *)GetDlgItem(IDC_Guide2); // get the desired
position for A
pStatic->GetWindowRect(&r);
A.MoveWindow(r.left,r.top,w,h,TRUE/*repaint*/);
A.ShowWindow(SW_SHOW);

This code repositions the modeless dialog A, but not at the correct
spot.

The documentation for MoveWindow says that the coordinates are client
wrt the parent, but screen if A is a "top-level window". Although A
was created with F as a parent, Spy++ and GetParent both indicate that
it has no parent.

I tried converting the guide coordinates from screen to client, in
case MoveWindow was using client coords of F; this didn't work either.

How can I reposition the modeless dialog A at a known position with
respect to modal dialog F, its creator?
 
 
 

Position a modeless dialog over a modal dialog?

Post by Joseph M. » Wed, 03 Feb 2010 11:59:24

ee below...
On Mon, 1 Feb 2010 14:04:40 -0800 (PST), Woody < XXXX@XXXXX.COM > wrote:

***
This line makes no sense at all. The variable A is declared somewhere, and has a type.
Both of these are critical to the understanding of what is going on. Therefore, it is
meaningless to give this statement without specifying the declaration of the variable, and
state where it is declared.

I would do this by doing
class CFDlg : public CDialog {
protected:
CADlg * A;
... etc.
};

to show where the variable is declared (it has to be at the scope of the F class; it is
meaningless to declare it as a local variable, and therefore it is impossible access it
using ".", as in "A.Create". If you do not get a syntax error, it means your declaration
is wrong, and probably, furthermore, in the wrong place. So the example as presented
cannot possibly work)

Then, you would show:

CFDlg::CDDlg(...etc...) : ...etc...
{
A = NULL;
}

to show that it is set to NULL in the constructor. Then you need show the instantiation

BOOL CFDlg::OnInitDialog()
{
...etc....
A = new CADlg;
A->Create(CADlg::IDD);
...maybe other stuff,,,
return TRUE;
}

Note that the correct form is NOT use use IDD_A, but the appropriate IDD name, which is
CADlg::IDD.

I would then create a PostNcDestroy handler:

void CADlg::PostNcDestroy()
{
delete this;
CDialog::PostNcDestroy();
}

Because A is a modeless dialog, I would add handlers to override the default OK and Cancel
events:

void CADlg::OnOK()
{
}

void CADlg::OnCancel()
{
}

I would then add on OnDestroy handler in the parent dialog

void CFDlg::OnDestroy()
{
if(A != NULL)
A->DestroyWindow();

Now you would have the essential infrastructure for doing a modeless dialog.

You have shown none of this, nor have you indicated that any of it has been done.
****

****
Never declare more than one variable on a line. NEVER initialize more than one variable
on a line. ALWAYS put a space after a comma when one appears in a separator context. The
line is unreadable as written, and should have been written as
CSize sz(r.Width(), r.Height());
****
****
Why would you write a GetDlgItem call for any control? If you are writing more than one a
year, you are not using MFC correctly. Therefore, we will assume that you have created a
CStatic control variable for guide2, which would be added by the Add Variable menu item
(right click on the stack control), and therefore this gibberish will disappear, and you
will write something like as shown below (where c_Guide2 is the name of the variable)
c_Guide.GetWIndowRect(&r);
****
****
The problem here is that you are using the wrong coordinates. If you want to position a
window, you must give its coordinates in parent client coordinates. Since F, the class
you are in, is the parent of the window, you must express the desired position of A in
terms of the F client system. So you have to do

ScreenToClient(&r);
This converts the screen coordinates of GetWindowRect to the client coordinates of F.

Since you do not want to change the size of the control A, you do NOT need either the
width or the height at all, so getting them was a meaningless activity. What you want to
write now is:
A->SetWindowPos(NULL, r.left, r.top, 0, 0, SWP_NOSIZE | SWP_NOZORDER |
SWP_SHOWWI
 
 
 

Position a modeless dialog over a modal dialog?

Post by Wood » Wed, 03 Feb 2010 13:25:44


A is a type, a dialog derived from CDialog. In simplifying the code
for the question, I confounded the type A, and the instance, a. In the
"parent" dialog F, there is a declaration

A a;

and in the remainder of the code I showed, "A" should be "a".


Not using either of these, although I may have to provide for user's
typing Esc.


It's true, I didn't show all my code. It does work, though, except for
the positioning.


These are your opinions, and as I have said before, I don't agree.

> This converts the screen coordinates of GetWindowRect to the client coordinates of F.

Finally, we get to the point. It is undoubtedly a question of
coordinates, but not just screen to client. As I said, I did try this,
and it positions the window incorrectly.>
> This presumes your modeless dialog is done as a popup, by the way, and you have sa>d
> nothing about how this window is managed. t would probably make more sense to create t>e
> dialog as a child window.

The modeless dialog has the following styles (as shown by Spy++):
WS_CAPTION, WS_VISIBLE, WS_CLIPSIBLINGS, WS_OVERLAPPED, DS_3DLOOK,
DS_FIXEDSYS, DS_SETFONT, WS_EX_ LEFT, WS_EX_LTRREADING,
WS_EX_RIGHTSCROLLBAR, WS_EX_TOOLWINDOW, WS_EX_WINDOWEDGE,
WS_EX_CONTROLPARENT, WS_EX_STATICEDGE. Spy++ shows its parent as
(none), although the dialog F was specified in the Create call.>
> Actually, it should probably be a child window.

It was created using VS2005 dialog editor. I left the style choice as
"overlapped" (the default), but I can try "child".

The issue may be complicated by the fact that I'm testing this on the
primary monitor of a dual-monitor system.>
> Note also that unless the dialog is a child (not a popup), when you move the pare>t
> dialog, the modeless dialog window will stay where it was, unless you also impleme>t
> MoveWindow to move it. nd if you resize the window to cover some of the controls, t>e
> modeless dialog will still appear on top of the window; it will not be clipped by t>e
> parent window.

These properties are OK, but I do want to get the coords correct, so I
will try the "child" approach.>

>o what do you intend here?

In MS Word 97, the Find dialog's behavior is what I'm after. It
behaves as a window which remains on top of the main window, can be
moved anywhere>

> But much of the code I see is deeply suspect.

We are all deeply suspect.
 
 
 

Position a modeless dialog over a modal dialog?

Post by Wood » Wed, 03 Feb 2010 13:30:06

Follow-up:

I changed the style of the modeless dialog from Overlapped to Child,
re-instated the screen-to-client conversion, and now MoveWindow works
as expected. Problem solved.
 
 
 

Position a modeless dialog over a modal dialog?

Post by Joseph M. » Wed, 03 Feb 2010 15:01:03

n Mon, 1 Feb 2010 20:25:44 -0800 (PST), Woody < XXXX@XXXXX.COM > wrote:

****
You don't have an option. You *MUST* provide them! And they must do NOTHING! If you
want 'ESC' to do something, you must handle it in the OnCancel, but you must NOT, under
any conditions, call CDialog::OnOK or CDialog::OnCancel as a consequence. These only work
correctly in modal dialogs. A modeless dialog handler MUST override these virtual methods
and no matter what it does, it must not call the superclass methods. This is because the
superclass methods call ::EndDialog, whose behavior is undefined for modeless dialogs.
****
****
But part of the answer depends on seeing the declaration. In general, you must not show
code that has "unbound variables", that is, variables for which we do not see the
declarations.
****
****
The code was nearly unreadable.
****
>> This converts the screen coordinates of GetWindowRect to the client coordinates of >. >>
>Finally, we get to the point. It is undoubtedly a question >f
>coordinates, but not just screen to client. As I said, I did try thi>,
>and it positions the window incorrectly.
****
WHatever that means. It actually is important to know what it did. Was the box to the
left and above, or to the right and below, or what?
**>* >>
>> This presumes your modeless dialog is done as a popup, by the way, and you have sa>>
>> nothing about how this window is managed. t would probably make more sense to create t>>
>> dialog as a child windo>. >>
>The modeless dialog has the following styles (as shown by Spy++>:
>WS_CAPTION, WS_VISIBLE, WS_CLIPSIBLINGS, WS_OVERLAPPED, DS_3DLOO>,
>DS_FIXEDSYS, DS_SETFONT, WS_EX_ LEFT, WS_EX_LTRREADIN>,
>WS_EX_RIGHTSCROLLBAR, WS_EX_TOOLWINDOW, WS_EX_WINDOWEDG>,
>WS_EX_CONTROLPARENT, WS_EX_STATICEDGE. Spy++ shows its parent >s
>(none), although the dialog F was specified in the Create call.
****
There is some total weirdness dealing with parents of modeless dialogs, done within MFC. I
found that it attaches all dialogs to the main window as a parent, no matter what you
specify in the Create call.

But without knowing what you intend, it isn't easy to figure out what the correct answer
is. I wouldn't have done it with a modeless popup dialog.
**>* >>
>> Actually, it should probably be a child windo>. >>
>It was created using VS2005 dialog editor. I left the style choice >s
>"overlapped" (the default), but I can try "child>. >>
>The issue may be complicated by the fact that I'm testing this on t>e
>primary monitor of a dual-monitor system.
****
That can have problems in some cases, but I suspect the child property is the one you
want.
**>* >>
>> Note also that unless the dialog is a child (not a popup), when you move the pare>>
>> dialog, the modeless dialog window will stay where it was, unless you also impleme>>
>> MoveWindow to move it. nd if you resize the window to cover some of the controls, t>>
>> modeless dialog will still appear on top of the window; it will not be clipped by t>>
>> parent windo>. >>
>These properties are OK, but I do want to get the coords correct, so>I
>will try the "child" approac>.gt;>>
>>o what do you intend he>e?> >
>In MS Word 97, the Find dialog's behavior is what I'm after.>It
>behaves as a window which remains on top of the main window, can>be
>mov
 
 
 

Position a modeless dialog over a modal dialog?

Post by Tom Serfac » Wed, 03 Feb 2010 16:19:45

Glad you got it to work. One thing I always do with modeless dialogs is
make the button that pops them up (from the parent) check to see if they are
already popped up and may have just slipped behind some other window (like
the parent). If that happens I just pop it to the front again using
SetForegroundWindow().

That way the dialog doesn't ever really get lost.

Tom
 
 
 

Position a modeless dialog over a modal dialog?

Post by Wood » Wed, 03 Feb 2010 18:04:03

I spoke too soon. Making the modeless dialog a child window works fine
wrt the coordinates, but the modeless gets clipped by the parent,
which I can't allow. So I'm back to figuring out the coord
transformation.

So far, I have discovered that neither GetParent nor GetOwner in the
"parent" dialog returns a non-null value, although Spy says the parent
F has an owner. Apparently there is more than one definition of
"owner", because ::GetOwner returns the correct window handle.

It looks like the coordinates for move window are relative to the
client area of another dialog, P, which is the owner of F. Now I need
to solve the problem of how to get the window coords of the client
area of a window. The only thing I have found so far on this topic is
WM_NCCALCSIZE, which may or may not get what I want (I haven't
finished researching this yet).
 
 
 

Position a modeless dialog over a modal dialog?

Post by Wood » Thu, 04 Feb 2010 04:58:49

For anyone interested, here is what I found. Modal dialog P creates
modal dialog F, which creates modeless A.

1) The coordinate difficulty came about because I was doing the
calculation in OnInitDialog (of F). At that time, F has not been
positioned relative to P, and its reported window rect is actually the
client rect of P.
2) When I move the calculation to OnCtlColor of F, MoveWindow on A
uses screen coords. I get the screen coords of the guide, so I can
position A correctly. I am never using client coords.
3) To get the screen coords of the client area, use GetWindowInfo.
This handy function returns both the window and client rectangles in
screen coords.
 
 
 

Position a modeless dialog over a modal dialog?

Post by Joseph M. » Thu, 04 Feb 2010 12:54:17

It is incorrect to do this in OnCtlColor. The correct solution is to PostMessage a
user-defined message in OnInitDialog, and in handling that message, do the positioning.
Consider it an accident if it works in OnCtlColor, and functionality should not be
overloaded in this fashion.
joe



Joseph M. Newcomer [MVP]
email: XXXX@XXXXX.COM
Web: http://www.yqcomputer.com/
MVP Tips: http://www.yqcomputer.com/
 
 
 

Position a modeless dialog over a modal dialog?

Post by Wood » Thu, 04 Feb 2010 17:23:30


I have a flag so it's only done once, so it's not an accident that it
works. At the time when the dialog is drawing its controls, it has
been moved to its final position by CDialog code.

You are right, though, that this is not an elegant soln. I looked for
a function that would be called once by the framework after the dialog
window had been positioned, but didn't find one; hence OnCtlColor.

Next time I'll try the message, although that's more work.
 
 
 

Position a modeless dialog over a modal dialog?

Post by Joseph M. » Thu, 04 Feb 2010 22:43:27

No it is still an accident that it works. It relies on the fact that this method is
called. You should not overload functionality in this fasion. This is bad design.

Of course you did not find a function that was called when you wanted it, because it is so
trivial to implement yourself that there is no need to create a function as part of the
API set to do it. PostMessage of a user-defined message handles this nicely.
joe




Joseph M. Newcomer [MVP]
email: XXXX@XXXXX.COM
Web: http://www.yqcomputer.com/
MVP Tips: http://www.yqcomputer.com/