List Control Delete Row on Delete Key press - c++

I have a List Control holding rows with data. Now i am trying to delete row on delete key press. I am trying with : LVN_DELETEITEM as below :
Afx Message :
afx_msg void OnLvnDeleteitemList(NMHDR *pNMHDR, LRESULT *pResult);
Message Map:
ON_NOTIFY(LVN_DELETEITEM, IDC_LIST_ACQUISITION_SETTINGS, &MeasureDialog::OnLvnDeleteitemList)
Implementation of OnLvnDeleteitemList :
void MeasureDialog::OnLvnDeleteitemList(NMHDR *pNMHDR, LRESULT *pResult)
{
LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
// TODO: Add your control notification handler code here
ReportMessage(L"Deleted");
*pResult = 0;
}
But i am never coming into the OnLvnDeleteitemList method. Whats wrong about it?

The message LVN_DELETEITEM clearly states that:
Notifies a list-view control's parent window that an item is about to be deleted.
Which means, when item is deleted, the notification message will be sent. Pressing the delete key won't invoke this method. You need to handle Delete key message itself (WM_KEYDOWN), and call CListCtrl::DeleteItem

Related

C++ MFC CListCtrl - How to sort item upon startup (without any UI interaction)?

I have a list of item to be loaded in a MFC window with CListCtrl in it.
Upon OnInitDialog(), I will perform
m_List.InsertItem(&item);
where this item contain data such as: filename, last date modified, comment.
The current output that I have now is that it will sort it with last date modified when I open this dialog, then I would need to manually click on the list header to trigger an event to sort the data according to filename with functions such as below:
ON_NOTIFY(LVN_COLUMNCLICK, IDC_FILELIST, OnColumnclickFilelist)
int CALLBACK CDlg::AlphaNumericSorting(LPARAM lParam1, LPARAM lParam2, LPARAM ParameterSort)
{
std::wstring wv1(((CLVData*)lParam1)->strFileName);
std::wstring wv2(((CLVData*)lParam2)->strFileName);
return StrCmpLogicalW(wv1.c_str(), wv2.c_str()) < 0;
}
void Dlg::OnColumnclickFilelist(NMHDR* pNMHDR, LRESULT* pResult)
{
NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
m_List.SortItems(AlphaNumericSorting, pNMListView->iSubItem);
*pResult = 0;
}
Now I'm trying to make it sort automatically upon OnInitDialog(), is there a correct way to do so? From what I searched so far, all samples I got are those with event handling.

Detecting Up / Down arrows in CSpinButtonCtrl MFC C++

Is there any way to differentiate when the Up or Down arrow of a CSpinButtonCtrl is pressed?
I am trying to use the OnPointerdown event, but I don't know how to do it...
afx_msg LRESULT CMySpinButtonCtrl::OnPointerdown(WPARAM wParam, LPARAM lParam)
{
if(IS_POINTER_PRIMARY_WPARAM(wParam))
{
//TODO
}
return 0;
}
I will appreciate any kind of help.
Is there any way to differentiate when the Up or Down arrow of a CSpinButtonCtrl is pressed?
You should use UDN_DELTAPOS to do this.
Right-click the control in the Resource Editor and select Add Event Handler:
Select the UDN_DELTAPOS message and click Add and Edit:
You will be provided with skeleton code:
void CMFCApplication1Dlg::OnDeltaposSpin1(NMHDR *pNMHDR, LRESULT *pResult)
{
LPNMUPDOWN pNMUpDown = reinterpret_cast<LPNMUPDOWN>(pNMHDR);
// TODO: Add your control notification handler code here
*pResult = 0;
}
The NMUPDOWN article explains about the structure that you use. What you need to do is test the iDelta value. Example:
void CColumnOrderDlg::OnDeltaposSpinColumns(NMHDR* pNMHDR, LRESULT* pResult)
{
LPNMUPDOWN pNMUpDown = reinterpret_cast<LPNMUPDOWN>(pNMHDR);
if (pNMUpDown != nullptr)
{
if( pNMUpDown->iDelta > 0)
// Up - Do stuff;
else if(pNMUpDown->iDelta < 0)
// Down - Do stuff;
}
*pResult = 0;
}
There is also a useful article here where it states:
If you use a spin control for some other purpose, for example, to page through a sequence of windows or dialog boxes, then add a handler for the UDN_DELTAPOS message and perform your custom action there.

VisualStudio MFC CListCtrl SetItemText fails

I created a MFC Visual Studio Project with a CListCtrl. I added some items in the CDialog class like:
int l_iItem = m_listCtrl.InsertItem(LVIF_TEXT|LVIF_STATE, counter, someString, 0, LVIS_SELECTED, 0, 0);
m_listCtrl.SetItemText( l_iItem, 1, blockHexChar );
m_listCtrl.SetItemText( l_iItem, 2, description);
This works fine.
Afterwards i want to edit a subitem (over double click event). Works also fine.
If the editing is finished (this is in the CListCtrl class),
OnEndLabelEdit(NMHDR* pNMHDR, LRESULT* pResult)
will be called. It looks like this
LV_DISPINFO *plvDispInfo = (LV_DISPINFO *)pNMHDR;
LV_ITEM *plvItem = &plvDispInfo->item;
if (plvItem->pszText != NULL)
{
bool res = SetItemText(plvItem->iItem, plvItem->iSubItem, plvItem->pszText);
}
I always getting 0 back, so the SetItemText is failing.
Any idea what i'm doing wrong?
Cheers ehmkey
You have to post yourself a user-defined message (WM_USER+NNN) using PostMessage from within OnEndLabelEdit. Change label in response to that message.
LVN_ENDLABELEDIT passes a pointer to NMLVDISPINFO through lParam. I think you're looking at the wrong structure when processing the notification. Using the class wizard to generate the event handler in VS2013 gives
void CMFCApplication6Dlg::OnLvnEndlabeleditList1(NMHDR *pNMHDR, LRESULT *pResult)
{
NMLVDISPINFO *pDispInfo = reinterpret_cast<NMLVDISPINFO*>(pNMHDR);
// TODO: Add your control notification handler code here
*pResult = 0;
}
Thanks for your input, but the problem was in the MESSAGE_MAP(....) in CDialog class.
I have a member here, which holds the list and I directly mapped to the CListCtrl Class.
BEGIN_MESSAGE_MAP(CPeriDialog, CDialog)
ON_NOTIFY(LVN_ENDLABELEDIT, IDC_LIST1, &CListCtrl::OnEndLabelEdit)
END_MESSAGE_MAP()
Now I made a wrapper function which forward the event to the right object.
BEGIN_MESSAGE_MAP(CPeriDialog, CDialog)
ON_NOTIFY(LVN_ENDLABELEDIT, IDC_LIST1, &CPeriDialog::EndEdit)
END_MESSAGE_MAP()
Function simple looks like this
void CPeriDialog::EndEdit(NMHDR* pNMHDR, LRESULT* pResult)
{
m_listCtrl.OnEndLabelEdit(pNMHDR, pResult);
}

Replicating user input from edit control of one MFC dialog to an edit control of another dialog

In one of my dialog based MFC application, I've used two dialogs of similar look. The requirement is when user populates an edit box of one dialog with some data the same to be replicated to similar edit box of another dialog instantly. I'm trying to implement it with EN_CHANGE event of the edit control; where when any change is detected application post a message with updated data to other dialog to update the content of its own edit box. The problem is when the second dialog is setting its edit box content with the received data from first dialog, EN_CHANGE event is getting triggered from the second dialog, which is obvious, resulting in an endless back and forth message exchange. Could anybody please suggest me some solution for instant replicating user inputs between edit boxes of two MFC dialogs while keeping MFC application type as dialog based?
In my implementation both the Dialogs are CDialog derived and have the following CEdit event handler and Message handler methods:
For CScreen1 class:
void CScreen1::OnEnChangeEditUser()
{
static CString msg;
m_username.GetWindowText(msg);
::PostMessage(m_mScreen2,WM_INTER_LOGIN,10,(LPARAM)&msg); //m_mScreen2 is the HWND of 2nd dlg
}
LRESULT CScreen1::OnInterLoginMsg(WPARAM wParam, LPARAM lParam)
{
CString *msg=(CString*)lParam;
switch((int)wParam)
{
case 10:
m_username.SetWindowText(msg->GetString()); //m_username is CEdit Ctrl
delete msg;
break;
}
return 0;
}
For CScreen2 class:
void CScreen2::OnEnChangeEditUser()
{
static CString msg;
m_username.GetWindowText(msg);
::PostMessage(m_mScreen1,WM_INTER_LOGIN,10,(LPARAM)&msg); //m_mScreen1 is the HWND of 1st dlg
}
LRESULT CScreen2::OnInterLoginMsg(WPARAM wParam, LPARAM lParam)
{
CString *msg=(CString*)lParam;
switch((int)wParam)
{
case 10:
m_username.SetWindowText(msg->GetString()); //m_username is CEdit Ctrl
delete msg;
break;
}
return 0;
}
Simply use a boolean variable to play around. I have updated your code here.
For CScreen1 class:
BOOL postchanges = TRUE; //always TRUE
void CScreen1::OnEnChangeEditUser()
{
if (!postchanges)
return;
static CString msg;
m_username.GetWindowText(msg);
::PostMessage(m_mScreen2,WM_INTER_LOGIN,10,(LPARAM)&msg); //m_mScreen2 is the HWND of 2nd dlg
}
LRESULT CScreen1::OnInterLoginMsg(WPARAM wParam, LPARAM lParam)
{
CString *msg=(CString*)lParam;
switch((int)wParam)
{
case 10:
postchanges = FALSE; // do not post msg
m_username.SetWindowText(msg->GetString()); //m_username is CEdit Ctrl
postchanges = TRUE; // revert back
delete msg;
break;
}
return 0;
}
For CScreen2 class: do the same
As the requirement is replicating user input between two Edit controls of different Dialogs; it can be handled through processing keystroke messages.

CBN_SELCHANGE in Dynamically created ComboBoxes

How to get the CBN_SELCHANGE in dynamically created CComboBoxes??.. I used a array of CComboBoxes.
MyCComboBox * p_ComboBoxes = new MyCComboBox[numberOcComboBoxes]; //numberOcComboBoxes determined at rumtime
In the message Map of MyCComboBox
BEGIN_MESSAGE_MAP(MyCComboBox , CComboBox)
ON_CONTROL_REFLECT(CBN_SELCHANGE, &CTestDlg::OnCbnSelchange)
END_MESSAGE_MAP()
void CTestDlg::OnCbnSelchange()
{
this->GetDlgCtrlID(); // Get The ComboBox ID;
CString sText;
p_ComboBoxes[0].GetLBText(p_ComboBoxes[0].GetCurSel() , sText); // I can't access like this
}
// Initialization of the p_ComboBoxes Array..
for (int i = 0 ;i < iNumber ; i++)
{
p_ComboBoxes[i].Create(WS_CHILD|WS_VISIBLE|WS_VSCROLL|CBS_DROPDOWN,
CRect(10 + MY_PIC_ADDITIONAL_WIDTH,iItemDrawHeight,10 + MY_PIC_ADDITIONAL_WIDTH +MY_PIC_PROPERTY_WIDTH
,iItemDrawHeight +MY_PIC_HEIGHT), this, pImageControlPropertyID[i]);
iItemDrawHeight += MY_PIC_PROPERTY_ADDITIONAL_HEIGHT;
}
I can't access the selected text it gives me "Access violation reading location 0x00000020" error..
As already mentioned, ON_CONTROL_REFLECT is for handling the message in the control class, not in the parent dialog class. You should be implementing your OnCbnSelchange() handler in the MyCComboBox class and adding an ON_CONTROL_REFLECT entry in the MyCComboBox message map.
If you really want to handle the message in the parent dialog, you should use the ON_CONTROL macro - one entry for each child combo each with a separate handler so you can tell which one sent the message.
You could also override OnCommand(WPARAM wParam, LPARAM lParam)
in the parent dialog window. If HIWORD(wParam) == CBN_SELCHANGE, then compare
LOWORD(wParam) against the id's of your controls.
BOOL CMyDlg::OnCommand(WPARAM wParam, LPARAM lParam)
{
if(HIWORD(wParam) == CBN_SELCHANGE) { //A combo box selection changed
int ID = LOWORD(wParam); //The ID of the corresponding ComboBox
//Perform additional handling...
}
return CDialog::OnCommand(wParam, lParam);
}