MFC - Changing dialog item focus programmatically - mfc

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

Related

Call button click function from grandchild

I'm creating my first C++ wxWidgets application. I'm trying to create some kind of split button where the options are displayed in a grid. I have a custom button class which, when right-clicked on, opens a custom wxPopupTransientWindow that contains other buttons.
When I click on the buttons in the popup, I want to simulate a left click on the main button. I'm trying to achieve this through events, but I'm kinda confused.
void expandButton::mouseReleased(wxMouseEvent& evt)
{
if (pressed) {
pressed = false;
paintNow();
wxWindow* mBtn = this->GetGrandParent();
mBtn->SetLabel(this->GetLabel());
mBtn->Refresh();
wxCommandEvent event(wxEVT_BUTTON);
event.SetId(GetId());
event.SetEventObject(mBtn);
mBtn-> //make it process the event somehow?
wxPopupTransientWindow* popup = wxDynamicCast(this->GetParent(), wxPopupTransientWindow);
popup->Dismiss();
}
}
What is the best way to do this?
You should do mBtn->ProcessWindowEvent() which is a shorter synonym for mBtn->GetEventHandler()->ProcessEvent() already mentioned in the comments.
Note that, generally speaking, you're not supposed to create wxEVT_BUTTON events from your own code. In this particular case and with current (and all past) version(s) of wxWidgets it will work, but a cleaner, and guaranteed to also work with the future versions, solution would be define your own custom event and generate it instead.

Where to initialize a rich edit control on another dialog?

I have an MFC dialog based application that has 2 Dialogs: Main Dialog CMyDlgand Second dialog CMyDlg2.
On the main Dialog I add a Button "Go dialog 2". So I added a handler for the button so that when clicked it pops up the second dialog. Everything works fine But on the second Dialog I have added a Rich Edit Control from toolbox. I Added for it a variable. I also added a class for the second dialog.
Now If I run the Application I get the dialog one and if I pressed "Go to dialog 2" I got what I want. But I need at some point to change the font of the rich edit control but my program crashes.
So I overrided OnInitDialog and inside it do some changes to the control but program crashes. After debugging I found that the handle of rich edit is null?!
So how and where can I change the color or do some initializations to the control?
(I called AfxInitRichEdit2() in OnInitInstance())
BOOL CMyDlg2::OnInitDialog() {
m_richEdit.SetWindowText("Hello there!"); // program crashes because the handle m_richEdit is null.
return TRUE;
}
And this is the handler of button that creates the Dialog2 and that contains the rich edit control:
void CMyDlg::OnBnClickedButton1(){
CMyDlg2 theDlg;
theDlg.DoModal();
// TODO: Add your control notification handler code here
}
If I create the rich edit control programmatically then everything works fine because I create it at OnInitDialog and then it works fine but I need the one that is I added using the wizard toolbox.
*** The thing is that if I write:
m_richEdit.SetWindowText(""); // program crashes but if I wirte:
GetDlgItem(IDC_RICHEDIT221).SetWindowText(""); it works fine?
You probably have the following code inserted by wizard:
void DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
DDX_Control(pDX, IDC_RICHEDIT22, m_richEdit);
}
This tells the dialog to associate m_richEdit with the dialog control IDC_RICHEDIT22. But this association is not performed until the base class method CDialog::OnInitDialog(); is called.
BOOL CMyDlg2::OnInitDialog()
{
//this line should work:
GetDlgItem(IDC_RICHEDIT22)->SetWindowText("Hello");
//this line won't work:
//m_richEdit.SetWindowText("Hello there!"); <- richedit's handle is NULL
//this line will subclass m_richEdit
//plus run other initialization
CDialog::OnInitDialog();
//m_richEdit is ready
m_richEdit.SetWindowText("Hello there!");
return TRUE;
}
It's recommended to put CDialog::OnInitDialog() int the first line, to make sure the initialization is done.
GetDlgItem works because the control IDC_RICHEDIT22 exists in the dialog template and you have a valid dialog handle. You are basically making a simple call based on WinAPI's GetDlgItem:
HWND hedit = ::GetDlgItem(m_hWnd, IDC_RICHEDIT22);
::SetWindowText(hedit, "Hello world");
There is no additional initialization needed.
But m_richEdit is just a C++ object, declared as CRichEditCtrl m_richEdit; The constructor for this C++ class doesn't do much besides setting m_hWnd to NULL.
Once it's associated with a valid window handle, we can begin using its windows methods such as CRichEdit::SetWindowText

MFC Rich Edit Control 2.0 receiving click event

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);
}

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.

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
}