C++/MFC Error accessing control's variable - c++

I created a control's variable for CEdit:
class CGateDlg : public CDialog
{
...
public:
// here is my control's variable
CEdit m_edit_a;
// here I map variable to control
virtual void DoDataExchange(CDataExchange* pDX);
}
And this is how I map my variable to the control:
void CGateDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
DDX_Control(pDX, IDC_EDIT_A, m_edit_a);
}
This is how it works: user types some text into the edit box. Then he presses the "Reset" button which clears the edit box. This is a piece of code responsible for clearing edit box after clicking Reset button:
void CGateDlg::OnBnClickedReset()
{
// clear edit box
m_edit_a.SetWindowTextW(L"");
}
Application starts without any errors. I type some text into EditBox and hit "Reset" button. Then I get an error which leads me to winocc.cpp, line 245 (ENSURE(this)):
void CWnd::SetWindowText(LPCTSTR lpszString)
{
ENSURE(this);
ENSURE(::IsWindow(m_hWnd) || (m_pCtrlSite != NULL));
if (m_pCtrlSite == NULL)
::SetWindowText(m_hWnd, lpszString);
else
m_pCtrlSite->SetWindowText(lpszString);
}
I think the problem is with the hWnd:
this 0x0030fa54 {CEdit hWnd=0x00000000} CWnd * const
but how to fix it ?
Everything works fine when I access my control's value using this:
CEdit *m_edit_a;
m_edit_a = reinterpret_cast<CEdit *>(GetDlgItem(IDC_EDIT_A));
m_edit_a->SetWindowTextW(L"");
What am I doing wrong ?

I can see two possibilities:
The control does not exist when the dialog starts. The first thing that CDialog::OnInitDialog will do is call DoDataExchange, so if you're creating the control later in the initialization process it's too late.
Your own OnInitDialog is not calling CDialog::OnInitDialog so DoDataExchange is not being called.

I think you should no use directly the meber of your control (in this case m_edit_a). Instead you should use a memeber variable, let's say CStrimg m_edit_data, and you should link it to the control:
DDX_Text(pDX, IDC_EDIT_A, m_edit_data); // as you did it in DDC_Cotrol
Now you can use directy the variable, but in order the control to be updated you should use the following code before using it:
UpdateData(true); // unlocks the control in a sense
m_edit_data = "this is my test";
UpdateData(false); // locks the control again (in a sense)
This is normal procedure in MFC :), hope I helped...
ohh... you should also add the control to String Table ... (let me know if you do not know)

I can not find something wrong with you. I Create a new project using VC6.0,and associate a variable to the Edit,just link you do. the exe operates normally.
class CEditTestDlg : public CDialog
{
// Construction
public:
CEditTestDlg(CWnd* pParent = NULL); // standard constructor
// Dialog Data
//{{AFX_DATA(CEditTestDlg)
enum { IDD = IDD_EDITTEST_DIALOG };
CEdit m_Edit;
//}}AFX_DATA
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CEditTestDlg)
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
//}}AFX_VIRTUAL
......
.cpp
void CEditTestDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CEditTestDlg)
DDX_Control(pDX, IDC_EDIT1, m_Edit);
//}}AFX_DATA_MAP
}
void CEditTestDlg::OnBnClickedReset()
{
// TODO: Add your control notification handler code here
m_Edit.SetWindowText("tttt");
}
so,I think it is not a code problem.You had better try again.

If your dialog starts off calling CDialog::OnInitDialog() and your DoDataExchange starts off calling CDialog::DoDataExchange but still you have null hWnd pointers and get CNotSupportedException, make sure your resource (rc) file's dialog template includes all the controls (IDC_) and such you have in DoDataExchange.
Check for overriding definitions if using a DLL that also provides resources.

Related

Create MFC controls in CDialog constructor [win32/MFC]

I'm working on the library, that wraps some MFC classes and methods. I want the user to be able to dynamically create a CDialogEx using a template in memory. For the modal dialogs, I call CDialog::InitModalIndirect and then CDialog::DoModal. For the modeless dialogs, I call CDialog::CreateIndirect and then CWnd::Show.
The code looks something like this:
// inside my library
class MyDialog : public CDialogEx
{
public:
MyDialog(CWnd* parent) : CDialogEx()
{
parent_ = parent;
my_template_data_ = CreateSomeGenericTemplate();
// OnInitDialog should be preferably called here
}
void ShowModal()
{
InitModalIndirect(my_template_data_, parent_);
DoModal(); // but it's called here - too late
}
void ShowModeless()
{
CreateIndirect(my_template_data_, parent_);
Show(); // but it's called here - too late
}
MyButton* GetButton(int id)
{
// returns the instance of my MyButton, which is a subclassed CButton
}
private:
BOOL MyDialog::OnInitDialog() override
{
CDialogEx::OnInitDialog();
// CWnd::Create for the UI controls can only be called here
}
};
// user's code
// user creates the dialog - in the constructor it's not clear if modal or modeless
1. MyDialog user_dialog(some_parent); // here, I need the controls to be created
2. user_dialog.GetButton(42)->SetWindowText(L"new text"); // user wants to initialize his controls
// but he can't, because MyButton::Create was not called yet
3. user_dialog.ShowModal(); // and only then display the dialog
//by default, here the MFC calls OnInitDialog - too late,
//the SetText method needed to be set on line 2.
My problem is, that the dialog's controls (buttons etc.) can only be created inside the CDialog::OnInitDialog method, which is called automatically after DoModal (for modal)/Show (for modeless) methods. I need the controls to be created and properly initialized (with the CWnd::Create method) preferably inside the constructor. I thought about calling Show/DoModal directly inside the constructor but I don't yet know if it's going to be modal or modeless dialog. It there a solution to this? Many thanks in advance.
Why don't you refactor your code and put the common code in a InitUI() method and call it from both sides?
And don't init interface on the constructor. Do it in OnInitDialog for modal; and in Create, or OnCreate (which implies a ON_WM_CREATE() map entry) for modeless dialogs.

MFC Edit control message handle access Document object

I create a new class extend CEdit to override some of the message handles.
My ultimate goal is when edit control is on focus, some of the toolbar buttons become available.
I created a bool variable in doc. then the pCmdUI->enable() set to this bool. The onfocus is overridden in new edit control class. I'm having trouble to update this bool vairbale from the onfocus message handle.
void CMFCDoc::OnUpdateTextColor(CCmdUI *pCmdUI)
{
// TODO: Add your command update UI handler code here
pCmdUI->Enable(shape_onfocus_);
}
class CMFCDoc : public COleServerDoc
{
...
bool shape_onfocus_;
}
//edit control
#include <afxwin.h>
class CEditControl :
public CEdit
{
public:
CEditControl();
~CEditControl();
DECLARE_MESSAGE_MAP()
afx_msg void OnEnSetfocus();
};
void CEditControl::OnEnSetfocus()
{
//----- I want to update shape_onfocus_ here. -----
this->SetWindowTextA(_T("Hello world"));
}
Assuming your CEditControl instance is a child of some sort of CView, you could go about it like this:
void CEditControl::OnEnSetfocus()
{
CView *view = static_cast<CView *>(GetParent());
CMFCDoc *doc = static_cast<CMFCDoc *>(view->GetDocument());
doc->shape_onfocus_ = true;
...
}
Assuming the edit-control is a child of a CView-derived class, you should better put the OnUpdateUI() handler in the view class, not the document one.
For example, if the view-class is CFormView-derived (dialog), you could simply write:
void CMyView::OnUpdateTextColor(CCmdUI *pCmdUI)
{
pCmdUI->Enable(GetFocus()==GetDlgItem(IDC_MYEDIT));
}
This piece of code works for both SDI and MDI applications.
If the view class is not CFormView-derived (the edit-box was created programmatically), the code above could be modified slightly, and instead of calling GetDlgItem() you should enumerate the view's children list (search your edit-box there).
If the only reason to override the edit-control was to capture the EN_SET/KILLFOCUS messages, sorry this wasn't worth the effort, as you could simply capture these in the view's code. Then the view's message-map would contain:
ON_EN_SETFOCUS(IDC_MYEDIT, &CMyView::OnEnSetfocusMyEdit)
ON_EN_KILLFOCUS(IDC_MYEDIT, &CMyView::OnEnKillfocusMyEdit)
and the view-class code:
void CMyView::OnEnSetfocusMyEdit()
{
// TODO: Add your control notification handler code here
}
void CMyView::OnEnKillfocusMyEdit()
{
// TODO: Add your control notification handler code here
}
These are generated by the wizard. Go to the Class View tab, select your class and then go to the Events page; in the Controls subtree you can find your control and add handlers for its events. But all this is not needed, as you can just use GetFocus()/GetDlgItem() as suggested above.
And as other members said, you can access the document class from any of its views by calling the GetDocument() function.

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.

Changing static text in dialog box at runtime

I have created a dialog box and linked it to the menu item. In this case the menu item is Help -> Statistics. It all works. So when I run the program, click on the menu Help, then Statistics, a dialog box pops up.
I also have a static text box in the dialog box. How do you change the text of this static text box at runtime?
P.S: Though I have a dialog box up and running, I do not have the handle for the dialog box. If any of your solutions involve knowing the handle to the dialog box, please tell me how to retrieve it. Thanks.
EDIT:
class CStatisticsDlg : public CDialogEx
{
public:
CStatisticsDlg();
// Dialog Data
enum { IDD = IDD_STATISTICS };
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
// Implementation
protected:
DECLARE_MESSAGE_MAP()
public:
};
CStatisticsDlg::CStatisticsDlg() : CDialogEx(CStatisticsDlg::IDD)
{
}
void CStatisticsDlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
}
BEGIN_MESSAGE_MAP(CStatisticsDlg, CDialogEx)
END_MESSAGE_MAP()
In Class Wizard, create a CString member variable for the label. Note: by default, labels don't have a custom id so you have to give it one like IDC_MY_LABEL.
Somewhere before showing the dialog call m_strMyLabel.SetWindowText("blah");
If you need to do it while the dialog is open you have to call UpdateData(FALSE)
Edit: if you don't want to create a member variable you can
**corrected - typing from memory....
// Find the label
// if called from within CStatusDlg class
CWnd *label = GetDlgItem(IDC_MY_LABEL);
label->SetWindowText("blah");
// If called from elsewhere
CStatusDlg dlg..... // create the dialog
CWnd *label = dlg.GetDlgItem(IDC_MY_LABEL);
label->SetWindowText("blah");

Catching when user selects an item from a CComboBox

This is as basic as it gets.
I want to catch when the user selects an item from a CComboBox (actually, a subclass of CComboBox).
Tried lots of combinations of OnCblSelChange, OnCommand. Guess I haven't hit the right combo yet (no pun intended).
OS is Vista but I'm forcing an XP-style dialog (That shouldn't matter, should it?)
I'm able to catch events for classes derived from CEdit and CFileDialog.
I am at my wits end here. Any assistance would be ever-so appreciated.
Any source code would, of course, be more than ever-so appreciated.
Unfortunately, it seems that all messages (even SELEND_OK) for combo box changing are sent before the text has actually changed, so DoDataExchange will give you the previous text in the CComboBox. I have used the following method, as suggested by MSDN:
void MyDialog::DoDataExchange(CDataExchange* pDX)
{
DDX_Text(pDX, IDC_COMBO_LOCATION, m_sLocation);
CDialog::DoDataExchange(pDX);
}
BEGIN_MESSAGE_MAP(MyDialog, CDialog)
ON_CBN_SELENDOK(IDC_COMBO1, &MyDialog::OnComboChanged)
ON_CBN_EDITUPDATE(IDC_COMBO1, &MyDialog::OnComboEdited) // This one updates immediately
END_MESSAGE_MAP()
...
void MyDialog::OnComboChanged()
{
m_myCombo.GetLBText(m_myCombo.GetCurSel(), m_sSomeString);
}
void MyDialog::OnComboEdited()
{
UpdateData();
}
It seems to work quite nicely.
CBN_SELENDOK should be the message that you're looking for. It's sent after the user selection is finalized but before the combo box closes up (if it does). CBN_SELCHANGE is sent before the selection is actually saved to the combo box control.
This MSDN link has more information (you've likely seen it already...)
Here is the code I promised you. One thing I noticed when gathering this up is that it is possible to have this message suppressed if you are using an ON_CONTROL_REFLECT handler within the class derived from CComboBox. This would cause the control itself to handle the message and not pass it on to the parent. You can get around that problem by using ON_CONTROL_REFLECT_EX with the proper return code, which will make both the box itself and the parent receive the message.
Anyway, here's the code snippet:
class SPC_DOCK_CLASS ProcessingExceptionDockDlg : public CSPCDockDialog
{
SPC_DOCK_DECLARE_SERIAL(ProcessingExceptionDockDlg);
public:
// ... redacted ...
//{{AFX_DATA(ProcessingExceptionDockDlg)
CComboBox m_comboFilter;
//}}AFX_DATA
//{{AFX_VIRTUAL(ProcessingExceptionDockDlg)
protected:
virtual void DoDataExchange(CDataExchange* pDX);
//}}AFX_VIRTUAL
protected:
//{{AFX_MSG(ProcessingExceptionDockDlg)
afx_msg void OnSelendokComboTreeFilter();
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
/****************/
// ProcessingExceptionDockDlg.cpp : implementation file
//
#include "stdafx.h"
#include "resource.h"
#include "ProcessingExceptionDockDlg.h"
// ... much code redacted ...
void ProcessingExceptionDockDlg::DoDataExchange(CDataExchange* pDX)
{
CSPCDockDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(ProcessingExceptionDockDlg)
DDX_Control(pDX, IDC_COMBO_TREE_FILTER, m_comboFilter);
//}}AFX_DATA_MAP
}
BEGIN_MESSAGE_MAP(ProcessingExceptionDockDlg, CSPCDockDialog)
//{{AFX_MSG_MAP(ProcessingExceptionDockDlg)
ON_CBN_SELENDOK(IDC_COMBO_TREE_FILTER, OnSelendokComboTreeFilter)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
void ProcessingExceptionDockDlg::OnSelendokComboTreeFilter()
{
// ... code redacted ...
}