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.
Related
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);
}
}
I want to drag file on dialog and get the file's path.
So I searched web and tried that.
MyDlg.cpp
KmCdmMakeMultiProjectDlg::KmCdmMakeMultiProjectDlg(CWnd* pParent)
: CDialog (KmCdmMakeMultiProjectDlg::IDD, pParent)
{
}
void KmCdmMakeMultiProjectDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
DDX_Control(pDX, IDC_LIST_MULTIPART, lst_AddList);
DDX_Control(pDX, IDC_BTN_ADD_PROJECT, btn_AddList);
DDX_Control(pDX, IDC_BTN_ADDLIST_CSV, btn_AddList_CSV);
DDX_Control(pDX, IDC_BTN_DEL_PROJECT, btn_DelList);
DDX_Control(pDX, IDC_BTN_TARGET_SELECT, btn_ReferFolder);
DDX_Control(pDX, IDC_BTN_FILE_SELECT, btn_ReferCSV);
DDX_Control(pDX, IDC_BTN_EXECUTE, btn_Execute);
DDX_Control(pDX, IDC_BTN_EDIT_NAME, btn_EditName);
DDX_Control(pDX, IDC_BTN_EDIT_DESCRIPTION, btn_EditDescription);
DDX_Control(pDX, ID_CLOSE, btn_Close);
}
BEGIN_MESSAGE_MAP(KmCdmMakeMultiProjectDlg, CDialog)
ON_BN_CLICKED(IDC_BTN_ADD_PROJECT, &KmCdmMakeMultiProjectDlg::AddList)
ON_BN_CLICKED(IDC_BTN_ADDLIST_CSV, &KmCdmMakeMultiProjectDlg::AddListCSV)
ON_BN_CLICKED(IDC_BTN_DEL_PROJECT, &KmCdmMakeMultiProjectDlg::DelList)
ON_BN_CLICKED(IDC_BTN_TARGET_SELECT, &KmCdmMakeMultiProjectDlg::SelectPath)
ON_BN_CLICKED(IDC_BTN_FILE_SELECT, &KmCdmMakeMultiProjectDlg::SelectCSV)
ON_BN_CLICKED(IDC_BTN_EXECUTE, &KmCdmMakeMultiProjectDlg::MakeExecute)
ON_BN_CLICKED(IDC_BTN_EDIT_NAME, &KmCdmMakeMultiProjectDlg::EditName)
ON_BN_CLICKED(IDC_BTN_EDIT_DESCRIPTION, &KmCdmMakeMultiProjectDlg::EditDescription)
ON_BN_CLICKED(ID_CLOSE, &KmCdmMakeMultiProjectDlg::CloseDialog)
ON_WM_DROPFILES()
END_MESSAGE_MAP()
BOOL KmCdmMakeMultiProjectDlg::OnInitDialog()
{
CDialog::OnInitDialog();
CDialog::DragAcceptFiles();
}
void KmCdmMakeMultiProjectDlg::OnDropFiles(HDROP hDropInfo)
{
CString csfile = "Why don't come this break point!";
CDialog::OnDropFiles(hDropInfo);
}
I thought it is not difficult.
Just set message ON_WM_DROPFILES() in dialog,
And Set CDialog::DragAcceptFiles(); in dialog's OnInitDialog() method.
I expected that OnDropFiles(HDROP hDropInfo) is run if I drag a file on dialog.
I have tested with debug mode, and checked break point in OnDropFiles method.
But didn't occur anything even though I dropped a file.
Have you any some idea?, waiting your teaching.
Thank you.
If application run as administrator, must include this 2 line before DragAcceptFiles();
For example.
BOOL KmCdmMakeMultiProjectDlg::OnInitDialog()
{
CDialog::OnInitDialog();
ChangeWindowMessageFilter(0x0049, MSGFLT_ADD);
ChangeWindowMessageFilter(WM_DROPFILES, MSGFLT_ADD);
CDialog::DragAcceptFiles();
}
Points are ChangeWindowMessageFilter.
Thank you.
In the name of the C++ an C, do not use ChangeWindowMessageFilter.
This deals with Privilege Isolation (UIPI) message filter and has nothing to do with drag and drop support.
You have to call DragAcceptFiles for any windows object that is going to accept drag and drop by processing the WM_DROPFILES message.
It has to be called when the object is attached to a window (valid m_hWnd)
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();
}
I have dialog box having buttons and edit box.
When edit control have focus then if I press tab key it moves and focus the button.
I wanted tab key work in such a way that it will not switch focus instead it should work as tab input inside edit control i.e. input to edit box as keys.
The solution is fairly simple, and essentially consists of handling the WM_GETDLGCODE message. This allows a control implementation to fine-tune keyboard handling (among other things).
In MFC this means:
Derive a custom control class from CEdit.
Add the ON_WM_GETDLGCODE message handler macro to the message map.
Implement the OnGetDlgCode member function, that adds the DLGC_WANTTAB flag to the return value.
Subclass the dialog's control, e.g. using the DDX_Control function.
Header file:
class MyEdit : public CEdit {
protected:
DECLARE_MESSAGE_MAP()
public:
afx_msg UINT OnGetDlgCode();
};
Implementation file:
BEGIN_MESSAGE_MAP(MyEdit, CEdit)
ON_WM_GETDLGCODE()
END_MESSAGE_MAP
UINT MyEdit::OnGetDlgCode() {
UINT value{ CEdit::OnGetDlgCore() };
value |= DLGC_WANTTAB;
return value;
}
Override the PreTranslateMessage function in your dialog like this :
BOOL CTestThreadDlg::PreTranslateMessage( MSG* pMsg )
{
if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_TAB)
{
CWnd* pFocusWnd = GetFocus( );
if (pFocusWnd != NULL && pFocusWnd->GetDlgCtrlID() == IDC_EDIT2)
{
CEdit *pEditCtrl = (CEdit *)pFocusWnd ;
int start, end ;
pEditCtrl->GetSel(start, end) ;
CString str ;
pEditCtrl->GetWindowText(str) ;
str = str.Left(start) + _T("\t") + str.Mid(end) ;
pEditCtrl->SetWindowText(str) ;
pEditCtrl->SetSel(start + 1, start + 1) ;
}
return TRUE ;
}
return CDialog::PreTranslateMessage(pMsg) ;
}
In this example we check if the focus is in the IDC_EDIT2 edit control. You probably have to adapt this to your situation.
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!