How can I handle control messages on the parent CDialog? - c++

I've got some control like CtrlTree on CMyDialog.
I want to handle messages like ON_WM_LBUTTONDOWN() from CTreeCtrl in the CMyDialog class.
Is there any way in MFC to redirect message stream to parent?

The simplest way to redirect your messages is just sending a custom (WM_USER + xxx) message from your control's ON_WM_LBUTTONDOWN handler to the parent class.
Place parent's WM_LBUTTONDOWN handler code in a separate method and call this method directly.
Something like that (pseudo code), presuming that your existing code sits in HandleTreeCtrlLBDown()
CMyTreeCtrl::OnLButtonDown(..)
{
pParent ->SendMessage(WM_TREECTRLLBDOWN, 0, (LPARAM)this);
}
CControlParentDialog::OnTreeCtrlLBDown(wParam, lParam)
{
HandleTreeCtrlLBDown();
}

Related

MFC Edit control - WM_DROPFILES message register for drag and drop

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

GUI with c++. lagged when i use button

am new in C++ GUI, am modifying a code sent with a machine to me, i want to make a while loop when i click button, i tried the thread and it is still stuck.
void CDlgWriteEPC::loop()
{
// Do something
}
void CDlgWriteEPC::OnBnClickedOk()
{
std::thread loadingThread(&CDlgWriteEPC::loop, this);
loadingThread.join();
}
join blocks the current thread until the other thread is done, so that's no help.
You should start the worker thread, return immediately and the worker thread
should send some kind of message when it's done.
The function names in your example code seem to look like it's from a MSVC++ MFC
application so we'll work with that.
Simply put, windows GUI applications are event driven, each time a event happens a
WM_MESSAGE is sent. The framework receives these messages and calls the appropriate
functions to handle it. We can define our own messages and message handlers.
This way the worker thread can send such messages to the framework and it will call
our handler function.
WM_APP is defined as a starting point for private user messages, so there won't be
any conflict with already existing system messages.
(https://msdn.microsoft.com/en-us/library/windows/desktop/ms644930%28v=vs.85%29.aspx)
So, imagine we are building a MFC dialog application that searches something in a file.
If the file is big it can take a long time and to prevent blocking the main thread and
getting a 'window is not responding' message we need to do this in a worker thread. We might
also want a progress bar for example.
First, we define our own messages inside our existing dialog class starting from WM_APP + 1
and we add our handler functions, these must be of the following type:
afx_msg LRESULT (CWnd::*)(WPARAM, LPARAM)
WPARAM and LPARAM are parameters passed when posting the message, you can use them to send
custom data. In our example we can use them to send the % progress for our progress bar.
(https://msdn.microsoft.com/en-us/library/k35k2bfs.aspx)
class CMyAppDlg : public CDialogEx
{
public:
//Public so the worker thread class can use these same sames when posting a message.
enum Messages
{
MSG_ThreadProgress = WM_APP + 1,
MSG_ThreadDone
};
private:
afx_msg LRESULT OnThreadProgress(WPARAM wParam, LPARAM lParam)
{
ProgressBar->SetPos(wParam); //Worker thread posts progress as wParam
};
afx_msg LRESULT OnThreadDone(WPARAM wParam, LPARAM lParam)
{
//Get result from worker thread class and use it...
};
};
Then we need to add our messages and handlers to the message map, you should add them to the already
existing message map in the .cpp file for the dialog/document.
BEGIN_MESSAGE_MAP(CMyAppDlg, CDialogEx)
ON_MESSAGE(MSG_ThreadProgress, &CMyAppDlg::OnThreadProgress)
ON_MESSAGE(MSG_ThreadDone, &CMyAppDlg::OnThreadDone)
END_MESSAGE_MAP()
Now we can simply post these messages in our worker thread and the framework main thread will handle
the messages and update the progress bar or use the result:
class ThreadClass
{
public:
//Constructor takes a reference to our dialog class because we need the window handle
//to post a message
ThreadClass(CMyAppDlg& MyAppDlg) : mMyAppDlg(MyAppDlg) {};
void operator()() //Thread worker code...
{
while (what_we_look_for_not_found)
{
int Progress = 0;
//Search for a while and update progress variable...
//Post message to dialog asking it to update the progressbar
PostMessage(mMyAppDlg.m_hWnd, CMyAppDlg::MSG_ThreadProgress, Progress, 0);
}
//Finished searching...
StoreResult();
//Post message to dialog informing it thread is done and result can be retrieved.
PostMessage(mMyAppDlg.m_hWnd, CMyAppDlg::MSG_ThreadDone, 0, 0);
}
private:
CMyAppDlg& mMyAppDlg;
};
QT uses a similar system with SIGNAL and SLOT and other frameworks surely have their own equivalent system.
You should be able to find more information in the manuals if you are using something else then MSVC++ MFC.

MFC: Does CWnd::SendMessage() only send messages to its class, or can other classes catch it?

Let's say I have a list component class called ListCtrl that derives from CWnd.
And let's say I also have a dialog class called DialogA that derives from CDialog.
DialogA uses ListCtrl to map it to a list component. For example,
void DialogA::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
DDX_Control(pDX, IDC_LIST_CONTROL, listCtrl);
}
Where
ListCtrl listCtrl;
So if ListCtrl were to call the SendMessage(), can DialogA handle it?
If not, how can I have DialogA handle something that ListCtrl does.
Ultimately, I want DialogA to use a "copy" function of it's own when the ListCtrl right-click menu option for "Copy" is clicked, and prevent the copy function of ListCtrl from executing.
CWnd::SendMessage will send a message to the window wrapped by that CWnd derived class. So if you use SendMessage from your ListCtrl (which is a child window of your dialog), the dialog won't see it.
You either need to have the raw HWND of the dialog window and use the global SendMessage like:
::SendMessage(hWnd, WM_WHATEVER, 0, 0); // note the "::" scoping operator
Or you can possibly use the parent window of your list control (assuming that the dialog is its parent):
GetParent()->SendMessage(WM_WHATEVER, 0, 0);
In this last case, it would be more robust to ensure that GetParent() does not return NULL so perhaps:
CWnd *pParent = GetParent();
if (pParent != NULL)
pParent->SendMessage(WM_WHATEVER, 0, 0);
else
// error handling
Any window (and a control is child window) can send a message to any window in the same process.
The question is if that's a good idea for your use case.
Perhaps, if you're going to derive a class for the list control anyway, just pass it a pointer to an object it can call member functions on as appropriate for whatever it's doing.

How do I use Dialog resources in Win32?

Without resources I can create my UI with a complex array of CreateWindow() and CreateWindowEx(), and WndProc() to process my events.
I noticed if I right-click in the resource view and click "add resource", I can draw a dialog box with all the controls. This would save me a huge amount of time if I could draw the interface like I normally do with C#.
After I've drawn the interface with the resource editor, how do I then create the window from code? Can someone provide a very simple example with a button, and show how to handle a WM_COMMAND event on that button please?
Also, is this generally how people create the GUI? Is there any loss in flexible to do this way? Even in C# I often have to supplement designer-generated UI with my own code-generated UI, but the majority of the time I'm quite happy to use designer.
After creating the dialog in the resource editor, call CreateDialog(modeless dialog;you need to dispatch the messages manually just like when you use CreateWindow) or DialogBox(modal dialog; the function does not return until you close the dialog. it does the dispatching for you) to make the dialog show up. Just like you pass in the window proc to RegisterClass, you pass the dialog proc to those functions for the dialog call back. An example of DialogProc looks likes this:
BOOL DialogProc( HWND hDlg, UINT iMessage, WPARAM wParam, LPARAM lParam ){
switch( iMessage ){
case WM_COMMAND:
switch( LOWORD( wParam ) ){
case BTOK:
MessageBox( hDlg, "Hello, World!", NULL, NULL );
return TRUE;
break;
}
break;
}
return FALSE;
}
This is a basic way of creating a dialog. More sophisticated method would normally involve OOP, usually wrapping each resource( button, window, etc) as a C++ object or using MFC.
If you have placed your button or any control on some dialog, that control is already in created state. For handling the messages of these child controls on this dialog , you have to override OnCommand Method in the class which is implementing your dialog.
For Example:
//CDialog_ControlDlg is my Dialog class derived from CDialog
//IDC_BUTTON_SAMPLE is the ID of the button which was palced on the dialog in the resource Editor..
BOOL CDialog_ControlDlg::OnCommand(WPARAM wParam,LPARAM lparam){
int iNotiFicationMsg=HIWORD(wParam);//This is thenotification Msg from the child control
int iCommandId=LOWORD(wParam);//And Control ID of the Child control which caused that Msg
BOOL result=FALSE;
switch(iCommandId){
case IDC_BUTTON_SAMPLE:
if(iNotiFicationMsg==BN_CLICKED)
{
//Your Code for handling this type of Msg for this control..
}
break;
default:
{
//Specific Code;
}
return result;
}
}

How does a CRichEditCtrl know a paste operation has been performed?

It has methods like CRichEditCtrl::Copy(), CRichEditCtrl::Paste() which you can call, but I can't spot any messages the control is sent by Windows telling it to perform a paste operation. Does anyone know if such a thing exists? Or does CRichEditCtrl do something lower-level like monitoring WM_CHAR events? If so can I reuse any internal methods or would I just have to roll my own in order to override the standard paste functionality?
What I actually want is for my custom subclass (CMyRichEditCtrl : CRichEditCtrl) to ignore any formatting on text pasted in to the control. Either by getting the clipboard data in a different clipboard format, or by pasting it in as normal and immediately removing formatting on inserted text.
What I tried so far:
Checking the message for WM_PASTE in CMyRichEditCtrl::PreTranslateMessage()
Creating a method virtual void CMyRichEditCtrl::Paste()
Putting a breakpoint on CRichEditCtrl::Paste() in afxcmn.inl
Dumping every message passing through CMyRichEditCtrl::PreTranslateMessage()
Results:
1: No WM_PASTE message seen
2: It's never called
3: It's never hit... how?
4: The control never receives any WM_COMMAND, WM_PASTE or focus-related messages. Basically only mouse-move and key-press messages.
It seems other people have actually done this successfully. I'm wondering if my MFC version or something could be screwing it up, at this point.
Handle EN_PROTECTED message.
ON_NOTIFY_REFLECT(EN_PROTECTED, &YourClass::OnProtected)
// call this from the parent class
void YourClass::Initialize()
{
CHARFORMAT format = { sizeof(CHARFORMAT) };
format.dwEffects = CFE_PROTECTED;
format.dwMask = CFM_PROTECTED;
SetDefaultCharFormat(format);
SetEventMask(ENM_PROTECTED);
}
void YourClass::OnProtected(NMHDR* pNMHDR, LRESULT* pResult)
{
*pResult = 0;
ENPROTECTED* pEP = (ENPROTECTED*)pNMHDR;
if (pEP->msg == WM_PASTE)
pResult = 1; // prevent paste
}
What happens when the user requests a paste action is usually that a WM_COMMAND message with the identifier ID_EDIT_PASTE is sent to the rich edit control. By default in MFC this is handled by CRichEditCtrl::OnEditPaste(), which calls Paste() on the edit control itself.
The way I'd go about this is to derive a class from CRichEditCtrl, add an OnEditPaste method and route the message to it with a
ON_COMMAND(ID_EDIT_PASTE, OnEditPaste)
declaration, which should work. Alternatively, in your PreTranslateMessage you could look for WM_COMMAND with a wParam of ID_EDIT_PASTE.
By the way, I've solved a very similar problem to yours (paste without formatting) by just having an implementation of OnEditPaste with
void MyRichEdit::OnEditPaste()
{
SendMessage(EM_PASTESPECIAL,CF_UNICODETEXT);
}
This responds to the paste request by sending a paste message to the control that insists that the data format is plain text.
Finally, I should point out that the above technique is sufficient to catch all pastes triggered from the user interface. However, it won't catch programmatically triggered pastes, when your code sends WM_PASTE to the edit control. In those cases it's easiest to just change your code. However, if you really want to intercept such cases, you have to get your hands dirty with COM and IRichEditOleCallback::QueryAcceptData. But you almost certainly don't want to go there :-)
Windows defines messages for cut/copy/and paste. see WM_CUT.
It's probably responding to those messages rather than to WM_CHAR messages to know when to do clipboard operations.
Use the ON_MESSAGE Macro on your derived class.
ON_MESSAGE(WM_PASTE, OnPaste)
LRESULT CMyRichEditCtrl::OnPaste(WPARAM, LPARAM)
If you open the RichEdit.h file, you will notice that some of the messages are on the range of WM_USER. Maybe this is how MFC handles the events for the Rich Edit Control.
i have to perform like below
void MyRichEcit::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
if( ( GetKeyState(VK_CONTROL)<0 && nChar==88 ) || (nChar==VK_DELETE && GetKeyState(VK_SHIFT) < 0) ) //cut
{
}
if( ( GetKeyState(VK_CONTROL)<0 && nChar==86 ) || (nChar==VK_INSERT && GetKeyState(VK_SHIFT) < 0) ) //paste
{
}
CWnd::OnKeyDown(nChar, nRepCnt, nFlags);
}
Did you try CRichEditCtrl::PasteSpecial(CF_TEXT)? I believe it should do what you are wanting to do.