MFC: Creating modeless dialog box without displaying - c++

I'm trying to create a simple modeless dialog box which I'm creating from my CWinApp derived InitInstance() function.
BOOL CMyApp::InitInstance()
{
...
m_pMyDialog = new CMyDialog();
m_pMyDialog->Create(CMyDialog::IDD);
...
retrun TRUE;
}
I've created the dialog template in the resource editor and the WS_VISIBLE bit is unset. My intention is to avoid showing the dialog until I explicitly call ShowWindow(SW_SHOW) but for some reason the call to Create displays the dialog.
I've tried to change the return value of OnInitDialog() to FALSE but that doesn't work.
I've even tried to call ModifyStyle() in case something else is setting the WS_VISIBLE bit.
int CMyDialog::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CDialog::OnCreate(lpCreateStruct) == -1)
return -1;
ModifyStyle(WS_VISIBLE, 0);
return 0;
}
That doesn't work either. In all cases, after I call Create the dialog is displayed which isn't how I've read it should work.

The problem was with AnimateWindow() which was causing the dialog to display prematurely.

Related

Show modelless dialog initially hidden

I have modelless child dialog. In resource properties Visible flag is set as true.(As per my requirement in resource properties visible flag should be true).
I want to programmatically hide the dialog while initially displaying.
I overrided the presubclasswindow and removed the WS_VISIBLE flag using below code but the dialog is not getting hidden.
void CAddressChildDlg::PreSubclassWindow()
{
CWnd::PreSubclassWindow();
if (::IsWindow(m_hWnd))
{
LONG lStyle = GetWindowLong(m_hWnd, GWL_STYLE);
lStyle &= ~WS_VISIBLE;
SetWindowLong(m_hWnd, GWL_STYLE, lStyle);
}
}
Please anyone help me to achieve my requirement
You can also override ON_WM_WINDOWPOSCHANGING
class CMyDialog : public CDialog
{
public:
bool m_override_showwindow;
//initialize somewhere ...
void OnWindowPosChanging(WINDOWPOS* wpos)
{
if (m_override_showwindow)
wpos->flags &= ~SWP_SHOWWINDOW;
CDialog::OnWindowPosChanging(wpos);
}
DECLARE_MESSAGE_MAP()
...
};
BEGIN_MESSAGE_MAP(CMyDialog, CDialog)
ON_WM_WINDOWPOSCHANGING()
...
END_MESSAGE_MAP()
Enable this override only when you don't want it to show the dialog. Make sure to disable the override otherwise dialog is never shown.
dlg.m_override_showwindow = true;
dlg.Create(...);
dlg.m_override_showwindow = false;
MessageBox(L"Test...");
dlg.ShowWindow(SW_SHOW);
You are not clear in what you want. Your title says you want to initially have the dialog hidden. then text in the question says you want it initially visible and then hidden. Which is it/
What do you mean by your requirement says that the dialog style has to be WS_VISIBLE. if you want to make it initially not visible, then don't include the flag.
For a modeless dialog, generally you create them on the heap whereas modal dialogs are typically created on the stack.
CYourDialog* pDlg = new CYourDialog(... and whatever arguments);
pDlg->Create(CYourDialog::IDD); // or whatever the ID is...
pDlg->ShowWindow(SW_NORMAL); // shows window if it was invisible...
pDlg->ShowWindow(SW_HIDE); // hides window if it was visible...

Apply button in CDialog

I have a dialog in which after pressing button OK, the program uses the data in the dialog and draws a plot. I need to draw the plot without having to close the dialog as with IDOK, hence the apply button.
The code with drawing the dialog is,
INT_PTR val = dlg->DoModal();
if ( val == IDOK) {
//draw plot
}
The code of onOK and onApply
void DLg::OnOK() {
GetDataGrid();
CDialog::OnOK();
}
void DLg::OnBnClickedApply()
{
GetDataGrid();
}
How do I get DoModal() to return a value on onApply() without closing the dialog?
Any help would be appreciated.
A modal dialog can't return a value and leave the dialog open. You could either make your dialog non-modal, or post your main window a message from the OnBnClickedApply function that makes it draw the plot.
I tend to put drawing into a separate thread and would call it wherever needed. So you can either
(1) call the OnDrawPlot again in your Apply button
if ( val == IDOK) {
AfxBeginThread(...);//draw plot
}
void DLg::OnBnClickedApply()
{
AfxBeginThread(...);//draw plot
}
(2) send the return value back to the DoModal using EndDialog method
What parameters are there in EndDialog ?
An example can be found here.
Declare a variable in CDialog derived class preferably public. Then just at OnOK assign this variable to appropriate value. The caller would use it directly.
class Dlg : public CDialog
{
public:
int TheVariable;
...
};
Call site:
if(dlg.DoModal()==IDOK)
{
dlg.TheVariable; // Use the variable
}
However, if you need to draw on the dialog itself (and not to other window, which has launched the dialog), then don't call CDialog::OnOK or EndDialog in your OnOK override. In this case, you need to do painting in dialog itself.

Child Dialog - SetWindowTextA or SendMessageA crashes program - MFC

ERROR: afxwin2.inl line 165
My app is a dialog box with a few edit boxes. Once I click the button to evaluate the information entered I want to open a child dialog to display the results. I tried overloading DoModal() like this:
//in the child dialog
//.h
CResultsDlg::CResultsDlg(CParentDlg *parent);
virtual INT_PTR DoModal(float bmi);
//.cpp
CResultsDlg::CResultsDlg(CParentDlg *parent) : CDialogEx(CResultsDlg::IDD), _parent(parent)
{ //initializations }
INT_PTR CResultsDlg::DoModal(float bmi)
{
m_sBMI.Format("%f", bmi);
m_hBMI.SetWindowTextA(m_sBMI); //crashes !!!!!!!!!!
m_hBMI.SendMessageA(WM_SETTEXT, 0, (LPARAM)"15.11"); //crashes !!!!!!!!
// OnInitDialog(); //because this wasn't getting called at all
return CDialogEx::DoModal();
}
BOOL CResultsDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
// __super::OnInitDialog(); //no difference...
m_hBMI.SetWindowTextA("10.3"); //crashes !!!
return true; // return TRUE unless you set the focus to a control
}
//in the parent dialog
//.cpp
void CParentDlg::OnBnClickedCalculate()
{
CResultsDlg childResultsDlg = this;
childResultsDlg.DoModal(15.7);
}
m_hBMI is a handle to a static text control. I tested an edit box but it still crashed.
I understand that it probably has something to do with the controls not being created yet but I tried every way I know.
Using breakpoints, I confirmed that OnInitDialog does not get called at all unless I put it in the overloaded DoModal function. SetWindowText/SendMessage still crashes in OnInitDialog with the same ASSERT error.
If I remove all SetWindowText/SendMessage then the child window does come up modal like it should but the 'result' static text control is the same as the text I set it to in the dialog editor properties pane.
Thanks !!!!!
*MORE DETAILS*-----------
void CResultsDlg::DoDataExchange(CDataExchange* pDX) // DDX/DDV support
{
CDialogEx::DoDataExchange(pDX);
DDX_Text(pDX, IDC_BMI, m_fBMI);
DDV_MinMaxFloat(pDX, m_fBMI, 0, 100);
DDX_Control(pDX, IDC_BMI, m_hBMI);
}
The usual sequence when you start a dialog is:
You call CDialog::DoModal.
The dialog window gets created.
The child controls of the dialog get created.
OnInitDialog gets called.
CDialog::OnInitDialog calls DoDataExchange.
You have a DDX_Control call in your DoDataExchange method to map the child control to a member variable.
Notice that the member variables only get initialized at the end of that sequence. You're trying to use them way before, so you get a crash.
Store the value you need for initialization in a member variable and take care of it in DoDataExchange.

Disabling dialog OK button MFC

How do I disable MFC dialog OK button?
This code:
CWnd* fieldOK = pDlg->GetDlgItem(IDOK);
fieldOK->EnableWindow(FALSE);
causes exception "Access violation reading location..."
in line ASSERT(::IsWindow(m_hWnd) || (m_pCtrlSite != NULL)); of function CWnd::EnableWindow(BOOL bEnable) in winnocc.cpp from mfc90d.dll
In this time focus is on another control.
What's can be wrong?
Thanks for help.
[EDITED]
bool CSCalcNormCell::OnSelectionChanged( CWnd* pDlg, int type, int page, UINT ctrl_id )
{
DDX_DataBox(pDX.get(), IDC_WORKSHOP_COMBO, ws_code);
if (!CInfactoryPriceAdapter::CanEditPricesForWorkshop( ws_code ))
{
CWnd* fieldOK = pDlg->GetDlgItem(IDOK);
fieldOK->EnableWindow(FALSE);
}
else
{
CWnd* fieldOK = pDlg->GetDlgItem(IDOK);
fieldOK->EnableWindow(TRUE);
}
}
I'm not sure why would wouldn't be able to do it. If I take a regular CDialog and I do an init like this:
BOOL CMyDialog::OnInitDialog() {
CDialog::OnInitDialog();
CWnd *okbtn = GetDlgItem( IDOK );
if ( okbtn ) {
okbtn->EnableWindow( FALSE );
}
return TRUE;
}
it disables the button just fine. Perhaps something else is wrong?
Try this: http://support.microsoft.com/kb/122489
How to Disable Default Pushbutton Handling for MFC Dialog
Although default button (pushbutton) support is recommended, you might
want to disable or modify the standard implementation in certain
situations. You can do this in an MFC application by following these
steps:
Load the dialog into App Studio and change the OK button identifier
from IDOK to something else such as IDC_MYOK. Also, clear the check
from Default Button property.
Use ClassWizard to create a message
handling function for this button named OnClickedMyOK. This function
will be executed when a BN_CLICKED message is received from this
button.
In the code for OnClickedMyOK, call the base class version of
the OnOK function. Here is an example:
void CMyDialog::OnClickedMyOK()
{
CDialog::OnOK();
}
Override OnOK for your dialog, and do nothing inside the function. Here is an example:
void CMyDialog::OnOK()
{
}
Run the program and bring up the dialog. Give focus to a control other
than the OK button. Press the RETURN key. Notice that CDialog::OnOK()
is never executed.
I suspect the problem comes from pDlg pointer. When you call pDlg->GetDlgItem(IDOK), is the dialog already created already?
Make a breakpoint at the line CWnd* fieldOK = pDlg->GetDlgItem(IDOK); and debug into it to see if fieldOK pointer is null or a valid pointer.
That is why I think mark's answer is very close. You can disable it onOnInitDialog` or other members of you dialog class after it showed up.
The problem you have is that the button control has not been created on the interface yet. We do not get the full vision of your problem.
Anyway, you should protect your code from crashing. It is better that your code does nothing than to crash the application. Restructuring it like this avoids the access violation problem due to the NULL pointer:
bool CSCalcNormCell::OnSelectionChanged( CWnd* pDlg, int type, int page, UINT ctrl_id )
{
DDX_DataBox(pDX.get(), IDC_WORKSHOP_COMBO, ws_code);
CWnd* fieldOK = pDlg->GetDlgItem(IDOK);
if (fieldOK)
{
if (!CInfactoryPriceAdapter::CanEditPricesForWorkshop( ws_code ))
fieldOK->EnableWindow(FALSE);
else
fieldOK->EnableWindow(TRUE);
}
}
You need to load a bitmap for the disable mode of the OK button in LoadBitmaps() function.

mfc access to formview item from dialog box

In my SDI application i need to get this behawiour. After I click on a button on the FormView, a CDialog opens. When I press the OK button on the CDialog, I call a function of the FormView. I don't want to close the CDialog. I try to do it with modeless dialog, but when i call formview function from dialog, i can't access to formview's control, like it's lost hwnd; the error is can't read memory of m_hwnd, the hwnd is ???.
This is my code:
Open modeless dialog:
CCampiDlg *m_pDialog = NULL;
HWND hCampi = NULL;
// Invoking the Dialog
m_pDialog = new CCampiDlg;
if (m_pDialog != NULL)
{
BOOL ret = m_pDialog->Create(m_pDialog->IDD, this);
if (!ret) //Create failed.
{
AfxMessageBox(_T("Error creating Dialog"));
}
m_pDialog->ShowWindow(SW_SHOW);
}
when i press the ok button in the dialog i do:
CEditorTxView pView;
box2 = (CEdit*)(GetDlgItem(IDC_CAMPI_BOX2));
box2->GetWindowTextW(campo);
pView.inserisciCampo(1, campo);
In inserisciCampo function in CEditorTxView (CFormView) i have to do operation with my control txtCtrl, but it's lost hwnd. The declaration of txtCtrl is in the CEditorTxView.h
CTx1 txtCtrl;
And initialize it in DoDataExchange function:
void CEditorTxView::DoDataExchange(CDataExchange* pDX)
{
CFormView::DoDataExchange(pDX);
DDX_Control(pDX, IDC_TX1, txtCtrl);
}
Someone can help me plz?
I can give you two answers here:
How to do what you are asking (get access to a control of the CFormView from a modeless dialog)
How to solve your underlying problem (communicate changes in a modeless dialog to the owner view)
For the first one, you have to declare a pointer to the view in the dialog class and initialize it in the constructor of the view:
class CCampiDlg : public CDialog
{
public:
CCampiDlg(CEditorTxView* pView, CWnd*pParent = NULL) // Change declaration to add pointer to view
: m_pView(pView)
{
}
// ... Whatever
private:
CEditorTxView* m_pView;
}
Now in your button handler:
CEdit* box2 = (CEdit*)(GetDlgItem(IDC_CAMPI_BOX2)); // Why not use a control variable?
box2->GetWindowTextW(campo);
m_pView->inserisciCampo(1, campo);
This should do what you are asking for. However, it is the wrong way to do it.
The problem with this approach is that the dialog knows way too much about its parent. It knows it is of type CEditorTxView and that it has a member called inserisciCampo, that takes a number and some text.
It shouldn't know that much. In fact, knowing anything about it, other than it is of type CView or even CWnd, is too much.
If the dialog knows about the view, you can't reuse the dialog with other views, and anytime the view changes its representation (what now is a textbox may be a combobox in the future, for example) the dialog must change accordingly.
The solution would be to send a message to the parent, explaining what's happened. Then the parent (the view) should know haw to handle that event. For example:
class CCampiDlg : public CDialog
{
public:
CCampiDlg(CWnd*pParent = NULL) {}
protected:
OnOk()
{
CString campo;
c_CampiBox2.GetWindowText(campo);
GetParent()->SendMessage(UWM_CAMPO2_SET, 0, (LPARAM)&campo);
}
}
In the view:
// It can be ON_REGISTERED_MESSAGE:
ON_MESSAGE(UWM_CAMPO2_SET, OnCampo2Set)
//...
LRESULT CEditorTxView::OnCampo2Set(WPARAM, LPARAM lParam)
{
CString* s = (CString*) lParam;
inserisciCampo(1, *campo);
return 0;
}
Now, you have decoupled the view and the dialog. The dialog knows nothing about the view. You can change its type, change the representation, even make it a dialog, and you don't have to change anything in the dialog. And if you need that same modeless dialog somewhere else, you just drop it there, create a message handler in the parent, and voilĂ !
For further explanations and better examples, check these articles:
Dialog and control design (Your case is explained in the section "Notifications to the environment")
Message management
On Ok button click the below code is running:
CEditorTxView pView;
box2 = (CEdit*)(GetDlgItem(IDC_CAMPI_BOX2));
box2->GetWindowTextW(campo);
pView.inserisciCampo(1, campo);
Note that, you are creating the new pView in stack and it does't attach with any window. You are not actually referring the view that already created and launched your dialog acting a parent. Revisit the above code and try the get the view:
Try the below code, if it is not working (Google it)
CFrameWnd * pFrame = (CFrameWnd *)(AfxGetApp()->m_pMainWnd);
CView * pView = pFrame->GetActiveView();