MFC limit selected item in ClistCtrl - c++

Hi
I use ClistCtrl that have 20 items and I want to limit selected item number.
for example only 10 item can be selected.
how i can do it?
thanks for your help herzl.

You would have to handle the LVN_ODSTATECHANGED notification message and count the number of selected item each time the LVIS_SELECTED state changes
Thanks

So I wrote this code. It should work. Just create an event handler for the list
void CDatenbankView::OnLvnItemchangedList1(NMHDR *pNMHDR, LRESULT *pResult)
{
int SelctedItems;
SelctedItems = 0;
int Index;
LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
for (Index = 0; Index < m_List.GetItemCount(); ) //Check every Item
{
if (m_List.GetItemState (Index, LVIS_SELECTED) == LVIS_SELECTED) //Checks if it is selected
{
if (SelctedItems > 10)
{
MessageBox (_T("Cant select more than 10 Items"));
for (Index = 0; Index < m_List.GetItemCount(); )
{
m_List.SetItemState (Index, ~LVIS_SELECTED, LVIS_SELECTED);
Index++;
}
break;
}
else
{
SelctedItems++;
}
}
Index++;
}
*pResult = 0;
}
m_List is my control variable for the CListCtrl

There is no built-in functionality for such a feature. You'd have to write your our code for that. Maybe you can find another way to do it, like having a source list and a "selection list". You copy/move items from the first to the second, but you do not allow the users to put more than 10 items into the destination list.

Related

How to get notification for keydown for CMFCRibbonComboBox?

I have CMFCRibbonComboBox on ribonbar and I want when that user press on a key open droplist and select Item acurding to chars that press by user.
For this purpose I want to get notification for keydown.
How can I to do it?
Thanks
I asked a very similar question on MSDN here and eventually solved it myself with the following hack;
Save a local copy of C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\atlmfc\src\mfc\afxribbonedit.cpp to your project
In BOOL CMFCRibbonRichEditCtrl::PreTranslateMessage(MSG* pMsg) replace this
case VK_DOWN:
if (m_edit.m_bHasDropDownList && !m_edit.IsDroppedDown())
{
m_edit.DropDownList();
return TRUE;
}
with this
case VK_DOWN:
if (m_edit.m_bHasDropDownList && !m_edit.IsDroppedDown())
{
m_edit.DropDownList();
CMFCRibbonBaseElement* pRibbonBaseElement = m_edit.GetDroppedDown();
if (pRibbonBaseElement && (pRibbonBaseElement->IsKindOf(RUNTIME_CLASS(CMFCRibbonComboBox))))
{
CString str;
GetWindowText(str);
CMFCRibbonComboBox *pCombo = (CMFCRibbonComboBox*)pRibbonBaseElement;
int ItemNo = -1;
for (int i = 0; i < pCombo->GetCount(); i++)
{
CString ItemText = pCombo->GetItem(i);
if (ItemText.Left(str.GetLength()).CompareNoCase(str) == 0)
{
ItemNo = i;
break;
}
}
if (ItemNo != -1)
{
pCombo->OnSelectItem(ItemNo);
// Draw and redraw dropdown for selection to show
m_edit.DropDownList();
m_edit.DropDownList();
}
}
return TRUE;
}
For drop lists (as opposed to drop downs) you can similarly hand WM_CHAR to do a first letter search based on the next item after the current position. Note that the above hack would need to be checked against any future updates to the ribbon library and should be dumped once it has been properly implemented in the library.

issue with retrieving current list item text from CListCtrl

I am trying to retrieve the selected list item from CListCtrl. The first item text is retrieved correct. Later on when I go select next, only the previous list item text is retrieved. Below is my event method that is triggered when I select an item from CListCtrl.
example scenario
List(m_RListCtrl) -> Item1, Item2, Item3
First time I click/select Item2. Item2 text displayed in m_EditBox.
Next I click Item3. Item2 is still displayed
Then I click Item1. Item3 is displayed in the editbox
then I click Item2. Item1 is displayed.
...
...
...
event code :
void CRTConfigDlg::OnLvnItemchangedRepoConfigList(NMHDR *pNMHDR, LRESULT *pResult)
{
CString itemText = L"";
itemText = m_RListCtrl.GetItemText(m_RListCtrl.GetSelectionMark(), 0);
m_EditBox.SetWindowText(itemText);
//UpdateWindow();
}
I have even tried following solution from Get Index of Item Text in MFC CListCtrl. But still the issue was same.
Can you help me to know , where I am going wrong?
You can also use the Itemchanged Notification but you have to keep in mind, that this event is triggered when an item is selected and deselected.
So you need to examin the items state.
void CAnyDialogClass::OnLvnItemchanged(NMHDR *pNMHDR, LRESULT *pResult)
{
LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
// check if the items state changed to selected.
if ((pNMLV->uChanged & LVIF_STATE)!=0 &&
(pNMLV->uOldState & LVIS_SELECTED)==0 &&
(pNMLV->uNewState & LVIS_SELECTED)!=0)
{
// This item is selected now
...
Even more precise is to use LVIS_FOCUSED. The user may change the focus of an item by just holding the Ctrl key and using the cursor movement keys.
You need to iterate through selected items like this:
int nColumns = m_RListCtrl.GetHeaderCtrl()->GetItemCount();
POSITION pos = m_RListCtrl.GetFirstSelectedItemPosition();
while (pos)
{
int nItem = m_RListCtrl.GetNextSelectedItem(pos);
for(int i=0; i<nColumns; i++)
{
CString sItem = m_RListCtrl.GetItemText(nItem, i);
// TO DO: do stuff with item text here
}
}

How to react to LVN_ITEMCHANGED notification only when user finishes selecting action on multipe selection CListCtrl?

I have a dialog with two list controls and one custom control with some graphic preview.
The first has a list of one kind of entities (1a, 1b, 1c,...) and the second has a list of another kind of entites (2a, 2b, 2c,...), both of them are multi-select.
I want to allow user to select a set of entities that will be highlighted on preview, but only those from a list where the last selection has been made.
For example:
select 1a,1b,1c -> highlight them on preview
select 2a,2b,2c -> unhighlight 1a,1b,1c and highlight 2a,2b,2c
If I process each LVN_ITEMCHANGED notification, the preview will be flickering, so I want to paint the preview when user finishes selection with a function like this:
void CPreviewPage::PaintSelection(HWND hWnd)
{
m_preview.DeselectAll();
SelectArray select;
if(hWnd == m_lstFirst.GetSafeHwnd())
{
for(int i = 0; i < m_lstFirst.GetItemCount(); i++)
{
if( m_lstFirst.GetItemState(i, LVNI_SELECTED) & LVNI_SELECTED)
{
Entity *pEnt = (Entity *) m_lstFirst.GetItemData(i);
select.append(pEnt);
}
}
}
else
if(hWnd == m_lstSecond.GetSafeHwnd())
{
for( int i = 0; i < m_lstSecond.GetItemCount(); i++ )
{
if( m_lstSecond.GetItemState(i, LVNI_SELECTED) & LVNI_SELECTED)
{
Entity *pEnt = (Entity *) m_lstSecond.GetItemData(i);
select.append(pEnt);
}
}
}
m_preview.PaintSelect(&select);
}
The problem is; when I have 2a selected, then hold shift and click 2c (to select 2a-2c), I get multiple LVN_ITEMCHANGED and can't detect which of them is last. If I could, then it would be possible to repaint the preview at the right moment, which is when the user finishes his/her selecting action.
I tried to call the repaint function when I get LVNI_FOCUSED:
void CPreviewPage::OnLstSecondSelChanged(NMHDR *pNMHDR, LRESULT *pResult)
{
NM_LISTVIEW* pNMListView = (NM_LISTVIEW*) pNMHDR;
if((pNMListView->uChanged & LVIF_STATE) && (pNMListView->uNewState & LVNI_FOCUSED) )
PaintSelection(pNMHDR->hwndFrom);
}
However, the LVNI_FOCUSED isn't guaranteed to be the last, and I don't want to add a button to call PaintSelection function.
So the question is: when is the right moment when I will have the state of all items set, according to user selection so I can call PaintSelection?

In QListWidget how do i check if QListWidgetItem already exists based on its Data member

im setting item's in QListWidget and in each QListWidgetItem im setting id like this:
newItem->setData(Qt::DisplayRole,ID);
now each time before im adding the itemi wish to check if already there is item with the same data in the list .
how can i do that ... i don't think the findItems will help me here
Let me assume that type of ID is int (cause you didn't specify it).
bool found = false;
for (int i = 0; i < list->count(); ++i) {
if (list->item(i)->data(Qt::DisplayRole).toInt() == ID_to_match) {
found = true;
break;
}
}
if (!found) {
do_something_here();
}

CTreeCtrl - getting an item position

Is there a way of getting the position (index) of an item in a CTreeCtrl?
I am interested in the index of a node at its particular level.
I was thinking to maintain the item positions within the item "data" field, but the problem is that my tree is sorted and I cannot predict the position an item will receive (well, only if I sort the items in advance which I would like to avoid).
I don't think you can. I assumed that maybe the control could be treated as an array (maybe it still can but I can't find a reference).
Anyways, there are no member functions (according to the MFC API) that give you access to that information
/// there is another way if you "Use Unicode Character Set" (visual studio)
/// Properties->General->Character Set
CPoint point;
GetCursorPos(&point);
m_Tree.ScreenToClient(&point);
UINT nHitFlags;
HTREEITEM hItem = m_Tree.HitTest(point, &nHitFlags);
int idx = m_Tree.MapItemToAccId(hItem);
Get the node handle and then iterate over the elem
Iterate over all the elements, while you count the elements, till you reach the right item?
int GetIndex(const CString & a_Cstr)
{
int idx = 0;
std::vector<CString>::const_iterator _begIt = m_RulesVec.begin();
std::vector<CString>::const_iterator _PosIt = find(m_RulesVec.begin(), m_RulesVec.end(), a_Cstr);
if (_PosIt == m_RulesVec.end()) {
return -1;
}
else {
while (_begIt != _PosIt) {
++idx;
++_begIt;
}
return idx;
}
}
/// it can(must) be done in this function
/// OnNMClickRulesTree(NMHDR *pNMHDR, LRESULT *pResult)
// Create vector like this
std::vector<CString> Vec{"first", "second", "third" };
// OnInit insert items to CtreeCtrl like this
m_Tree.InsertItem("first", hItem);
m_Tree.InsertItem("second", hItem);
m_Tree.InsertItem("third", hItem);
// then get cur selected item like this
CPoint point;
GetCursorPos(&point);
m_Tree.ScreenToClient(&point);
UINT nHitFlags;
HTREEITEM hItem = m_Tree.HitTest(point, &nHitFlags);
// get item text
CString Cstr = m_Tree.GetItemText(hKid);
int idx = GetIndex(Cstr);