MFC Control "CVSListBox" - How to use the interface "AddButton"? - c++

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.

Related

How to get event when click destroy button before document was closed

I have a problem for my MFC project as follows:
When I click on the destroy button, I want to show a messagebox "asking save file" before document closed.
But I can't.
The message is always shown after the document was closed.
A lot of places that I have placed code.
CloseDocument Function of Document Class
OnDestroy Function of MainFrame Class
Destructor Function of View Class
ExitInstance Function of App Class
But without success.
Can someone show what's wrong?
A WM_CLOSE message is sent to a window when the user wants close it. A custom message handler can decide whether it wants to initiate window destruction from here, or go back (or initiate window destruction after storing information).
In an MFC application this is reflected as the OnClose member function of CWnd or a CWnd-derived classes (such as CFrameWnd). Client code can provide a custom implementation and wire it up through the message map. When it does it should only call into the base class implementation if it wants the program to terminate.
Assuming that your CMainFrame class derives from CFrameWnd you will need to make the following changes:
MainFrm.h (add the following class member)
class CMainFrame : public CFrameWnd
{
// ...
protected:
void OnClose();
// ...
};
MainFrm.cpp (message map)
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
// ...
ON_WM_CLOSE()
END_MESSAGE_MAP()
MainFrm.cpp (implementation)
void CMainFrame::OnClose()
{
if (::AfxMessageBox(_T("Close application?"), MB_YESNO) == IDYES)
{
// Have the base class initiate destruction
CFrameWnd::OnClose();
} else {
// Do nothing
}
}
An attempt to close the application's main window pops up a confirmation dialog. If the user selects "Yes", the program terminates. Otherwise the program continues to execute. In that case the document will remain unchanged.
The simpler approach, a Oneliner as described in MSDN
'By calling this function consistently, you ensure that the framework prompts the user to save changes before closing a document. Typically you should use the default value of TRUE for the bModified parameter. To mark a document as clean (unmodified), call this function with a value of FALSE.'
BOOL CMyDocument::OnNewDocument()
{
if (!CDocument::OnNewDocument())
return FALSE;
// TODO: add reinitialization code here
// (SDI documents will reuse this document)
SetModifiedFlag(TRUE); // Set this somewhere, when you want to safe the document before the document goes off
return TRUE;
}

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.

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

C++/MFC: Handling multiple CListCtrl's headers HDN_ITEMCLICK events

I'm coding an MFC application in which i have a dialog box with multiple CListCtrls in report view. I want one of them to be sortable.
So i handled the HDM_ITEMCLICK event, and everything works just fine .. Except that if i click on the headers of another CListCtrl, it does sort the OTHER CListCtrl, which does look kind of dumb.
This is apparently due to the fact that headers have an ID of 0, which make the entry in the message map look like this :
ON_NOTIFY(HDN_ITEMCLICK, 0, &Ccreationprogramme::OnHdnItemclickList5)
But since all the headers have an id of zero, apparently every header of my dialog sends the message.
Is there an easy way around this problem ?
EDIT: Maybe i wasn't clear, but i did check the values inside the NMHDR structure. The HwndFrom pointer is different depending on which header is clicked, which doesn't help me a lot since it's value is obviously different at each runtime. The idFrom value is 0, for the very reasons i explained above, because that's the id of every header. Thanks
EDIT2: The hwnd pointer values do also not correspond to the CListCtrl, probably because it's coming from a different object entirely.
Check the values of the NMHDR structure.
http://msdn.microsoft.com/en-us/library/bb775514%28VS.85%29.aspx
Ok i found a solution, though i find it a bit dirty but it works, so i'll post it for future reference.
You can get the Header through the GetHeaderCtrl member function of CListCtrl. You can then get it's handler thru m_hWnd. So all you got to do is to test if that handler is the same as the one in the NMHDR structure, so the code looks like this :
void Ccreationprogramme::OnHdnItemclickList5(NMHDR *pNMHDR, LRESULT *pResult)
{
if (pNMHDR->hwndFrom == LC_gen_schedules.GetHeaderCtrl()->mhWnd)
{
// Code goes here
}
*pResult = 0;
}
Thanks all for the help
The LPARAM passed to your message handler is actually a pointer to an NMHEADER structure, which contains an NMHDR structure, which in turn contains the HWND and control ID of the control which sent the message. You may be able to compare that to your list controls' HWNDs to determine which window's header control was clicked.
Alternatively, you could derive a class from CListCtrl and reflect the HDN_ITEMCLICK messages back to the list control. That way, each list control object handles its own header's notifications.

How to handle messages from dynamically created controls in an MFC app?

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)