I am working with a legacy MFC (VC 6) application that needs to be modified to be centered vertically and horizontally on the screen at startup. I have tried using the call to CenterWindow in the main frame OnCreate call, but that doesn't appear to do anything. Any help would be greatly appreciated.
m_pMainWnd->CenterWindow() on InitInstance()?
left = (ScreenWidth-WindowWidth) /2
top = (ScreenHeight-WIndowHeight) / 2
I have an MDI application which does the positioning at startup in InitInstance of the Application class. (I remember having read that OnCreate of the main frame is indeed the wrong place, but I don't know anymore where I've read that ... long time ago) I try to strip out the relevant parts here:
BOOL CMyApp::InitInstance()
{
// stuff...
CMyMainFrame* pMyMainFrame=CreateMainFrame();
if (!pMyMainFrame)
return FALSE;
m_pMainWnd = pMyMainFrame;
// stuff...
if (!ProcessShellCommand(cmdInfo))
return FALSE;
int nCmdShow=SW_NORMAL;
UINT flags=WPF_SETMINPOSITION;
WINDOWPLACEMENT aWndPlacement;
CRect rect;
// determine the desired rect of the application window
aWndPlacement.length=sizeof(WINDOWPLACEMENT);
aWndPlacement.showCmd=nCmdShow;
aWndPlacement.flags=flags;
aWndPlacement.ptMinPosition=CPoint(0,0);
aWndPlacement.ptMaxPosition=CPoint(-::GetSystemMetrics(SM_CXBORDER),
-::GetSystemMetrics(SM_CYBORDER));
aWndPlacement.rcNormalPosition=rect;
m_pMainWnd->SetWindowPlacement(&aWndPlacement);
m_nCmdShow=nCmdShow;
pMyMainFrame->ShowWindow(m_nCmdShow);
pMyMainFrame->UpdateWindow();
return TRUE;
}
I hope that works for you.
Related
I have this very strange issue. I'm trying to get a window hierarchy to be replicated. So on creating the 1st level dialog, I'm start the instance of the 2nd level dialog.
I've done this in many different ways, but it always shows up as the 2nd level being below the 1st level and then usually a zorder inversion happens (they flip positions). Occasionally, the inversion doesn't happen, but if I click on the owner, the owned immediately jumps to the top of the zorder.
Here are the main parts of a small example to show this happening:
const unsigned short WMA_DIALOGACTION = WM_APP+1;
// Button event handler for the 0th level
void CdialogcallingdialogsDlg::OnBnClickedDlgLvl1()
{
CDlgLvl1 x(this);
x.DoModal();
}
BEGIN_MESSAGE_MAP(CDlgLvl1, CDialogEx)
ON_WM_WINDOWPOSCHANGED()
ON_MESSAGE(WMA_DIALOGACTION, OnDialogAction)
END_MESSAGE_MAP()
void CDlgLvl1::OnWindowPosChanged(WINDOWPOS* lpwndpos)
{
if (!m_shownDlg) {
m_shownDlg = true;
PostMessage(WMA_DIALOGACTION);
}
}
// Level 1 dialog opening up level 2 dialog
LRESULT CDlgLvl1::OnDialogAction(WPARAM wParam, LPARAM lParam)
{
ShowWindow(SW_SHOW);
CDlgLvl2 x(this);
x.DoModal();
return LRESULT();
}
BEGIN_MESSAGE_MAP(CDlgLvl2, CDialogEx)
ON_WM_WINDOWPOSCHANGING()
END_MESSAGE_MAP()
// Level 2 dialog offseting its position
void CDlgLvl2::OnWindowPosChanging(WINDOWPOS* lpwndpos)
{
ASSERT(lpwndpos->hwnd == m_hWnd);
// Offset dialog to see the problem of dlg2 showing up below dlg1
if (!(lpwndpos->flags & SWP_NOMOVE)) {
lpwndpos->x += 10;
lpwndpos->y += 10;
}
}
In the example, you click on the button in the main dialog. That then starts up CDlgLvl1 which then starts up CDlgLvl2. The dialogs are the default dialogs except for the message handling that is shown here and a button on the main application dialog. If you look at it carefully, you can see the inversion.
What am I doing wrong? Perhaps there is a better way to do this?
In case it makes a difference, the issue is more pronounced under Windows 10 and doesn't seem to be visible on Windows 8.1.
A copy of the solution can be pulled from my git repo here:
https://github.com/Ma-XX-oN/dialog-calling-dialogs.git
I've just added some bitmaps on the dialogs to really show the issue, but I've not tested on my 8.1 box yet.
I did a recording of how it pops up and here is frame 0, 2, and 3 of that recording:
Frame 0
Frame 2
Frame 3
As you can see, LVL1 appears over LVL2 in Frame 2, and then flips position in Frame 3.
Full video can be found here.
Using this example project, I've not been able to replicate LVL1 staying overtop of LVL2, but I believe that the behaviour of the zorder inversion not happening is some sort of race condition.
The problem is caused when windows "transition animation" is enabled. WM_WINDOWPOSCHANGED is being sent before the animation is finished.
To fix this problem, you can simply disable the transition for the dialog:
BOOL CDlgLvl2::OnInitDialog()
{
BOOL res = CDialogEx::OnInitDialog();
BOOL attrib = TRUE;
DwmSetWindowAttribute(m_hWnd, DWMWA_TRANSITIONS_FORCEDISABLED, &attrib, sizeof(attrib));
return res;
}
If you don't want to disable the transition, you have to wait until this transition is finished. I don't know how to detect it or how to determine the transition time. It seems to be 250 milliseconds. SystemParametersInfo(SPI_SETMENUSHOWDELAY...) gives a value of 400 milliseconds which seems a bit too long.
Assuming we know the time, use SetTimer to run the function after transition is over:
BOOL CDlgLvl2::OnInitDialog()
{
BOOL res = CDialogEx::OnInitDialog();
ANIMATIONINFO info = { sizeof info };
SystemParametersInfo(SPI_GETANIMATION, sizeof(ANIMATIONINFO), &info, 0);
if (info.iMinAnimate)
SetTimer(1, 250, nullptr);
else
SetTimer(1, 1, nullptr);
return res;
}
void CDlgLvl2::OnTimer(UINT_PTR nIDEvent)
{
CDialogEx::OnTimer(nIDEvent);
if(nIDEvent == 1)
{
KillTimer(nIDEvent);
CDlgLvl2(this).DoModal();//note, PostMessage is not needed in SetTimer
}
}
Maybe the problem is caused because the 1st level dialog creates the 2nd one before it has a chance to display itself. And yes, this can vary from system to system. There's no really a fix, but I would suggest a workaround, employing a timer. Below is some code.
Header file for CDlgLvl1:
class CDlgLvl1 : public CDialogEx
{
.
.
.
protected:
UINT_PTR nIDTimer = 0; // Add this
};
Source file for CDlgLvl1:
BEGIN_MESSAGE_MAP(CDlgLvl1, CDialogEx)
.
.
ON_MESSAGE(WMA_DIALOGACTION, OnDialogAction)
ON_WM_TIMER()
END_MESSAGE_MAP()
BOOL CDlgLvl1::OnInitDialog()
{
CDialogEx::OnInitDialog();
nIDTimer = SetTimer(1, 250, NULL);
return TRUE;
}
void CDlgLvl1::OnTimer(UINT_PTR nIDEvent)
{
if (nIDTimer && nIDEvent == nIDTimer)
{
KillTimer(nIDTimer);
nIDTimer = 0;
PostMessage(WMA_DIALOGACTION);
return;
}
CDialogEx::OnTimer(nIDEvent);
}
LRESULT CDlgLvl1::OnDialogAction(WPARAM wParam, LPARAM lParam)
{
CDlgLvl2 x(this);
x.DoModal();
return 0;
}
The mechanism you provided to prevent the 2nd window being displayed multiple times (the m_shownDlg variable) has been replaced by the nIDTimer check.
Please experiment with the timer's elapse value. The one I suggest (250 - 1/4 sec) is OK for most systems and imperceptible to to the user.
I wrote this in the SO editor, no actual test in VS (so it may contain some few syntax errors - pls fix them if so).
Note: You do not need to override OnWindowPosChanging() if you only want to set the position of the 2nd dialog. It's relative to its parent, so you can simply set the X Pos and Y Pos properties of the dialog's resource.
I tried your project in Visual Studio 2019:
I ran it in DEBUG mode and it works fine. The third dialogue showed up as a child of the second dialog (that is, with the correct ZORDER). The same is true for RELEASE build.
See: https://www.dropbox.com/s/8f5z5ltq3vfc10r/Test.mp4?dl=0
Update
If one of my classes I had a timer and I did this:
void CChristianLifeMinistryEditorDlg::OnTimer(UINT_PTR nIDEvent)
{
READYSTATE eState = READYSTATE_UNINITIALIZED;
if (nIDEvent == PRINT_PREVIEW_TIMER)
{
eState = m_pPrintHtmlPreview->GetReadyState();
if (eState == READYSTATE_COMPLETE)
{
KillTimer(m_uPreviewTimer);
PostMessage(WM_COMMAND,
MAKELONG(IDC_BUTTON_PRINT_PREVIEW2, BN_CLICKED));
}
}
CResizingDialog::OnTimer(nIDEvent);
}
You could adapt the principle and then just simulate pressing the button to display the second next dialog. Might work.
All
i have a doubt on property sheet in VC++. i have 2 or more mfc dialogs and i merged those dialogs in one dialog as a tab control through property sheet. can i place a control on the top of the property sheet ? is it possible ?
Thanks in advance.
If you are using CPropertySheet, you can add your controls in the OnCreate handler (as described on MSDN):
int CMyPropertySheet::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
int ret = CPropertySheet::OnCreate(lpCreateStruct);
if(ret != 0)
return ret;
CRect rect;
GetWindowRect(rect);
// ... adjust rect here...
MoveWindow(rect);
// ... move tab control down here ...
// ... add your controls above the tabctrl here ...
}
I have an interesting issue. My MFC dialog CManageDlg is calling another MFC dialog CmyMfcDlg using this call, on press of a button
void CManageDlg::OnBnClickedBt()
{
CmyMfcDlg ipmfc;
if ( ipmfc.DoModal() != IDOK )
{
return MyError;
}
}
Here is :
BOOL CmyMfcDlg ::OnInitDialog()
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
CDialog::OnInitDialog();
CString tmpStr;
UpdateData(FALSE);
CDC dc;
dc.Attach(::GetDC(this->m_hWnd));
int mx = dc.GetDeviceCaps(HORZRES);
int my = dc.GetDeviceCaps(VERTRES);
// lots of initializations
}
The problem is once OnBnClickedBt() is triggered by press of a button (ON_BN_CLICKED), CmyMfcDlg wait and does not open until mouse is moved! I do not know how these two are connected. I meant mouse move and opening the dialog.
EDIT1:
it turns out that this issue only happened when using QT User Interface, if I called the same function using UI written in MFC, it works fine with no problem!
Edit2:
I noticed also that this issue happened only when you open the dialog on the stack (modal) ipmfc.DoModal(), on the heap (modelless) everything is fine!
I need to adjust the dialog window dynamically based on its size. To do so I employ the following technique:
I load it up and get its size from the CDialog::OnInitDialog() handler.
If the size is too big, I end the dialog by calling CDialog::EndDialog
And then update global variable and reinit the dialog-derived class again with the size adjustment.
What happens is that on the second pass, some APIs start acting strangely. For instance, MessageBox does not show (thus all ASSERT macros stop working) and some SetWindowText APIs crash the app. Any idea why?
Here're the code snippets:
#define SPECIAL_VALUE -1
//From CWinApp-derived class
BOOL CWinAppDerivedClass::InitInstance()
{
//...
for(;;)
{
CDialogDerivedClass dlg(&nGlobalCounter);
m_pMainWnd = &dlg;
if(dlg.DoModal() != SPECIAL_VALUE)
break;
}
//...
}
And then from the dialog class itself:
//From CDialogDerivedClass
BOOL CDialogDerivedClass::OnInitDialog()
{
//The following API shows message box only on the 1st pass, why?
::MessageBox(NULL, L"1", L"2", MB_OK);
//...
if(checkedDialogSizeIndicatesReload)
{
this->EndDialog(SPECIAL_VALUE);
return FALSE;
}
//Continue loading dialog as usual
...
}
EDIT: I noticed by chance that if I comment out the following line it seems to work. Any idea why?
//m_pMainWnd = &dlg;
Variable dlg is not yet a window at the place where you are setting m_pMainWnd (the dialog box is displayed only after OnInitInstance returns TRUE); the Following code should work:
for(;;)
{
CDialogDerivedClass dlg(&nGlobalCounter);
// m_pMainWnd = &dlg;
if(dlg.DoModal() != SPECIAL_VALUE)
break;
}
m_pMainWnd = &dlg;
InitDialog is the last message processed before the dialog window appears on the screen - you can detect and adjust the size in place and not have the kind of funky global variable thing you are doing.
if(checkedDialogSizeIndicatesReload)
{
// look up SetWindowPos -
// I am nt sure if there is another parameter or not that is optional
int x,y,cx,cy;
WINDOWPLACEMENT wp;
GetWindowPlacement(&wp);
// calc new size here
SetWindowPos(this,x,y,cx,cy);
}
// window appears when the message handler returns
I'm having a bug in my code that's kicking my ass, so after much attempted debugging I finally decided to see if anyone else knew what my issue was.
I'm trying to add a grid object to a dialog that I have, but I keep hitting the assert mentioned in the title and I don't know why.
LONG myDialog::OnInitDialog(UINT wParam, LONG lParam)
{
BOOL bRet = super::OnInitDialog();
InitGridControl();
InitLayout();
myApp.ActiveDocChangeEvent->Attach(
RefMemberDelegate1(*this, &myDialog::OnNewDoc), this); // attach to event so I know when document is created
return bRet;
}
void myDialog::OnNewDoc(CDerivedDocument* pNewDoc)
{
pNewDoc->SetMyDialog(this); // when new document is created, set pointer to dialog
}
void myDialog::InitGridControl()
{
CRect rect;
// Get the grid area rectangle and set it up.
GetDlgItem(IDC_GRID)->GetClientRect(rect);
GetDlgItem(IDC_GRID)->MapWindowPoints(this, &rect); // replacing dummy image with the grid
m_Grid = new myGridCtrl;
bool result = m_Grid->Create(WS_CHILD | WS_BORDER | WS_VISIBLE | WS_TABSTOP, rect, this, IDC_GRID);
// Set the appropriate options
//...options...
m_Grid->InsertColumn(0, _T("Name"), 100); // doesn't seem to crash here, which means grid is created okay?
}
void myDialog::PopulateGridControl(BOOL bRedraw, CDerivedDocument * pDoc)
{
if (GetSafeHwnd() == NULL)
return;
// get handles to document and stuff
m_Grid->SetRedraw(FALSE); // ** ASSERT() CALL IS HERE **
m_Grid->RemoveAll();
// other stuff..
}
/////////////////////
// In CDocument, once it is created...
CDerivedDocument::SetMyDoc(myDialog * pDlg)
{
pDlg->PopulateGridControl(true,this);
}
Any idea what's going on? I mean, I only create the dialog once everything has been initialized, so there shouldn't be a problem there. m_Grid.Create() returns true, so creation is successful. Why is SetRedraw() hitting the assert that the m_hWnd isn't a handle to a window? Where does m_hWnd get set anyway?
Thanks for any help you can offer.
Cheers
Are you sure the dialog is created when you call
CDerivedDocument::SetMyDoc(myDialog * pDlg)?
What I see is that you are loading the grid (& dialog) from document, you should rather load the dialog and grid from the view using the document.
This may not be the direct cause of your assert trouble but nevertheless an improvement. It might just put things in the right order and fix this issue.