I have a CDialog contains many CEdit objects. They all have to do similar operations when kill focus (for example: when focus is killed the edit box text's is changed).
I can define the dialog's message map this way:
ON_EN_KILLFOCUS(ID1, kf1)
ON_EN_KILLFOCUS(ID2, kf2)
ON_EN_KILLFOCUS(ID3, kf3)
ON_EN_KILLFOCUS(ID4, kf4)
and all kf function will call a common function:
CommonFunction(CEdit* editBox)
But is there a way to transfer the edit box in the kf function itself? I mean to define it this way:
ON_EN_KILLFOCUS(ID1, kf(ID1))
ON_EN_KILLFOCUS(ID2, kf(ID2))
ON_EN_KILLFOCUS(ID3, kf(ID3))
ON_EN_KILLFOCUS(ID4, kf(ID4))
or another way.
NOTE: I use Visual C++ 6.0 ('98 edition)
You can use ON_CONTROL_RANGE in the message map to dispatch all of the messages to the same function. To do this it is necessary to assure that the IDs are in a continuous range. (Edit resource.h if necessary.)
ON_CONTROL_RANGE(BN_CLICKED, IDC_RADIO_DRAWALL, IDC_RADIO_DRAWBEST, OnRadioBtnDraw)
void CVisualPPView::OnRadioBtnDraw(UINT nID)
{
}
Related
I created a dialog MainDialog.cpp with 2 edit controls whose IDs are IDC_EDITCONTROL_A and IDC_EDITCONTROL_B, and have variables defined as m_editControlA and m_editControlB, respectively.
Also, I have 2 buttons whose IDs are IDC_MFCBUTTON_KEY_X and IDC_MFCBUTTON_KEY_Y, and variables are m_buttonKeyX and m_buttonKeyY, respectively.
Below is the code in the source file
#include "afxdialogex.h"
IMPLEMENT_DYNAMIC(CMainDialog, CDialogEx)
CMainDialog::CMainDialog(CWnd* pParent): CDialogEx(IDD_MAIN_DIALOG, pParent)
{
}
CMainDialog::~CMainDialog()
{
}
void CMainDialog::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
DDX_Control(pDX, IDC_EDITCONTROL_A, m_editControlA);
DDX_Control(pDX, IDC_EDITCONTROL_B, m_editControlB);
DDX(Control(pDX, IDC_MFCBUTTON_KEY_X, m_buttonKeyX);
DDX(Control(pDX, IDC_MFCBUTTON_KEY_Y, m_buttonKeyY);
}
BEGIN_MESSAGE_MAP(CMainDialog, CDialogEx)
ON_EN_CHANGE(IDC_EDITCONTROL, &CMainDialog::OnEnChangeEditA)
ON_BN_CLICKED(IDC_MFCBUTTON_KEY_X, &CMainDialog::OnBnClickedButtonX)
ON_BN_CLICKED(IDC_MFCBUTTON_KEY_Y, &CMainDialog::OnBnClickedButtonY)
END_MESSAGE_MAP()
void CMainDialog::OnBnClickedButtonX()
{
m_editControlA.SetWindowTextW(_T("X")); // test
}
void CMainDialog::OnBnClickedButtonX()
{
m_editControlA.SetWindowTextW(_T("Y")); // test
}
I am trying to understand how I can have each button send their respective character (i.e. X or Y in this example) to the selected edit control if one is selected. Essentially, I would like to simulate keyboard input.
I have read the docs about how to simulate keyboard events and also the sendMessage but I could not understand how to implement it since my C++ knowledge is very basic. Also, following my previous question I have found that the GetFocus would be useful but still my main issue currently is sending the input.
Any example code or useful link could be very useful for me to learn how I can simulate a keyboard input inside an app.
The characters are sent from the OS to the edit controls using the WM_CHAR message.
In reality it is a bit more complex than that, but you do not need to emulate the entire WM_KEYUP WM_KEYDOWN message sequence, since its end result is to generate a WM_CHAR message.
You can use CWnd::PostMessage to send characters directly to your edit controls, even when they do not have the focus.
You have probably already found the documentation for WM_CHAR here: https://msdn.microsoft.com/fr-fr/library/windows/desktop/ms646276(v=vs.85).aspx
oops.. excuse my french, ths english doc is here
https://msdn.microsoft.com/en-us/library/windows/desktop/ms646276(v=vs.85).aspx
(just changing the fr-fr to en-us does the trick, it probably works for all other languages, neat!
wParam holds the character you want to send. Either an plain ASCII character, or one of the VK_ constants... I suggest you use the unicode version WM_CHARW, as most windows software uses unicode nowadays. The notation for wide chars is either L'X' or _T('X'), the unicode (UTF-16) character type is wchar_t.
lParam contains other keystroke details, 0 should be fine for what you want to do.
to send X, simply call
m_editControlA.PostMessage(WM_CHAR, _T('X'));
When using the _T() notation, the character (or string) literal between the parenthesis will be automatically converted to the right character width for your app's unicode setting (you should set that to UNICODE, since that's what the OS is using, and is also the only valid encoding for Windows CE, for example, and you should get used to manipulating this type.
the _T() macros and _t* overrides for almost all C library functions operating on strings are defined in tchar.h, which is included by Visual Studio in stdafx.h. Under MFC, you'll mostly use CString, but it's good to know where these things are.
[EDIT] When you get that running, you should start playing with WM_KEYDOWN. You will discover that PostMessage(WM_CHAR, VK_ESCAPE) directly to your dialog does not close it, while a PostMessage(WM_KEYDOWN, VK_ESCAPE) does. And that m_editBox.PostMessage(WM_KEYDOWN, _T('X')) will send a lower key 'x' to your edit box. But that's another topic to ivestigate.
Have fun with MFC!
For your last question:
Sure, but it gets a bit more complicated, as your button will gain focus, as soon as you click on it. You'd have to create handlers for EN_SETFOCUS for eeach of your edit boxes, and add a CWnd* data member to keep track of the last edit box that had focus.
Your EN_SETFOCUS handlers should look something like this
void CdlgDlg::OnEnSetfocusEdit1()
{
m_pWndLastFocus = &m_edit1;
}
Don't forget to set the pointer to NULL in your constructor and to chjeck it's valid before calling m_pWndLastFocus->PostMessage() though.
The way to synthesize input in MFC is by using the SendInput method.
Is there anybody could tech me how to use the interface "AddButton" of the MFC control? "CVSListBox"? I wrote the below code, and the new buttons have showed on the control successful, but I can't respond its ON_BN_CLICKED event. Could you tell me why? thanks.
ON_BN_CLICKED(IDC_BTN_AWSPORTIMPORT, &CPgTestAwsPortfolio::OnBnClickedBtnAwsportimport)
void CPgTestAwsPortfolio::OnBnClickedBtnAwsportimport()
{
int xx = 100;
}
CPortCaseListBox m_lbAwsPortCases;
m_pgTestAwsPort.m_lbAwsPortCases.AddButton(IDB_AFXBARRES_NEW, _T("Import"), 0, 0, IDC_BTN_AWSPORTIMPORT);
The idea is different here.
All Buttons are handled internally in the CVSListBox class.
See CVSListBoxBaseBase::OnCommand override.
When a button sends a WM_COMMAND it is intercepted by CVSListBoxBaseBase::OnCommand
When the id is member of the internal button list of the ist Control the virtual function OnClickButton is executed.
GetButtonID might help you to convert the Position into the ID.
Note OnClickButton receives the number of the button, not the id.
So the parent never receives any notification of this Buttons. It is all handled in the virtual functions of CVSListBox.
The documentation is incomplete because the Base class isn't described and document.
The question is like this:
I write a windows application in VS2005, building success. Then I transfer it to VS2008, when I build the program, there will be an error messsage shows:
error C4867: 'CGroupBox::CtlColor': function call missing argument list; use '&CGroupBox::CtlColor' to create a pointer to member
the message map is as follows:
BEGIN_MESSAGE_MAP(CGroupBox, CButton)
//{{AFX_MSG_MAP(CGroupBox)
ON_WM_PAINT()
ON_WM_CTLCOLOR_REFLECT()
//ON_WM_CTLCOLOR()
ON_WM_ERASEBKGND()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
the function is is as follows:
HBRUSH CGroupBox::CtlColor(CDC* pDC, UINT nCtlColor)
{
GetParent()->Invalidate();
return NULL;
}
but if I change to the ON_WM_CTLCOLOR() ,I can get a successful build.
The error message is because the signature of the message handler doesn't match the signature you're using. Look up the signatures for ON_WM_CTLCOLOR_REFLECT() and ON_WM_CTLCOLOR() on MSDN to get the correct ones.
As to the question in your title, some controls normally send their notification messages to their parent. Which is inconvenient, because every time you re-use that control, you need to change its parent window. Therefore MFC has 'reflection' support, which is basically a way of saying 'when you receive a notification from a child control, first try bouncing it back to that control to see if that control knows how to deal with it itself.'
Read the details on https://web.archive.org/web/20101229015404/http://msdn.microsoft.com/en-us/library/eeah46xd(v=vs.80).aspx .
I just had the same problem, and as you can see from the link Roel provided, the original poster DID have the correct signature.
Turns out this is a bug in MFC itself. Find ON_WM_CTLCOLOR_REFLECT in afxmsg_.h and you'll notice that CtlColor is missing the "&" before it. I guess the older compilers didn't mind. Add the "&" to make it "&CtlColor" and the error is fixed.
Alternatively, if you don't like changing the official headers, you could #under ON_WM_CTLCOLOR_REFLECT in your own code and define it again properly.
I notice many other (maybe all?) reflected messages in that file have the same problem too.
Imagine I have a CDialog which creates controls dynamically when the user clicks a button. It could be like this:
// We don't know which is the first id for the new buttons until runtime (!)
MyDialog::MyDialog(/*whatever parameters needed*/, first_id)
: next_id_(first_id)
{ /*...*/ }
BOOL MyDialog::OnSomeButtonClicked()
{
CButton* new_button = new CButton;
new_button->Create("Caption", WS_CHILD | WS_VISIBLE, this->new_button_rect_,
this, this->next_id_++);
}
Then my question would be: How could I handle messages from this button? Is it possible to use the MFC message map facility?
The solution should work in both vs6 and vs2005.
Thank you!
These are the solutions I've found so far in order of relevance:
Use ON_COMMAND_RANGE if you can define the range of the control IDs you want to handle.
Overload CWnd::PreTranslateMessage() and do whatever stuff you want with the messages received. NOTE: When dealing with buttons, take into account that the BN_CLICKED event is NOT sent to PreTranslateMessage but directly sent to the window procedure.
Overload CWnd::WindowProc() and do whatever stuff you want with the messages received. NOTE that when dealing with buttons this is the ONLY WAY I've found to handle the BN_CLICKED event.
Interesting links:
Please help with PreTranslateMessage and user defined messages handling.
TN006: Message Maps
I hope this helps... thank you all for your contributions.
Eventhough you dont know the exact values of the id, if you know the possible range of IDs then the following macro can be used.
BEGIN_MESSAGE_MAP(MyDialog, CDialog)
...
...
ON_COMMAND_RANGE(1000, 5000, OnButtonDynamic)
END_MESSAGE_MAP()
void MyDialog::OnButtonDynamic(UINT nID)
{
}
This will work for ids in the range 1000 - 5000.
I'm a few years late to this party, but the solution to this is to assign the same control id to each button (no need to 'reserve' id's in resource.h, and no artificial restrictions on the amount of controls that can be created), to save the window handle and to use GetCurrentMessage() in the handler for that button:
// resource.h
#define IDC_DYNAMIC_BUTTON 123
// In message map
ON_BN_CLICKED(IDC_DYNAMIC_BUTTON, OnDynamicButtonClicked)
// Store the window handles when creating them in a member:
std::map<HWND, SomeStruct> m_Buttons;
... fill this map when creating the buttons, presumably in OnInitDialog()
// Actual handler
void MyDialog::OnDynamicButtonClicked()
{
const MSG* message = GetCurrentMessage();
if (m_Buttons.find((HWND)message->lParam) != m_Buttons.end()) {
// Do something with m_Buttons[(HWND)message->lParam]
}
}
I believe this article explains it pretty well and has source code. I have not tried this so I can't guarantee it works, but in the time I have thought it might help.
Article
You can find details (+ a lot more) on modeless dialogs there.
insert the entry of ID of the handler in Resouce.h
Then insert the entry in the message map of the handler like ON_BN_CLICKED(IDC_BTNCREATE, OnBnClickedrunCreated)
or you can directly use the integer ID like ON_BN_CLICKED(1200, OnBnClickedrunCreated). If you use 2nd version then there is
no need to insert entry in resource.h. Give defination and declaration of the handler in .h and .cpp file. you will get your answer.
Use this way: ON_CONTROL_RANGE(wNotifyCode, id1, id2, memberFxn ).
for example:
ON_CONTROL_RANGE(EN_UPDATE, IDC_EDIT_START, IDC_EDIT_END, OnEnUpdateEditParams)
I am using the MFC Feature Pack and I have some buttons on a ribbon bar, instances of CMFCRibbonButton. The problem is that I would like to enable and disable some of them in certain conditions, but at runtime. How can I do this? because there is no specific method for this...I heard that a solution would be to attach/detach the event handlers at runtime, but I do not know how...
When you create the CMFCRibbonButton object you have to specify the associated command ID (see the documentation for the CMFCRibbonButton constructor here). Enabling and disabling of ribbon buttons is then done using the usual command update mechanism in MFC, using the CCmdUI class.
For example, if you have a ribbon button whose command ID is ID_MYCOMMAND and you want to handle this command in your application's view class, you should add these functions to the class:
// MyView.h
class CMyView : public CView {
// ...
private:
afx_msg void OnMyCommand();
afx_msg void OnUpdateMyCommand(CCmdUI* pCmdUI);
DECLARE_MESSAGE_MAP()
};
and implement them in the .cpp file:
// MyView.cpp
void CMyView::OnMyCommand() {
// add command handler code.
}
void CMyView::OnUpdateMyCommand(CCmdUI* pCmdUI) {
BOOL enable = ...; // set flag to enable or disable the command.
pCmdUI->Enable(enable);
}
You should also add ON_COMMAND and ON_UPDATE_COMMAND_UI entries to the message map for the CMyView class:
// MyView.cpp
BEGIN_MESSAGE_MAP(CMyView, CView)
ON_COMMAND(ID_MYCOMMAND, &CMyView::OnMyCommand)
ON_UPDATE_COMMAND_UI(ID_MYCOMMAND, &CMyView::OnUpdateMyCommand)
END_MESSAGE_MAP()
For more information on message maps in MFC, refer to TN006: Message Maps in MSDN.
I hope this helps!
ChrisN gave a pretty perfect answer. You can see an example of exactly how this is done by downloading the VS2008 Sample Pack from here, and opening the MSOffice2007Demo solution.
When running the sample, look at the "Thumbnails" checkbox in the View tab of the ribbon, it's disabled.
This is controlled by CMSOffice2007DemoView::OnUpdateViewThumb which calls pCmdUI->Enable(FALSE);. You can change this to call TRUE or FALSE at runtime to enable/disable the button respectively.