MFC Rich Edit Control 2.0 receiving click event - c++

I was hoping someone out there would help me with my predicament I ran into. Essentially I have a Checkbox and a RichEditControl next to each other. I want to be able to know when a user has clicked on my RichEditControl so i can send a message to my checkbox to flag it on and off.
At first i tried to overlay my checkbox with empty text to act as a "blank" background for my RichEditControl so i wouldn't have to worry about sending messages left and right. No matter what i tried the "blank" background would overlap the RichEditControl text and leave it completely blank.
I searched on here for some help and i found this which is exactly what I ran into. I understand what he is saying but don't have the knowledge to implement what they said.
Right now I'm playing around with EN_LINK to attempt to capture a message so i can tell my checkbox to flag itself.
BEGIN_MESSAGE_MAP(TempInit, CDialog)
ON_NOTIFY(EN_LINK, IDC_TempInitMsg, &TempInit::OnEnLinkTempinitmsg)
END_MESSAGE_MAP()
void TempInit::OnEnLinkTempinitmsg(NMHDR *pNMHDR, LRESULT *pResult)
{
ENLINK *pEnLink = reinterpret_cast<ENLINK *>(pNMHDR);
// TODO: Add your control notification handler code here
// TODO: Add your control notification handler code here
radioClicked = !radioClicked;
if (radioClicked == true)
{
GetParent()->SendMessage(WM_MYRADIOCLICKED, CHECKENABLED, 0);
}
else
{
GetParent()->SendMessage(WM_MYRADIOCLICKED, CHECKDISABLED, 0);
}
}
*pResult = 0;
}
I'm sorry in advance if this is totally the wrong way to go about this. I've been googling for a few hours and have come empty handed. If anyone has any other method please help me if possible. I can post more code if what i have above isn't enough.

Steven,
One way to go about this would be to handle the EN_MSGFILTER notification from the rich edit control. I can't provide you any code to show you how to do this off hand but here's the documentation for the Notification messages from that the Rich edit control generates. Simply handle it the same way your doing with your radio button.

This will check the check box when the Rich Edit Ctrl has the focus and untick
it when it losses the focus.
BEGIN_MESSAGE_MAP(TempInit, CDialogEx)
ON_EN_SETFOCUS(IDC_RICHEDIT21, &TempInit::OnEnSetfocusRichedit21)
ON_EN_KILLFOCUS(IDC_RICHEDIT21, &TempInit::OnEnKillfocusRichedit21)
END_MESSAGE_MAP()
void CMFCApplication1Dlg::OnEnSetfocusRichedit21()
{
CButton* pCheckBox = (CButton*)GetDlgItem(IDC_CHECK1);
pCheckBox->SetCheck(1);
}
void CMFCApplication1Dlg::OnEnKillfocusRichedit21()
{
CButton* pCheckBox = (CButton*)GetDlgItem(IDC_CHECK1);
pCheckBox->SetCheck(0);
}

Related

MFC: Using ContextMenuManager to TrackPopupMenu causes CTreeView item to retain TVGN_DROPHILITE status from time to time

I have a weird issue with a CTreeView context menu. I was just calling pPopup->TrackPopupMenu() as is active in the code below. No problems, but doesn't automatically update status text and icons. So searching the Internet I found there is a ContextMenuManager for this in the MFC Feature Pack (I'm now using the BCGControlBar Pro which is what the feature pack was based on).
I tried using the ContextMenuManager in the code below (change the #if 1 to 0) and while it works, I find that sometimes (many times) afterwards the selected tree item will not show the highlight, it just flashes and goes back to the item that was right clicked on like TVGN_DROPHIILITE is still on. (I confirmed TVGN_DROPHILITE is what the right click uses to select the tree item via the debug print items on the OnNMRClick() function). Also if I enabled the treeCtrl.SelectDropTarget(NULL) it fixes the issue but I shouldn't have to do that?
I'd really like to use the ContextMenuManager but this issue is a show stopper. Does anyone know what is going on?
void CMyTreeView::OnNMRClick(NMHDR *pNMHDR, LRESULT *pResult)
{
CDebugPrint::DebugPrint(_T("NMRClick: In DropHighlightItem %p\n"), GetTreeCtrl().GetDropHilightItem());
// Send WM_CONTEXTMENU to self
SendMessage(WM_CONTEXTMENU, (WPARAM)m_hWnd, GetMessagePos());
CDebugPrint::DebugPrint(_T("NMRClick: Out DropHighlightItem %p\n"), GetTreeCtrl().GetDropHilightItem());
*pResult = 0;
}
void CMyTreeView::OnContextMenu(CWnd* pWnd, CPoint ptMousePos)
{
HTREEITEM htItem;
CTreeCtrl &treeCtrl=GetTreeCtrl();
//
// ...
//
// the popup is stored in a resource
CMenu menu;
menu.LoadMenu(IDR_TREE_CONTEXT_MENU);
CMenu* pPopup = menu.GetSubMenu(0);
#if 1
UINT id=pPopup->TrackPopupMenu(TPM_LEFTALIGN|TPM_RETURNCMD, ptMousePos.x, ptMousePos.y, this);
#else
CBCGPContextMenuManager *manager = theApp.GetContextMenuManager();
UINT id;
if (manager) {
id=manager->TrackPopupMenu(pPopup->GetSafeHmenu(), ptMousePos.x, ptMousePos.y, this);
// treeCtrl.SelectDropTarget(NULL); // fixes issue
}
else id=0;
#endif
//
// ...
//
}
The TVGN_DROPHILITE is a temporary selection, only valid for the duration of drag-and-drop operation. Why are you messing with that?
You should use TVGN_CARET, if anything.
However, the problem is that right-click doesn't select the clicked item. If you like that behavior (I do), select it yourself.
It is also strange that you // Send WM_CONTEXTMENU to self - the WM_CONTEXTMENU should be sent to you by the system, in response to right click.

How can I force a modeless dialogue to close?

I have seen a similar question here:
How to close a modeless MFC dialog in C++
This is how I close my modeless dialogue:
void CChristianLifeMinistryEditorDlg::OnDestroy()
{
if (m_pAssignHistoryDlg != NULL)
{
delete m_pAssignHistoryDlg;
m_pAssignHistoryDlg = NULL;
}
CDialogEx::OnDestroy();
}
This has always been sufficient, until today. What I have found out (quote by chance) is that if the modeless dialogue has a message box up, and the user goes and closes the parent dialogue, the modeless dialogue stays on screen until you close the message box.
The modelless dialogue is clearly still within an event handler since it has just performed some action and displayed the results to the user.
Is it possible to force it to shut? Or should I somehow stop my parent dialogue from being closed whilst the modelless popup is on screen?
What is the right thing to do?
Thank you.
Update:
I tried this:
void CChristianLifeMinistryEditorDlg::OnDestroy()
{
if (m_pAssignHistoryDlg != NULL)
{
CWnd *pPopupMessage = m_pAssignHistoryDlg->GetWindow(GW_ENABLEDPOPUP);
if (pPopupMessage != NULL)
pPopupMessage->PostMessage(WM_CLOSE);
delete m_pAssignHistoryDlg;
m_pAssignHistoryDlg = NULL;
}
CDialogEx::OnDestroy();
}
It didn't work. When I debugged, it refused to process that call etc until I had dismissed the popup message box myself.
Things are further complicated by the fact the the modelless dialogue makes use of two message boxes:
Are you sure you want to delete the name? YES | NO
Name deleted OK
Either the code can be changed to work, or if I go the custom message route I need to know the result of the message box before my code can proceed (for scenario 1).
Update 2:
I am now confused. I added a custom registered message in the parent dialogue:
afx_msg LRESULT CChristianLifeMinistryEditorDlg::OnDisplayMessage(WPARAM wParam, LPARAM lParam)
{
return AfxMessageBox((LPCTSTR)wParam, (UINT)lParam);
}
The modeless dialogue invokes it like this:
iResult = ::SendMessage(GetParent()->GetSafeHwnd(), UWM_DISPLAY_MESSAGE_MSG,
(WPARAM)strEntry.GetBuffer(_MAX_PATH), MB_YESNO | MB_ICONQUESTION);
strEntry.ReleaseBuffer();
I have placed a breakpoint in the message handler so I know it is being fired correctly. And indeed a message box is being displayed.
But for some reason, the message box is still showing central to the modeless dialogue and not the editor. I don't understand why?
Result!
I found this topic: How to force AfxMessageBox to center on mainframe and not whatever child window that currently has focus
I adjusted my code:
afx_msg LRESULT CChristianLifeMinistryEditorDlg::OnDisplayMessage(WPARAM wParam, LPARAM lParam)
{
return MessageBox((LPCTSTR)wParam, NULL, (UINT)lParam);
}
Now it shows center of the parent dialogue.
I do apologize that this question started of in one direction and ended up with an answer in another direction. I am not sure what we do about that? Technically, the comment to me to use GetWindow didn't seem to work for me. So as such, there is no answer. But by changing my design I have been able to prevent the situation.
Premature Result
Whilst the message box showed centered, it affects the subsequent code. Coming up with read allocation errors.
But I have come up with a viable solution now:
GetParent()->EnableWindow(FALSE);
strEntry.Format(IDS_TPL_SURE_DELETE_FROM_ASSIGN_HIST, psHist->strName);
if (AfxMessageBox(strEntry, MB_YESNO | MB_ICONQUESTION) == IDNO)
{
GetParent()->EnableWindow(TRUE);
return;
}
I disable the parent before showing the message box and enable it again once it is dismissed. This prevents the user from closing the main window. They much close the popup message. I will have to go this way for now.
Thanks guys.

How to move Borderless DhtmlDialog with other click events

I am moving Borderless DHtmlDialog of MFC with this method:
BOOL CMyDlg::preTranslateMessage(MSG* pMsg)
{
if(pMsg->message == WM_LBUTTONDOWN)
{
PostMessage( WM_NCLBUTTONDOWN, HTCAPTION, MAKELPARAM( pMsg->pt.x, pMsg->pt.y));
return false;
}
return CDHtmlDialog::preTranslateMessage(pMsg);
}
But the problem is that it is moving but couldn't picking up any click event for tags and ids given in DHTML_EVENT_MAP.
Can any one please guide what is the main problem in this? Please help.
This is because DHTML_EVENT_MAP will not handle click events on tags.
MFC macros are not supposed to do that. You will need event sink in your code.
Refer Handling HTML Element Events

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).

(WinAPI) Simulating item selection in ComboBox

I am currently writing a wrapper for an existing application that has its own GUI. I don't have access to original application's source code (unfortunately). The program that I am writing is in C++ and I am making use of WinAPI. I am manipulating target application by simulating button-clocks, ticking checkboxes etc.
The problem I am facing at the moment is following:
I need to make a selection in droplist implemented as WinAPI ComboBox. I am doing it by using macro ComboBox_SetCurSel. The selection in the droplist changes correctly. However in the original application there is a read-only textbox that changes the value depending on the selection in combobox. And this one does not change when I execute ComboBox_SetCurSel.
The assumption I made is that CBN_SELENDOK and/or CBN_SELCHANGE are sent when selecting an entry in ComboBox manually and this is the bit I am not doing when setting the selection with ComboBox_SetCurSel macro.
However due to lack of experience I cannot figure out how to resolve the problem. Who is normally listening for CBN_SELENDOK and CBN_SELCHANGE. Is it main application window, parent element of the combobox or main application thread? How do I find out.
Is there a macro that would do the whole thing? Like changing the selected item in ComboBox and sending all necessary notifications? Is there some smart workaround?
Any help on the subject, or any additional questions that would help to make situation more clear are welcome.
UPDATE: thanks for comment by Jonathan Potter. I am now attempting to send messages explicitly. Here is the part of the code where I am doing it:
int res = ComboBox_SetCurSel(this->handle, index);
if (res == CB_ERR)
{
return false;
}
PostMessage(GetParent(this->handle),WM_COMMAND, MAKEWPARAM(0,CBN_SELENDOK),0);
PostMessage(GetParent(this->handle),WM_COMMAND, MAKEWPARAM(0,CBN_SELCHANGE),0);
Note this->handle is just a handle to ComboBox itself as I have packed it into the structure for convenience. GetParent(this->handle) Should get immediate parent of ComboBox
Still no result. Does the order of messages matter? Also how do I obtain the identifier that needs to go into LOWORD of WPARAM sent along with WM_COMMAND?
ANSWER:
Thanks to AlwaysLearningNewStuff I have found and an answer. I have been sending messages with 0 as LPARAM. Apparently a handle to ComboBox itself neets to be sent as LPARAM in order for solution to work. This would take me ages to figure it out.
#AlwaysLearningNewStuff, you should have posted this as an answer, not a comment.
Also the bit about using GetDlgCtrlID() to get ControlID of the ComboBox is very useful. This makes code more reliable.
Thank you, everyone who participated.
Here is my final code:
if (this->handle == NULL)
{
return false;
}
int res = ComboBox_SetCurSel(this->handle, index);
if (res == CB_ERR)
{
return false;
}
PostMessage(GetParent(this->handle), WM_COMMAND, MAKEWPARAM(GetDlgCtrlID( this->handle ),CBN_SELENDOK),
(LPARAM)(this->handle));
PostMessage(GetParent(this->handle), WM_COMMAND, MAKEWPARAM(GetDlgCtrlID( this->handle ),CBN_SELCHANGE),
(LPARAM)(this->handle));
return true;
You are correct that CBN_SELCHANGE is not sent when using ComboBox_SetCurSel(), and the documentation says as much:
The CBN_SELCHANGE notification code is not sent when the current selection is set using the CB_SETCURSEL message.
So you have to send the notifications manually. However, you are missing key elements in your messages - the ComboBox's Control ID and HWND. The parent window uses those to identify which child control is sending messages to it so it can then act accordingly.
Try this instead:
int res = ComboBox_SetCurSel(this->handle, index);
if (res == CB_ERR)
{
return false;
}
HWND hParent = GetParent(this->handle);
int iCtrlId = GetDlgCtrlID(this->handle);
if (GetWindowLong(this->handle, GWL_STYLE) & CBS_SIMPLE)
PostMessage(hParent, WM_COMMAND, MAKEWPARAM(iCtrlId,CBN_SELENDOK), LPARAM(this->handle));
PostMessage(hParent, WM_COMMAND, MAKEWPARAM(iCtrlId,CBN_SELCHANGE), LPARAM(this->handle));