Disabling dialog OK button MFC - c++

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.

Related

MFC Send message to a button (child to parent)

I want to send a message from my child window (CDialog) to the parent window (CFormview). If I press the cancel button at the child window, the Dialog should quit and the program should continue with the code of the STOP-Button at the parent Window.
void ChildDialog::OnBnClickedCancel()
{
CDTParentView *pButtonWnd = (CDTParentView *)AfxGetMainWnd();
pButtonWnd->OnBnClickedbuttonStop();
CDialogEx::OnCancel();
}
but there is an error in this objore.cpp:
BOOL CObject::IsKindOf(const CRuntimeClass* pClass) const
{
ENSURE(this != NULL);
// it better be in valid memory, at least for CObject size
ASSERT(AfxIsValidAddress(this, sizeof(CObject)));
// simple SI case
CRuntimeClass* pClassThis = GetRuntimeClass(); <------- error
ENSURE(pClassThis);
return pClassThis->IsDerivedFrom(pClass);
}
Can anyone tell me, whats the problem?
And maybe post a better idea to send the button-clicked message?
Your code isn't actually sending a message, it's trying to call the handler directly. It's easy to simulate the clicking of a button the same way Windows would do it, then your existing code will handle it naturally.
CWnd * pMain = AfxGetMainWnd();
CWnd * pButton = pMain->GetDlgItem(ID_STOP_BUTTON);
pMain->PostMessage(WM_COMMAND, MAKEWPARAM(ID_STOP_BUTTON, BN_CLICKED), (LPARAM)pButton->m_hWnd);
AfxGetMainWnd does not return a pointer to the CFormView, it returns a pointer to the CMainFrame. If your dialog is modal you can simply check the return value of the DoModal call that displays the dialog. Or you might have better luck with calling GetParent to get a pointer to the CFormView.

Effect of destroying OK, CANCEL and HELP windows of a porperty sheet

I wanted to use a CPropertySheet based application for a project and I did not want those default OK, Cancel, Help and Apply buttons that come with a CPropertySheet class. Therefore, I destroyed those windows on OnInitDialog. Here is the code for reference:
BOOLCProductUI::OnInitDialog()
{
CPropertySheet::OnInitDialog();
CRect rect;
CButton *pTempBtn;
CButton SaveChanges;
pTempBtn = reinterpret_cast<CButton *>(GetDlgItem(IDHELP));
if (NULL != pTempBtn)
{
pTempBtn->GetWindowRect(&rect);
pTempBtn->DestroyWindow();
}
pTempBtn = reinterpret_cast<CButton *>(GetDlgItem(IDOK));
if (NULL != pTempBtn)
{
pTempBtn->DestroyWindow();
}
pTempBtn = reinterpret_cast<CButton *>(GetDlgItem(IDCANCEL));
if (NULL != pTempBtn)
{
pTempBtn->DestroyWindow();
}
pTempBtn = reinterpret_cast<CButton *>(GetDlgItem(ID_APPLY_NOW));
if (NULL != pTempBtn)
{
ScreenToClient(&rect);
pTempBtn->MoveWindow(rect);
pTempBtn->SetWindowText(_T("Save Changes"));
}
UpdateData(FALSE);
return TRUE;
}
CProductUI is a class of CPropertySheet.
However, when I compile the program using VC++2008 in Debug mode, I get a Debug Assertion Failed error message at the line
"CPropertySheet::OnInitDialog();"
Can anyone please shed some light on why this is happening?
Per How to Hide the Apply Button in CPropertySheet. Destroying window is not a proper solution to hide default buttons of property sheet. I would suggest you to use "ShowWindow()". But as you already mentioned your showwindow() also creating problem which is not possible if your calls are correct. Let it be, if your ShowWindow() is not working in "OnInitDialog()" function then better move this function to "OnCreate()". And also if it is not working then please share your whole .H and .CPP file.
You should call ShowWindow (SW_HIDE); instead of DestroyWindow();
Also there is no need to cast CWnd* returned by GetDlgItem() to CButton*.
Please also comment out your CButton SaveChanges; declaration. You
don't need it.
You can also use built-in flags to do that:
CMyPropertyPage myPage;
myPage.m_psp.dwFlags &= ~PSP_HASHELP;
myPropertySheet.AddPage(&myPage);
myPropertySheet.m_psh.dwFlags |= PSH_NOAPPLYNOW;
myPropertySheet.m_psh.dwFlags &= ~PSH_HASHELP;
IMPORTANT: In general please run your application in Debug mode to see where it ASSERTs.

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.

How to capture MouseMove event in a MFC Dialog Based application for a checkbox?

My application is a VC6 MFC dialog based application with multiple property pages.
I have to capture a mousemove event over a control, for example Checkbox.
How can I capture the mousemove events over a checkbox in MFC?
A checkbox is a button control (eg. CWnd). Derive your own class from CCheckBox and handle the OnMouseMove event.
Per request...assuming a class derived from CButton...
BEGIN_MESSAGE_MAP(CMyCheckBox, CButton)
ON_WM_MOUSEMOVE()
END_MESSAGE_MAP()
void CMyCheckBox::OnMouseMove(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
CButton::OnMouseMove(nFlags, point);
}
Thanks for your replies.. I found a way to get the mousemove event for my app.
WM_SETCURSOR windows message gets the mouse move. It returns the Cwnd pointer for a control and the dialog.
Find my code below.
BOOL CMyDialog::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
{
CWnd* pWndtooltip = GetDlgItem(IDC_STATIC_TOOLTIP);
if (pWnd != this)
{
if (IDC_SN_START_ON == pWnd->GetDlgCtrlID())
pWndtooltip->ShowWindow(SW_SHOW);
}
else
pWndtooltip->ShowWindow(SW_HIDE);
SetCursor(AfxGetApp()->LoadStandardCursor(IDC_ARROW));
return true;
}
I found in #raj's OnSetCursor() code, that the associated Member variable for IDC_STATIC_TOOLTIP is that variable to which you assign the desired tool tip text. For example, if the associated variable is m_strToolTip, assign the desired text to display during the hovering event as follows:
m_strToolTip.Format("%s", "Tool tip text goes here");
I also found that UpdateData() was required upon entry into the event handler and UpdateData(FALSE) was required before the return. The SetCursor() call seems to have no effect when commented.
You can also override CDialog::PreTranslateMessage:
BOOL CSomeDlg::PreTranslateMessage(MSG* pMsg)
{
if (pMsg->message == WM_MOUSEMOVE && pMsg->hwnd == m_checkBox->m_hWnd)
{
...
}
return CDialog::PreTranslateMessage(pMsg);
}

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();