DDX_Control example for mfc - mfc

I am not able to get the DDX_Control working example.
When I create the dialog box, I am not able to create a reference for the control object.
Google doesn't have examples as well.
Thanks.
void CEditDialog::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
DDX_Control(pDX, IDC_COMBO1, m_cmbBox);
DDX_Text(pDX, IDC_EDIT1, m_Edit);
}
void CMFCApplicationDDEView::OnActionEdit2()
{
// TODO: Add your command handler code here
CEditDialog dlg;
CString str;
dlg.m_cmbBox.GetLBText(0, str);
if (dlg.DoModal() == IDOK)
{
MessageBox(dlg.cmbItemStr);
}
}
dlg.m_cmbBox is NULL. Why is it null and how can I reference it in my view

#barmak is correct in saying that you cannot access dialog box controls directly before the InitDialog() has executed.
However, you can set / retrieve the text of the edit portion of a combo box by using DDX_CBString, like:
// in .h file
CString m_cmbItemStr;
// in .cpp
void CEditDialog::DoDataExchange(CDataExchange* pDX)
{ CDialog::DoDataExchange(pDX);
DDX_CBString(pDX, IDC_COMBO1, m_cmbItemStr);
DDX_Text(pDX, IDC_EDIT1, m_Edit);
}
void CMFCApplicationDDEView::OnActionEdit2()
{ CEditDialog dlg;
CString str = TEXT("some value");
dlg.m_cmbItemStr = str;
if (dlg.DoModal() == IDOK)
MessageBox(dlg.m_cmbItemStr);
}

Your code for combo box and dialog box is correct but m_cmbBox.GetLBText() cannot be used before and after DoModal() because there is no window handle. Override like code below, then access combo_str instead of accessing windows
BEGIN_MESSAGE_MAP(CEditDialog, CDialog)
ON_COMMAND(IDOK, OnOK)
//...
END_MESSAGE_MAP()
BOOL CEditDialog::OnInitDialog()
{
BOOL res = CDialog::OnInitDialog();
//Dialog is created, window handles are available, set text here
return res;
}
void CEditDialog::OnOK()
{
//get text before dialog's window handles are destroyed
int sel = m_cmbBox.GetCurSel();
if (sel >= 0) m_cmbBox.GetLBText(sel, cmbItemStr);
CDialog::OnOK();
}

Related

How to handle the 'Left mouse Clicked' Event of an Edit Box in MFC

I have a Dialog with 8 Dynamic Read-Only Edit Boxes, 7/8 of them will hold different text strings, and the last one is empty. What i'm trying to do is: when a user clicks at 1 of those Edit Boxes (which hold the text string), the text will be shown in the empty Edit Box. If you guys have any ideas on how this should be done, I would be grateful.
Here are some codes that i've tried:
void CTab1::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
...
DDX_Text(pDX, IDC_TAB1CMTBOX, m_StrShow);
}
BEGIN_MESSAGE_MAP(CTab1, CDialog)
...
ON_CONTROL_RANGE(EN_SETFOCUS, 4000, 4100, &CTab1::OnEditBoxClicked)
END_MESSAGE_MAP()
void CTab1::OnEditBoxClicked(UINT nID)
{
switch (nID)
{
case 4001:
GetDlgItemText(4001, m_CmtText);
m_CmtText = m_StrShow;
UpdateData(FALSE);
break;
case 4003:
GetDlgItemText(4003, m_CmtText);
m_CmtText = m_StrShow;
SetDlgItemText(IDC_TAB1CMTBOX, m_StrShow);//This line doesn't work
UpdateData(FALSE);
break;
...
}
What I see You obviously only have swapped the variables.
void CTab1::OnEditBoxClicked(UINT nID)
{
switch (nID)
{
case 4003:
GetDlgItemText(4003, m_CmtText); // Read ctrl Text to m_CmtString
// m_CmtText = m_StrShow; // then Write immediately m_strShow to m_CmtText. Which make no sense
m_StrShow = m_CmtText; // <-- swapped
// SetDlgItemText(IDC_TAB1CMTBOX, m_StrShow); // sure? You want show the Text in IDC_TAB1CMTBOX ?
SetDlgItemText(IDC_SHOWBOX, m_StrShow); // replace IDC
UpdateData(FALSE);
break;
..
}
This is what I would do, simplify the code.
void CTab1::OnEditBoxClicked(UINT nID)
{
if (UpdateData(TRUE))
{
GetDlgItemText(nID, m_CmtText); // Read ctrl Text nID
SetDlgItemText(IDC_SHOWBOX, m_CmtText); // Show the ctrl nID Text to ShowBox
UpdateData(FALSE);
}
}

Spin Control GetPos() value is delayed

I have an edit control buddied with a spin control with the initial position set to 0. When the up arrow is clicked the edit box goes from 0 to 1, which is fine. But when I use GetPos() the MyValue is 0. When the spin control is incremented again from 1 to 2, the MyValue becomes 1. When down arrow is pressed the edit box goes from 2 to 1, but the value becomes 2. It seems that the MyValue is always one action behind the spin control.
BOOL CAlphaDlg::OnInitDialog()
{
// default code left out to keep it short ...
// TODO: Add extra initialization here
// set range and initial position
mSpinControl.SetRange(0, 3600); // range
mSpinControl.SetPos(0); // inital position
MyValue = mSpinControl.GetPos();
// display initial value in buddy editcontrol
mEditControlDisplay.Format("%d", MyValue);
UpdateData(false);
return TRUE;
}
void CAlphaDlg::OnDeltaposSpin1(NMHDR *pNMHDR, LRESULT *pResult)
{
LPNMUPDOWN pNMUpDown = reinterpret_cast<LPNMUPDOWN>(pNMHDR);
// TODO: Add your control notification handler code here
UpdateData(true);
int MyValue= mSpinControl.GetPos();
std::cout << MyValue << std::endl;
*pResult = 0;
}
I have tried getting the value from the editcontrol but the value is exhibiting the same behavior. How do I get the value of the GetPos() to match what is showing in the edit control?
Thank you in advance.
edit: Here is the full code
// AlphaDlg.cpp : implementation file
//
#include "stdafx.h"
#include "Alpha.h"
#include "AlphaDlg.h"
#include "afxdialogex.h"
#include <iostream>
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
// CAboutDlg dialog used for App About
class CAboutDlg : public CDialogEx
{
public:
CAboutDlg();
// Dialog Data
#ifdef AFX_DESIGN_TIME
enum { IDD = IDD_ABOUTBOX };
#endif
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
// Implementation
protected:
DECLARE_MESSAGE_MAP()
};
CAboutDlg::CAboutDlg() : CDialogEx(IDD_ABOUTBOX)
{
}
void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
}
BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx)
END_MESSAGE_MAP()
// CAlphaDlg dialog
CAlphaDlg::CAlphaDlg(CWnd* pParent /*=NULL*/)
: CDialogEx(IDD_ALPHA_DIALOG, pParent)
, mEditControlDisplay(_T(""))
{
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}
void CAlphaDlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
DDX_Text(pDX, IDC_EDIT1, mEditControlDisplay);
DDX_Control(pDX, IDC_SPIN1, mSpinControl);
}
BEGIN_MESSAGE_MAP(CAlphaDlg, CDialogEx)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_NOTIFY(UDN_DELTAPOS, IDC_SPIN1, &CAlphaDlg::OnDeltaposSpin1)
END_MESSAGE_MAP()
// CAlphaDlg message handlers
BOOL CAlphaDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
// Add "About..." menu item to system menu.
// IDM_ABOUTBOX must be in the system command range.
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000);
CMenu* pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu != NULL)
{
BOOL bNameValid;
CString strAboutMenu;
bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
ASSERT(bNameValid);
if (!strAboutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
}
}
// Set the icon for this dialog. The framework does this automatically
// when the application's main window is not a dialog
SetIcon(m_hIcon, TRUE); // Set big icon
SetIcon(m_hIcon, FALSE); // Set small icon
// TODO: Add extra initialization here
mSpinControl.SetRange(0, 10);
mSpinControl.SetPos(0);
int MyValue = mSpinControl.GetPos();
mEditControlDisplay.Format("%d", MyValue);
UpdateData(false);
return TRUE; // return TRUE unless you set the focus to a control
}
void CAlphaDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
if ((nID & 0xFFF0) == IDM_ABOUTBOX)
{
CAboutDlg dlgAbout;
dlgAbout.DoModal();
}
else
{
CDialogEx::OnSysCommand(nID, lParam);
}
}
// If you add a minimize button to your dialog, you will need the code below
// to draw the icon. For MFC applications using the document/view model,
// this is automatically done for you by the framework.
void CAlphaDlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // device context for painting
SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);
// Center icon in client rectangle
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;
// Draw the icon
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialogEx::OnPaint();
}
}
// The system calls this function to obtain the cursor to display while the user drags
// the minimized window.
HCURSOR CAlphaDlg::OnQueryDragIcon()
{
return static_cast<HCURSOR>(m_hIcon);
}
void CAlphaDlg::OnDeltaposSpin1(NMHDR *pNMHDR, LRESULT *pResult)
{
LPNMUPDOWN pNMUpDown = reinterpret_cast<LPNMUPDOWN>(pNMHDR);
// TODO: Add your control notification handler code here
*pResult = 0;
UpdateData(true);
int MyValue = mSpinControl.GetPos();
std::cout << mEditControlDisplay << std::endl;
std::cout << MyValue << std::endl;
}
Firstly, consider mapping your edit box to a int and then you do not need to cast to a string. And the default value is 0 to begin with so the spinner will be OK. You can also switch off the auto buddy.
In the deltapos handler you could do this:
void CMFCApplication1Dlg::OnDeltaposSpin1(NMHDR *pNMHDR, LRESULT *pResult)
{
LPNMUPDOWN pNMUpDown = reinterpret_cast<LPNMUPDOWN>(pNMHDR);
// TODO: Add your control notification handler code here
*pResult = 0;
SetDlgItemInt(IDC_EDIT1, pNMUpDown->iPos);
UpdateData(TRUE);
CString a;
a.Format(_T("%d"), iNumberValue);
AfxMessageBox(a);
}
For me the results in the popup message and the edit control are the same.
Update
You can workout what the new value would be by looking at the structure:
void CMFCApplication1Dlg::OnDeltaposSpin1(NMHDR *pNMHDR, LRESULT *pResult)
{
LPNMUPDOWN pNMUpDown = reinterpret_cast<LPNMUPDOWN>(pNMHDR);
int iCurrentPos = pNMUpDown->iPos;
if (pNMUpDown->iDelta > 0)
iCurrentPos++;
else
iCurrentPos--;
CString strNewValue;
strNewValue.Format(_T("%d"), iCurrentPos);
AfxMessageBox(strNewValue);
*pResult = 0;
}
For Consideration
You might want to give some thought to CSpinButtonCtrl::SetAccel which sets the acceleration for a spin button control.
In your case I don't think it is going to matter because your range is only 10
units. But if you had a larger range then it is possible that it would increment by more than one unit. It is just something to keep in mind.
According to this reference for UDN_DELTAPOS it says:
The iDelta member of the structure is a signed integer that contains the proposed change in position.
So you could improve the code and increment/decrement based on the iDelta value instead of 1. This would factor in for acceleration. So:
int iCurrentPos = pNMUpDown->iPos + pNMUpDown->iDelta;

CListControl Insert Column Debug Assertion Failure

In my application, I create a modal dialog that contains a mfc List Control. When I don't initialize any columns or items in the List Control, the dialog shows without error. When I try to add a column to the List Control, I get the following Debug Assertion Failed message:
If it helps, the breakpoint is at
_AFXCMN_INLINE int CListCtrl::InsertColumn(int nCol, const LVCOLUMN* pColumn)
{ ASSERT(::IsWindow(m_hWnd)); return (int) ::SendMessage(m_hWnd, LVM_INSERTCOLUMN, nCol, (LPARAM)pColumn); }
I am trying to add the column headers with the following code in OnInitDialog():
BOOL EventL::OnInitDialog()
{
m_ListEventLog.InsertColumn(0, _T("Description"), LVCFMT_LEFT, 250); //Failure happens HERE
//m_ListEventLog.InsertColumn(0, "Description", LVCFMT_LEFT, 200, 0); //I have also tried things such as this.
return FALSE;
}
I add column headers to other CListControls in my application in this way, without problems. The modal dialog is called with the code:
void ListOption::OnBnClickedEventLog()
{
EventL eventLog;
eventLog.DoModal();
}
Maybe you forgot to call the default function:
BOOL EventL::OnInitDialog()
{
BOOL res = CDialog::OnInitDialog();
m_ListEventLog.InsertColumn(0, _T("Description"), LVCFMT_LEFT, 250); //Failure happens HERE
//m_ListEventLog.InsertColumn(0, "Description", LVCFMT_LEFT, 200, 0); //I have also tried things such as this.
return res; // or return FALSE;
}
That's why ASSERT(::IsWindow(m_hWnd)) fails, because m_hWnd of ListView control is not ready. m_hWnd of dialog would not be ready either.
I had the same issue, until i added DDX_Control(pDX, IDC_LIST1, movies); to
void MainDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
DDX_Control(pDX, IDC_LIST1, movies);
}
movies - is name of the Listcontrol
CListCtrl movies;

MFC input form not accept less than 5 symbols

I have MFC dialog form with Text Edit control that allows to enter not more than 5 symbols. But how to make system not accept string less than 5 symbols?
Dialog form:
IMPLEMENT_DYNAMIC(InputDialog, CDialogEx)
InputDialog::InputDialog(CWnd* pParent /*=NULL*/)
: CDialogEx(InputDialog::IDD, pParent)
, m_edit(_T(""))
{
}
InputDialog::~InputDialog()
{
}
void InputDialog::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
DDX_Text(pDX, IDC_EDIT_INPUT, m_edit);
DDV_MaxChars(pDX, m_edit, 5);
}
BEGIN_MESSAGE_MAP(InputDialog, CDialogEx)
ON_BN_CLICKED(IDOK, &InputDialog::OnBnClickedOk)
END_MESSAGE_MAP()
Microsoft provides the source to MFC so you can see how they implemented DDV_MaxChars. Simply copy it and change the condition.
void AFXAPI DDV_MinChars(CDataExchange* pDX, CString const& value, int nChars)
{
// ...
if (pDX->m_bSaveAndValidate && value.GetLength() < nChars)
{
// ...
Handle the Kill Focus event for the text field. In the handler for the event get the length of the string that was entered. If it's less than 5, optionally pop up a message, and, set the focus back to the field.

Controls not rendering in modeless dialog box MFC

UPDATE: Possible solution. Declaring the CWait dialog in the header seems to solve this.
UPDATE2: Message pump may be the culprit. Explicit "pumping" seems to resolve problem.
Im trying to display a modal "Please Wait" Dialog box while some functions execute in an application. The dialog I want to display is this:
Im using this code to call the dialog box.
CWait dialog;
dialog.Create(IDD_WAIT);
dialog.SetWindowTextW(L"Geocoding");
dialog.ShowWindow(SW_SHOW);
mastImageGeocode(); //Working here
slvImageGeocode();
interfImageGeocode();
cohImageGeocode();
dialog.CloseWindow();
What ends up displaying is this:
I cant seem to figure out why the controls don't render.
I tried manually processing the message loop after initialization of the dialog with this approach:
MSG stMsg;
while (::PeekMessage (&stMsg, NULL, 0, 0, PM_REMOVE))
{
::TranslateMessage (&stMsg);
::DispatchMessage (&stMsg);
}
Did not really work.
I also tried the pointer approach
Cwait * dialog; //THis is in header
dialog = new CWait(this);
dialog->Create(IDD_WAIT);
dialog->SetWindowTextW(L"Geocoding");
dialog->ShowWindow(SW_SHOW);
mastImageGeocode(); //Some work here
slvImageGeocode();
interfImageGeocode();
cohImageGeocode();
dialog->CloseWindow();
delete dialog;
Am I doing something wrong.
Thanks for the help.
Update: If I call it inside the individual functions, it works fine!
It sounds like you're not updating your dialog from your main processing loop. I've put a cut down version of my MFC progress dialog below. Note that it is necessary to call SetProgress regularly to update the screen. As a more general point, if you're using modeless dialogs in MFC, you need to call OnUpdate(FALSE) for them to refresh, and ensure they have enough time to do so. Quite often when I think I need a modeless dialog, I'm actually served better by splitting the task into separate threads, i.e. the processing part gets placed in its own worker thread. YMMV.
class CProgressDialog : public CDialog
{
public:
CProgressDialog(LPCTSTR Name,int Max,CWnd* pParent = NULL);
CProgressDialog(UINT NameID,int Max,CWnd* pParent = NULL);
virtual ~CProgressDialog();
int m_Max;
void SetProgress(int Progress);
void SetRange(int Range);
enum { IDD = IDD_PROGRESS_DIALOG };
CProgressCtrl m_Progress;
protected:
virtual void DoDataExchange(CDataExchange* pDX);
protected:
virtual BOOL OnInitDialog();
DECLARE_MESSAGE_MAP()
};
CProgressDialog::CProgressDialog(LPCTSTR Name,int Max,CWnd* pParent /*=NULL*/)
: CDialog(CProgressDialog::IDD, pParent)
{
Create(CProgressDialog::IDD, pParent);
SetWindowPos(&wndTop,1,1,0,0,SWP_NOSIZE | SWP_SHOWWINDOW);
SetWindowText(Name);
m_Max = Max;
}
CProgressDialog::~CProgressDialog()
{
DestroyWindow();
}
void CProgressDialog::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
DDX_Control(pDX, IDC_PROGRESS, m_Progress);
}
BEGIN_MESSAGE_MAP(CProgressDialog, CDialog)
END_MESSAGE_MAP()
BOOL CProgressDialog::OnInitDialog()
{
CDialog::OnInitDialog();
m_Progress.SetRange32(0,m_Max);
return TRUE;
}
void CProgressDialog::SetProgress(int Progress)
{
m_Progress.SetRange32(0,m_Max);
m_Progress.SetPos(Progress);
UpdateData(FALSE);
}
void CProgressDialog::SetRange(int Range)
{
m_Max = Range;
}
You need to manually pump messages not just at the beginning of the dialog, but after updates as well.
Something like this:
void CProgressDialog::SetProgress(int Progress)
{
m_progressBar.SetPos(Progress);
MSG msg;
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) > 0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
Use:
m_progressBar.SetPos(Progress+1);
m_progressBar.SetPos(Progress);
and it will show. Don't ask me why ...
PS.: attention when reaching the last Progressstep!