How to detect a CListCtrl selection change? - visual-c++

I want to execute some code when the user selects a row in a CListCtrl (report view, I don't care about the other viewing modes).
How do I catch this event? is there some message I can map or a method like "OnSelectionChanged" or something like that?

Also try:
BEGIN_MESSAGE_MAP(cDlgRun, CDialog)
ON_NOTIFY(LVN_ITEMCHANGED, IDC_LIST2, OnItemchangedList2)
END_MESSAGE_MAP()
/* ... */
void cDlgRun::OnItemchangedList2(NMHDR* pNMHDR, LRESULT* pResult)
{
NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
if ((pNMListView->uChanged & LVIF_STATE)
&& (pNMListView->uNewState & LVIS_SELECTED))
{
// do stuff...
}
}

There are a few notifications based on what's happening.
If you are selecting an item and nothing is selected yet, you will get one LVIF_STATE change notification: uNewState & LVIS_SELECTED. The newly selected item will be found at:
pNMListView->iItem
If an item is selected before you select a new object, you'll get three state changes:
First you will be informed that the previous item in focus is losing focus:
pNMListView->uOldState & LVIS_FOCUSED
Then you will be notified that the old item is being unselected:
pNMListView->uOldState & LVIS_SELECTED
Finally, you will get the new item selection state:
pNMListView->uNewState & LVIS_SELECTED
(again look at iItem for newly selected item)
So the pitfall we ran across is that, because item deselection results in two notifications, we were doing a lot of repetitive, sometimes detrimental, processing. What we ended up doing was only doing this processing for the 2nd message (pNMListView->uOldState & LVIS_SELECTED), and skipping the same processing after the loss of focus notification.

djeidot is right on.
I just want to add that there is no OnSelectionChanged() because the ListView supports multi-selection (although this can be disabled). Therefore, a single-selection listview will send you two events: Old item unselected AND New item selected.

On my Visual Studio 2010, the visual editor declares a callback in the dialog header file like this:
afx_msg void OnLbnSelchangeListOnvif();
and in the source file:
BEGIN_MESSAGE_MAP(CDialogOnvif, CDialog)
ON_LBN_SELCHANGE(IDC_LIST_ONVIF, &CDialogOnvif::OnLbnSelchangeListOnvif)
END_MESSAGE_MAP()
void CDialogOnvif::OnLbnSelchangeListOnvif()
{
// do stuff...
}

Related

MFC MDI dynamically changing tab style from a property dialog

It has been 10 months since I worked on my app due to a death in the family, just started looking at it again and still not sure how to solve the problem.
The project inquires/help started here:
MFC MDI Collecting control states for the "apply" button routine
Since this is a specific focused question, I didn't want to muck up my other thread, so what I'd like to do is change the documents tab styles after the view is loaded. I know that this can be done because the master repository from Microsoft with all the code examples has a project called VCSamples-master\VCSamples-master\VC2010Samples\MFC\Visual C++ 2008 Feature Pack\TabControl which I have looked at. It dawns on me that even though I can follow its code, the calls are from within the MDI window itself where my issue is I'm trying to do this via a property page dialog using OnApply which changes things.
I was able to do part of this properly with the help of the thread above to the OutputPane successfully because I was able to get the Pane handle and execute. I was told that for the MDI tabs after creation that I need to parse the tabs, count them, and then execute. So my issue here is after I capture the tabs......how to change their styles.
Here is the code as it stands:
BOOL CSettingsUserTabs::OnApply()
{
BOOL bResult = CMFCPropertyPage::OnApply();
if (bResult)
{
// Update Output Pane Tab Styles (Works 100%)
AfxGetApp()->WriteProfileInt(_T("Settings"), _T("UserTabStyle"), m_style_tabs); // Save value to registry
((CMainFrame*)AfxGetMainWnd())->m_wndOutput.m_wndTabs.ModifyTabStyle((CMFCTabCtrl::Style)m_style_tabs);
((CMainFrame*)AfxGetMainWnd())->m_wndOutput.m_wndTabs.RecalcLayout();
//Get the open file tabs in the MDI
for (POSITION pos = AfxGetApp()->GetFirstDocTemplatePosition(); pos != NULL; )
{
CDocTemplate* pTempl = AfxGetApp()->GetNextDocTemplate(pos);
for (POSITION pos1 = pTempl->GetFirstDocPosition(); pos1 != NULL; )
{
CDocument* pDoc = pTempl->GetNextDoc(pos1);
for (POSITION pos2 = pDoc->GetFirstViewPosition(); pos2 != NULL; )
{
CView* pView = pDoc->GetNextView(pos2);
if (pView->IsKindOf(RUNTIME_CLASS(CTrainView)))
{
// THIS IS WHERE MY ISSUE IS, NOW THAT ALL THE TABS ARE
// CAPTURED, HOW DO I ADDRESS THEM LIKE WHAT IS SHOWN
// ABOVE:
//((CMainFrame*)AfxGetMainWnd())->xxxxxx.yyyyyy.ModifyTabStyle((CMFCTabCtrl::Style)m_style_tabs);
}
}
}
}
}
return bResult;
}
If I can figure this last piece out, I'll be basically finished, I just can't seem to find a solution on how to do this via property sheet via OnApply.
Any suggestions or actual code examples I can see to solve my problem?
FYI: No, I haven't had any time to take additional OOP to solve this. I'm hoping someone can provide some guidance so I can move on after getting this sorted.
Thanks,
Chris
EDIT 1:
So I took a closer look at Constantine's suggestion and here is what I came up with:
BOOL CSettingsUserTabs::OnApply()
{
BOOL bResult = CMFCPropertyPage::OnApply();
if (bResult)
{
// Update Output Pane Tab Styles
AfxGetApp()->WriteProfileInt(_T("Settings"), _T("UserTabStyle"), m_style_tabs); // Save value to registry
((CMainFrame*)AfxGetMainWnd())->m_wndOutput.m_wndTabs.ModifyTabStyle((CMFCTabCtrl::Style)m_style_tabs);
((CMainFrame*)AfxGetMainWnd())->m_wndOutput.m_wndTabs.RecalcLayout();
CMFCTabCtrl& MDI_STYLES = ((CMainFrame*)AfxGetMainWnd())->GetMDITabs();
MDI_STYLES.ModifyTabStyle((CMFCTabCtrl::Style)m_style_tabs);
MDI_STYLES.RecalcLayout();
CMDIFrameWndEx* pMainFrame = DYNAMIC_DOWNCAST(CMDIFrameWndEx, GetTopLevelFrame());
pMainFrame->SetFocus();
pMainFrame->RecalcLayout();
}
return bResult;
}
The m_styles_tabs is getting the index value of 0-8 when I select the radio button. The code compiles and runs and I see the index value change when I break on it, but the tabs for the MDI are still not updating. Does the edited code make sense based on the members shown here:
https://learn.microsoft.com/en-us/cpp/mfc/reference/cmfctabctrl-class?view=msvc-170#modifytabstyle
I think this the right direction, am I missing something?

How to guarantee that I get the `HTREEITEM` for the item that the user right-clicked on in a CTreeControl

I have a window which has a CTreeCtrl. A user can right-click any element and display a context menu. From there they can choose to delete the entry. Something like this:
Here is a snippet of the context menu item handler:
void CAssignHistoryDlg::OnDeleteFromAssignmentHistory()
{
CString strINI = theApp.GetAssignHistoryPath();
HTREEITEM hItem = m_treeHistory.GetSelectedItem();
CString strName, strDeletedName, strEntry;
if (m_treeHistory.GetParentItem(hItem) != nullptr)
{
// The user has picked one of the history dates.
// So the parent should be the actual name.
hItem = m_treeHistory.GetParentItem(hItem);
// Now OK to proceed
}
strName = ExtractName(hItem);
GetParent()->EnableWindow(FALSE);
strEntry.Format(IDS_TPL_SURE_DELETE_FROM_ASSIGN_HIST, strName);
if (AfxMessageBox(strEntry, MB_YESNO | MB_ICONQUESTION) == IDNO)
{
The image shows my problem. If I first click on Test, so that it is selected and bright blue and then right-click, it shows Test in the popup message. This is fine. But ...
If the first name is initially selected, and I go ahead and directly right-click Test, even though it seems to go blue (as if selected), m_treeHistory.GetSelectedItem() is returning the original, first name. I think I am describing it not very well.
In short, I want to guarantee that I get the HTREEITEM for the item that the user right-clicked on. What I have is not 100% fool-proof.
If it helps, this is how I display the context menu:
void CAssignHistoryDlg::OnNMRclickTreeHistory(NMHDR *pNMHDR, LRESULT *pResult)
{
CMenu mnuContext, *pMnuEdit = nullptr;
CPoint ptLocal;
LPNMTREEVIEW pNMTreeView = reinterpret_cast<LPNMTREEVIEW>(pNMHDR);
GetCursorPos(&ptLocal);
mnuContext.LoadMenu( IDR_MENU_SM_ASSIGN_HIST_POPUP );
pMnuEdit = mnuContext.GetSubMenu( 0 );
if (pMnuEdit != nullptr)
{
pMnuEdit->TrackPopupMenu( TPM_LEFTALIGN|TPM_LEFTBUTTON,
ptLocal.x, ptLocal.y, this, nullptr );
}
*pResult = 0;
}
So to recap, at the moment the user must physically left click on a item in the tree to select it. Then they can right-click and it will offer to delete this person. But if they go ahead and just right-click on anyone, it will not offer to delete THAT person.
You can get the actual tree item at any given point using the HitTest() member of the CTreeCtrl class. Exactly how and where you do this will depend on your code design but, if you have a pointer to your CTreeCtrl (pwTree in the code below), then you can do something like this:
CPoint ptLocal;
GetCursorPos(&ptLocal);
pWTree->ScreenToClient(&ptLocal); // May not be required in certain handlers?
HTREEITEM hItem = pWTree->HitTest(ptLocal); // Remember to check for NULL return!
You can then either use the returned hItem directly, or use it to explicitly set the tree's selection (to that item), before doing any further processing.
The MS doc: https://learn.microsoft.com/en-us/cpp/mfc/reference/cwnd-class?view=vs-2019 doesn't have a "click" handler, it has CWnd::OnRButtonDblClk, CWnd::OnRButtonDown and CWnd::OnRButtonUp. Which one are you handling?
The reason for your defect might be that you don't let the tree control handle that right button event (to select that new item).
I would suggest to use CWnd::OnContextMenu instead.

Preserve Selection when Scrolling GXT

I am using Sencha GXT Grid for a web app. But what I see is after scrolling the grid the selection is gone. I tried to preserve the selection by catching the scroll event and restoring the selected items (using setsecteditems() ). But was not successful also.
Is there a method to preserve the selection in sencha GXT grid.
Thanx
I have finally able to preserve the selection in Live grid view. I found two ways that I thought worth to share.It's kind of a hack :)
1. If you are receiving data from a server. You can maintain a boolean in server side data preserving the selection. and when you render rows in the client side you can add a style name to that row checking the boolean which is set previously.
the style name can be set using
grid.getView().setViewConfig(new GridViewConfig() {
#Override
public String getColStyle(Object model, ValueProvider<? super Data, ?> valueProvider, int rowIndex, int colIndex) {
return null;
}
#Override
public String getRowStyle(Objectmodel, int rowIndex) {
//Do the logic here and return the Style name
return null;
}
});
You can also maintain a list of keys in client side which contains the selected items. and use the previous method to add a style name if the row you are drawing is in the list.
Thanx :)

Sharepoint Toolpart Event Not Firing

I have created a Sharepoint WebPart, and I have given it a custom ToolPart that includes a Grid (a Telerik RadGrid, to be exact, though that is rather irrelevant). I have populated the grid, and created a GridButtonColumn object to add to the grid:
protected override void CreateChildControls()
{
GridButtonColumn c = new GridButtonColumn();
c.ConfirmText = "Really Delete?";
c.ConfirmDialogType = GridConfirmDialogType.RadWindow;
c.ConfirmTitle = "Delete";
c.ButtonType = GridButtonColumnType.LinkButton;
c.Text = "Delete";
c.UniqueName = "DeleteColumn";
grid.Columns.Add(c);
// ...
grid.DeleteCommand += new GridCommandEventHandler(Grid_DeleteCommand);
}
The grid renders correctly - populated with data and with the delete button present.
Now, when I click any of the delete button, the Grid_DeleteCommand() event does not get triggered. However, when I add a random button outside of the grid, it's click event gets triggered:
Button b = new Button();
b.Text = "Hello World";
b.Click += new EventHandler(Button_Click);
I'm not able to debug on this installation of Sharepoint (or maybe I can, but attaching to the process hasn't allowed me to do so yet), so the method of both of those events is simply a redirection to Google. That is how I check to see if the events fire:
string AbsoluteUri ="http://www.google.com";
Page.Response.Redirect(AbsoluteUri);
The only difference I can see between the two is that, with the 'Delete' button, it is nested inside of a Grid control, whereas with the 'Hello World' button, there is no nesting.
How would I be able to have the Grid_DeleteCommand fire when I click the button in the grid?
Using the Telerik Grid control, you should specify button's CommandName in your code.
Adding this line should solve the problem:
c.CommandName = "Delete";

Problem in Handling the Mouse click of CListCtrl

I have a listctrl with CheckBox in it(LVS_EX_CHECKBOXES) .It is a single column List Control . My Problem is when I Click on the CheckBox the particular item is getting selected/UnSelected. But when I click on the Item text the corresponding Checkbox is not getting Selected/UnSelected . How to handle both the Scenarios.
To check the item when the user clicks on the item text, you'll have to handle the NM_CLICK message, which is sent whenever the user clicks on the item.
Something along the lines of:
CYourListCtrl::OnNMClick(NMHDR* pNMHDR, LRESULT* pResult)
{
LPNMITEMACTIVATE pNMItemActivate = reinterpret_cast<LPNMITEMACTIVATE>(pNMHDR);
int nItemIndex = pNMItemActivate->iItem;
BOOL bCurrentCheckState = GetCheck(nItemIndex);
SetCheck(nItemIndex, !bCurrentCheckState);
*pResult = 0;
}
I'm writing this without testing though, so you'll have to make sure that it doesn't conflict with the handler for clicks on the check box iteself.

Resources