Tab Control: How to prevent the selection from changing? - c++

My project is simple WIN32 dialog box with a tab control.
Under some condition, I want to prevent the user from changing tab. MS documentation is quite straightforward -
Returns TRUE to prevent the selection from changing, or FALSE to allow the selection to change.
However, this just does not work!
The code:
case WM_NOTIFY:
if (((LPNMHDR)lParam)->idFrom == IDC_DEVTABS)
{
if (((LPNMHDR)lParam)->code == TCN_SELCHANGING)
return (INT_PTR)TRUE;
//return (INT_PTR)OnSelChanging(hDlgTab);
if (((LPNMHDR)lParam)->code == TCN_SELCHANGE)
OnSelChanged(hDlgTab);
return (INT_PTR)TRUE;
}
I can see that my parent dialog box receives the message, but the tab is changing anyhow. Any idea how I can prevent the tab from changing?

If your control is in a dialog, as I assume it is, you need to return the value through the DWLP_MSGRESULT window data.
SetWindowLongPtr(hWndDlg, DWLP_MSGRESULT, TRUE);
return TRUE;
The return value from the dialog procedure indicates to the dialog manager whether or not the message was handled. The actual return value for the message is provided via DWLP_MSGRESULT.
As always, see Raymond's blog for a good discussion of this.

Related

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

How to handle closing MessageBox

my environment is C++, MFC, compact-framework for WM 6.0+ devices.
In many places, I am showing pop-up messages using 'MessageBox()' to give a simple warning or get Yes/No repsonse from user. What I want to do is that whenever any message is closed, call some common function before I perform specific codes.
I tried WM_SHOWWINDOW in parent window but it doesn't seem to occur.
Any suggestion will be appreciated.
[Added] my screen has many buttons and I have to make sure only one button is focused all the time. When I show message box, button seems to loose its focus so I want to focus it back when message is closed. Of course, I can do it in every place where message is used but looking for a better way to handle this situation.
The MessageBox function returns specific return codes when it is closed, you can wrap the MessageBox function and check the return values and run some code based on that.
Here are the return codes from MSDN :
IDABORT 3 The Abort button was selected.
IDCANCEL 2 The Cancel button was selected.
IDCONTINUE 11 The Continue button was selected.
IDIGNORE 5 The Ignore button was selected.
IDNO 7 The No button was selected.
IDOK 1 The OK button was selected.
IDRETRY 4 The Retry button was selected.
IDTRYAGAIN 10 The Try Again button was selected.
IDYES 6 The Yes button was selected.
So the following code can be used to run different functions based on the return code.
void MyMessageBox(wstring title,wstring message)
{
int msgboxID = MessageBox(
NULL,
(LPCWSTR)message.c_str(),
(LPCWSTR)title.c_str(),
MB_ICONWARNING | MB_CANCELTRYCONTINUE | MB_DEFBUTTON2
);
switch (msgboxID)
{
case IDCANCEL:
// TODO: add code
break;
case IDTRYAGAIN:
// TODO: add code
break;
case IDCONTINUE:
// TODO: add code
break;
//so on
}
}
More info here :
http://msdn.microsoft.com/en-us/library/windows/desktop/ms645505(v=vs.85).aspx
You might try intercepting the WM_ACTIVATE message in the parent window.

CPropertyPage derived dialog doesn't close on Esc when showing as a standalone dialog

I have a dialog which I need to show both inside a CPropertySheet and as a standalone dialog. I've chosen not to have 2 separate classes to avoid code redundancy (I make changes a lot in those dialogs, and having to sync 2 classes constantly would be hell), instead when I want to show it as a standalone dialog, I just call CPropertyPage::DoModal. This causes some problems, but I've fixed most of them.
However, some still remain, namely enter and esc don't work. Also pressing tab doesn't change the focus. This makes me think that CPropertyPage eats up all keyboard input, or maybe it tries to pass them to its parent.
Any ideas how I can override that behaviour in the standalone mode?
I believe this would work for you. I don't have a dialog that I can test this with so I am doing this all from memory but I believe you could add a bool that you set when you call DoModal or expose it as a property that you set before the call to DoModal to indicate it is running as a stand alone dialog, then override PreTranslateMessage like this:
CMyPropertyPage::PreTranslateMessage(MSG* pMsg)
{
if (m_runningAsStandalone && pMsg->message == WM_KEYDOWN)
{
UINT key = pMsg->wParam;
switch(pMsg->wParam)
{
case VK_RETURN:
OnOK();
return TRUE;
case VK_ESCAPE:
OnClose();
return TRUE;
}
}
return CPropertyPage::PreTranslateMessage(pMsg);
}
You may also find this link helpful http://support.microsoft.com/kb/125645

EN_MSGFILTER, can't prevent message propogation

I have a dialog with a rich-text control. The dialog wants to intercept right-click events on the control; in some cases the dialog should do its own functionality and block the message reaching the control, in other cases it should let the control receive the message.
So I have:
ON_NOTIFY(EN_MSGFILTER, IDC_RICHTEXT, OnRichTextMsgfilter)
void CMyDialog::OnRichTextMsgfilter(NMHDR *pNMHDR, LRESULT *pResult)
{
MSGFILTER *pMsgFilter = reinterpret_cast<MSGFILTER *>(pNMHDR);
*pResult = 0;
if (pMsgFilter->msg == WM_RBUTTONUP)
{
if(...)
*pResult=1;
}
}
I step through the code and pResult is set when it should be, but the control still gets the message. Looking at MSDN it says:
If the control should process the
event, the message returns a zero
value. If the control should ignore
the event, the message returns a
nonzero value.
But the defined message handler signature has no return... I am assuming that's what *pResult is for. Is that not true? If so how do I achieve this?
So i've tried to reproduce this behavior in a simple dialog based app and i really can't -- however, i'm not sure what it is that intercepting the right button message is trying to solve.
That said the following code completely blocks the Left button mouse clicks in my testing (If this returns TRUE the control does not respond to left clicks - however focus will get set to the control on the initial click down and that is more a window manager issue than the control itself)
void CTestDlg::OnMsgfilterRichedit1(NMHDR* pNMHDR, LRESULT* pResult)
{
MSGFILTER *pMsgFilter = reinterpret_cast<MSGFILTER *>(pNMHDR);
if (pMsgFilter->msg == WM_LBUTTONUP || pMsgFilter->msg == WM_LBUTTONDOWN)
{
*pResult = TRUE;
return;
}
*pResult = FALSE;
}
If i change *pResult = TRUE to *pResult = FALSE then the left clicks start working again.
It could be that you want to catch and filter out the WM_RBUTTONDOWN rather than WM_RBUTTONUP to do what you intend, but since i'm unsure what functionality you are trying to filter out i can't say for sure.
The WM_RBUTTONDOWN is stil getting through...