MFC Edit control - WM_DROPFILES message register for drag and drop - mfc

According to this article, to allow Drop Only on the Target, we have to
Use SubclassDlgItem() to re-route that one message to the dialog object so that all of the handling can be done there.
Mr. DanRollins (the author of the article) also provides an example
class CEditDropNotif : public CEdit
{
virtual BOOL PreTranslateMessage(MSG* pMsg) {
if ( pMsg->message == WM_DROPFILES ) {
GetParent()->SendMessage(WM_DROPFILES, pMsg->wParam, pMsg->lParam);
return TRUE; // eat it
}
return FALSE; // allow default processing
}
};
BOOL CMyDlg::OnInitDialog()
{
...
static CEditDropNotif cEd; // must persist (usually a dlg member)
cEd.SubclassDlgItem( IDC_EDIT1, this );
::DragAcceptFiles( cEd.m_hWnd, true ); // the editbox, not the dialog
...
But I don't understand why Edit control (CEdit) has the Accept Files in the properties window (Visual Studio Resource view), but can't register the message WM_DROPFILES for itself without having to create an inherited class (or it can but I haven't known yet).
I see that we can register the click message for button by the following code
BEGIN_MESSAGE_MAP(CSimpleDlg, CDialogEx)
...
ON_BN_CLICKED(IDC_BTN_01, &CSimpleDlg::OnBnClickedBtn01)
END_MESSAGE_MAP()
Is there a way I can do similar for drag drop event, like
ON_DRAW_DROP(IDC_TXT_01, &CSimpleDlg::OnDragDrop01)//Is exist?

The answer is: Nope.
ON_BN_CLICKED macro maps a member function that handles BN_CLICKED notification sent via WM_COMMAND message. The notifications are sent to control's parent (although MFC has also a "reflection" mechanism that forwards the notifications to controls).
WM_DROPFILES is a general Windows message, not a notification so that's it: if you want to handle it then you have to derive from CEdit.
See also:
Message Categories
Message Reflection for Windows Controls

Related

How to get event when click destroy button before document was closed

I have a problem for my MFC project as follows:
When I click on the destroy button, I want to show a messagebox "asking save file" before document closed.
But I can't.
The message is always shown after the document was closed.
A lot of places that I have placed code.
CloseDocument Function of Document Class
OnDestroy Function of MainFrame Class
Destructor Function of View Class
ExitInstance Function of App Class
But without success.
Can someone show what's wrong?
A WM_CLOSE message is sent to a window when the user wants close it. A custom message handler can decide whether it wants to initiate window destruction from here, or go back (or initiate window destruction after storing information).
In an MFC application this is reflected as the OnClose member function of CWnd or a CWnd-derived classes (such as CFrameWnd). Client code can provide a custom implementation and wire it up through the message map. When it does it should only call into the base class implementation if it wants the program to terminate.
Assuming that your CMainFrame class derives from CFrameWnd you will need to make the following changes:
MainFrm.h (add the following class member)
class CMainFrame : public CFrameWnd
{
// ...
protected:
void OnClose();
// ...
};
MainFrm.cpp (message map)
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
// ...
ON_WM_CLOSE()
END_MESSAGE_MAP()
MainFrm.cpp (implementation)
void CMainFrame::OnClose()
{
if (::AfxMessageBox(_T("Close application?"), MB_YESNO) == IDYES)
{
// Have the base class initiate destruction
CFrameWnd::OnClose();
} else {
// Do nothing
}
}
An attempt to close the application's main window pops up a confirmation dialog. If the user selects "Yes", the program terminates. Otherwise the program continues to execute. In that case the document will remain unchanged.
The simpler approach, a Oneliner as described in MSDN
'By calling this function consistently, you ensure that the framework prompts the user to save changes before closing a document. Typically you should use the default value of TRUE for the bModified parameter. To mark a document as clean (unmodified), call this function with a value of FALSE.'
BOOL CMyDocument::OnNewDocument()
{
if (!CDocument::OnNewDocument())
return FALSE;
// TODO: add reinitialization code here
// (SDI documents will reuse this document)
SetModifiedFlag(TRUE); // Set this somewhere, when you want to safe the document before the document goes off
return TRUE;
}

MFC - Changing dialog item focus programmatically

I have a Modeless dialog which shows a bunch of buttons; some of these are customized to draw stuff with GDI.
Now, when the user clicks on a customized one under certain conditions, a message box appears to alert user of the error and this is fine.
The problem is that after accepting the Message Box (showed as MB_ICON_ERROR), everywhere I click in the dialog, I always get the error message as if the whole dialog send the message to the customized button and the only way to get rid this is to press tab and give the focus to another control.
This is a strange behaviour and knowing why happens wouldn't be bad, but a simple workaround for now should do the job.
Since the moment that is probably a matter of focus, I've tried to set it on another control (in the owner dialog) by doing:GetDlgItem( IDC_BTN_ANOTHER_BUTTON )->SetFocus();
and then, inside the customized control by adding:KillFocus( NULL );but had no results.
How should I use these functions?
Thanks in advance.
PS: if I comment the AfxMessageBox, the control does not show this bizarre behaviour.
EDITI'll show some code as requested.
// This is where Message Box is popping out. It is effectively inside the dialog code.
void CProfiloSuolaDlg::ProcessLBtnDownGraphProfilo(PNT_2D &p2dPunto)
{
// m_lboxProfiles is a customized CListBox
if(m_lboxProfiles.GetCurSel() == 0)
{
// This profile cannot be modified.
/*
CString strMessage;
strMessage.Format( _T("Default Profile cannot be edited.") );
AfxMessageBox( strMessaggio, MB_ICONERROR );
*/
return;
}
// Selecting a node from sole perimeter.
SelectNodo(p2dPoint);
}
Actually, the message is commented to keep the dialog working.
// This is inside the customization of CButton
void CMyGraphicButton::OnLButtonDown(UINT nFlags, CPoint point)
{
PNT_2D p2dPunto;
CProfiloSuolaDlg* pDlg = (CProfiloSuolaDlg*)GetParent();
m_pVD->MapToViewport(point,p2dPunto);
switch(m_uType)
{
case GRF_SEZIONE:
pDlg->ProcessLBtnDownGraphProfilo(p2dPunto);
break;
case GRF_PERIMETRO:
pDlg->ProcessLBtnDownGraphPerimetro(p2dPunto);
break;
}
CButton::OnLButtonDown(nFlags, point);
}
Since you are handling the button down event in the button handler for the custom control, you don't need to call the base class. Just comment out CButton::OnLButtonDown(nFlags, point).

How to prevent MFC dialog closing on Enter and Escape keys?

I know one method of preventing an MFC dialog from closing when the Enter or Esc keys are pressed, but I'd like to know more details of the process and all the common alternative methods for doing so.
Thanks in advance for any help.
When the user presses Enter key in a dialog two things can happen:
The dialog has a default control (see CDialog::SetDefID()). Then a WM_COMMAND with the ID of this control is sent to the dialog.
The dialog does not have a default control. Then WM_COMMAND with ID = IDOK is sent to the dialog.
With the first option, it may happen that the default control has a ID equal to IDOK. Then the results will be the same that in the second option.
By default, class CDialog has a handler for the WM_COMMAND(IDOK) that is to call to CDialog::OnOk(), that is a virtual function, and by default it calls EndDialog(IDOK) that closes the dialog.
So, if you want to avoid the dialog being closed, do one of the following.
Set the default control to other than IDOK.
Set a handler to the WM_COMMAND(IDOK) that does not call EndDialog().
Override CDialog::OnOk() and do not call the base implementation.
About IDCANCEL, it is similar but there is not equivalent SetDefID() and the ESC key is hardcoded. So to avoid the dialog being closed:
Set a handler to the WM_COMMAND(IDCANCEL) that does not call EndDialog().
Override CDialog::OnCancel() and do not call the base implementation.
There is an alternative to the previous answer, which is useful if you wish to still have an OK / Close button. If you override the PreTranslateMessage function, you can catch the use of VK_ESCAPE / VK_RETURN like so:
BOOL MyCtrl::PreTranslateMessage(MSG* pMsg)
{
if( pMsg->message == WM_KEYDOWN )
{
if(pMsg->wParam == VK_RETURN || pMsg->wParam == VK_ESCAPE)
{
return TRUE; // Do not process further
}
}
return CWnd::PreTranslateMessage(pMsg);
}
The answer of #the-forest-and-the-trees is quite good. Except one situation which was addressed by #oneworld. You need to filter messages which are not for dialog window:
BOOL CDialogDemoDlg::PreTranslateMessage(MSG* pMsg)
{
if (pMsg->hwnd == this->m_hWnd && pMsg->message == WM_KEYDOWN)
{
if (pMsg->wParam == VK_RETURN || pMsg->wParam == VK_ESCAPE)
{
return TRUE; // Do not process further
}
}
return CWnd::PreTranslateMessage(pMsg);
}
Remember to add virtual in the header file.
When dealing with Dialog style MFC applications, the framework automatically hard codes a few items that must be overridden to prevent the application from quitting when the Esc or Enter keys are pressed. But there is a very simple way that doesn't require anything special such as implementing PreTranslateMessage() which is very much not recommend.
There are three functions that need to be in place:
The OnCancel() function to override the base class version and not to call it. This prevents the Esc key from closing the app.
The OnOK() function to override the base class version and not to call the base class. This prevents the Enter key from closing the app.
Because you've now prevented the dialog window from being closed you must now implement the OnClose() event handler. This function handler will handle when the Windows "X" button or the system command Close Alt+F4 are clicked. Now in order to close the application, you then call the base class version of one of the other functions OnOK(), OnCancel() if desired, to actually close the app. At this point you now have full control of how the app is closed.
Step 1
In the header, add the three function prototypes.
You can use the Class Wizard if you like to add the WM_CLOSE event handler but it's super simple to just type it in.
// DefaultDialogAppDlg.h
//
class CDefaultDialogAppDlg : public CDialogEx
{
// ... other code
protected:
virtual void OnCancel(){} // inline empty function
virtual void OnOK(){} // inline empty function
public:
afx_msg void OnClose(); // message handler for WM_CLOSE
// ...other code
};
Step 2
In the .cpp file, add the ON_WM_CLOSE() entry to the message map and the definitions for the three functions. Since OnCancel() and OnOK() are generally going to be empty, you could just inline them in the header if you want (see what I did in Step 1?).
The .cpp file will have something like this:
// DefaultDialogAppDlg.cpp
// ... other code
BEGIN_MESSAGE_MAP(CDefaultDialogAppDlg, CDialogEx)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_WM_CLOSE() // WM_CLOSE messages are handled here.
END_MESSAGE_MAP()
// ... other code
void CDefaultDialogAppDlg::OnClose()
{
// TODO: Add exit handling code here
// NOTE: to actually allow the program to end, call the base class
// version of either the OnOK() or OnCancel() function.
//CDialogEx::OnOK(); // returns 1 to theApp object
CDialogEx::OnCancel(); // returns 2 to theApp object
}
I simply override the OnOk event and instead of passing the message to the parent dialog, do nothing.
So it's basically simple as doing so:
void OnOk() override { /*CDialog::OnOK();*/ }
This should prevent the dialog from closing when pressing the return/enter key.
Make sure you don't #define CUSTOM_ID 2 because 2 is already defined for escape and I think 1 is defined for enter? Correct me if i'm wrong.

MFC OK/Cancel Dialog Button Override?

Language: C++
Development Environment: Microsoft Visual C++
Libraries Used: MFC
Pretty new to MFC, so bear with me. I have a dialog that is launched via DoModal(). I'm attempting to add buttons to this dialog that will replace the default "OK" and "Cancel" buttons. Right now, I can't quite figure out how to do this. I deleted the OK and Cancel buttons and added new ones with new IDs, added event handlers, and just some simple code for them to execute upon being pressed, but I couldn't get it to work.
I suspect it has something to do with the fact that DoModal() expects responses from OK or Cancel, but nothing else. I'm not really sure though. Any help would be greatly appreciated!
EDIT: Stripped down code added for reference.
void CPrefsDlg::Launch() {
[ ... ]
CSAPrefsDialog dlg;
INT_PTR nRet = -1;
nRet = dlg.DoModal();
// Handle the return value from DoModal
switch ( nRet )
{
case -1:
AfxMessageBox("Dialog box could not be created!");
break;
case IDABORT:
// Do something
break;
case IDOK: // This works just fine.
exit(0);
break;
case IDSAVEONE: // This does not work.
MessageBox("Save One");
break;
default:
break;
};
}
void CPrefsDlg::SaveOne()
{
// I tried adding in my own handler for 'Save One'...this does not work.
MessageBox("Save one");
}
To wire up your dialog to terminate and return IDSAVEONE, you need to add a click handler to the Save One button and have it call EndDialog:
void CSAPrefsDialog::OnBnClickedSaveone()
{
EndDialog(IDSAVEONE);
}
If you add the click handler through the dialog editor (e.g. by double-clicking on your button) then the necessary framework code will be generated for you to wire this up; otherwise you'll need to add the following line into your BEGIN_MESSAGE_MAP section in your dialog class:
ON_BN_CLICKED(IDSAVEONE, &CSAPrefsDialog::OnBnClickedSaveone)
but (as AJG85's just beaten me to posting) depending on what the operation is, how fast it is and whether you want to report errors in the preferences dialog or not, you may want to just carry out the extra function in your on-clicked handler instead.
MFC has built in ids for the ok and cancel buttons. Those being IDOK and IDCANCEL. You can either handle these in a switch via the return of DoModal() or probably better would be to override OnOK() and OnCancel() methods in your dialog class to do what you want.
You can do this by adding a line to the message map to call your handler:
Edit: The same thing works for buttons you add to the dialog which I added to my example code below:
BEGIN_MESSAGE_MAP(MyDialog, CDialog)
ON_BN_CLICKED(IDOK, &OnBnClickedOk)
ON_BN_CLICKED(IDSAVEONE, &OnBnClickedSave)
END_MESSAGE_MAP()
void MyDialog::OnBnClickedOk()
{
// do extra stuff when they click OK
CDialog::OnOK(); // call base class version to complete normal behavior
}
void MyDialog::OnBnClickedSave()
{
// this would be called for your save button with custom id IDSAVEONE
// note: no base class call here as it's specific to your dialog
}

Why doesn't OnKeyDown catch key events in a dialog-based MFC project?

I just create a dialog-based project in MFC (VS2008) and add OnKeyDown event to the dialog.
When I run the project and press the keys on the keyboard, nothing happens. But, if I remove all the controls from the dialog and rerun the project it works.
What should I do to get key events even when I have controls on the dialog?
Here's a piece of code:
void CgDlg::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
// TODO: Add your message handler code here and/or call default
AfxMessageBox(L"Key down!");
CDialog::OnKeyDown(nChar, nRepCnt, nFlags);
}
When a dialog has controls on it, the dialog itself never gets the focus. It's stolen by the child controls. When you press a button, a WM_KEYDOWN message is sent to the control with focus so your CgDlg::OnKeyDown is never called. Override the dialog's PreTranslateMessage function if you want dialog to handle the WM_KEYDOWN message:
BOOL CgDlg::PreTranslateMessage(MSG* pMsg)
{
if(pMsg->message == WM_KEYDOWN )
{
if(pMsg->wParam == VK_DOWN)
{
...
}
else if(pMsg->wParam == ...)
{
...
}
...
else
{
...
}
}
return CDialog::PreTranslateMessage(pMsg);
}
Also see this article on CodeProject: http://www.codeproject.com/KB/dialog/pretransdialog01.aspx
Many of my CDialog apps use OnKeyDown(). As long you only want to receive key presses and draw on the screen (as in make a game), delete the default buttons and static text (the CDialog must be empty) and OnKeyDown() will start working. Once controls are placed on the CDialog, OnKeyDown() will no longer be called.