I want to display item lastly checked in tree control mfc - mfc

I tried this
void GetCheckedItems(const CTreeCtrl& tree, CArray<HTREEITEM> *checkedItems, HTREEITEM startItem = NULL)
{
if (startItem == NULL)
startItem = tree.GetRootItem();`
for (HTREEITEM item = startItem; item != NULL; item = tree.GetNextItem(item, TVGN_NEXT))
{
// figure out if this item is checked or not
UINT state = (tree.GetItemState(item, TVIS_STATEIMAGEMASK) >> 12) & 15;
if (state == 2)
checkedItems->Add(item);
// deal with children if present
HTREEITEM child = tree.GetNextItem(item, TVGN_CHILD);
if (child != NULL)
GetCheckedItems(tree, checkedItems, child);
}
}
now I have an array contains checked values but how do i know which i checked last time.

Just use a handler to TVN_ITEMCHANGED. It is called whenever an item state is changing.
You need to check the state flags that are changed. You can detect changes to the TVIS_STATEIMAGEMASK.

CString m;
HTREEITEM selItem;
selItem=m_treeRel.GetSelectedItem();
UINT uFlags = 0;
CPoint pt(0, 0);
GetCursorPos(&pt);
m_treeRel.ScreenToClient(&pt);
HTREEITEM hItem = m_treeRel.HitTest(pt, &uFlags);
if(NULL != hItem && (TVHT_ONITEM & uFlags))
{
/*if(selItem == hItem)
{
m=_T("Selected Item....");
}
else */
if(!m_treeRel.GetCheck(hItem))
{
m=m_treeRel.GetItemText(hItem);
//m_treeRel.SetCheck(hItem,true);
}
else
{
//m=_T("ERROR");
//m_treeRel.SetCheck(hItem,false);
}
}
ShowMessage(m);
*pResult = 0;
I Used This to find out which item was checked last time....!

Related

Virtual list (Problems with parameter/pointer)

I am trying to convert my CListCtrl into a virtual list, but i dont know which parameter i have to use
// --- Virtual List ---
void CSpielebibliothekGUIDlg::OnGetdispinfoList(NMHDR* pNMHDR, LRESULT* pResult) // --- nullptr muss weg ---
{
LPNMITEMACTIVATE pNMIA = reinterpret_cast<LPNMITEMACTIVATE>(pNMHDR);
LV_DISPINFO* pDispInfo = (LV_DISPINFO*)pNMHDR;
LV_ITEM* pItem = &(pDispInfo)->item;
int itemid = pItem->iItem;
if (pItem->mask & LVIF_TEXT)
{
CString text;
// --- Welche Spalte ---
if (pItem->iSubItem == 0)
{
// --- Name ---
text = Spiele[itemid].m_Name;
}
if (pItem->iSubItem == 1)
{
//Text is slogan
text = Spiele[itemid].m_Plattform;
}
if (pItem->iSubItem == 2)
{
text = Spiele[itemid].m_Genre;
}
if (pItem->iSubItem == 3)
{
CString Release;
Release.Format(_T("%d"), Spiele[itemid].m_Erscheinungsjahr);
text = Release;
}
if (pItem->iSubItem == 4)
{
CString Preis;
Preis.Format(_T("%g"), Spiele[itemid].m_Preis);
text = Preis;
}
if (pItem->iSubItem == 5)
{
CString EAN;
EAN.Format(_T("%d"), Spiele[itemid].m_EAN);
text = EAN;
}
if (pItem->iSubItem == 6)
{
text = Spiele[itemid].Verwandschaft;
}
lstrcpyn(pItem->pszText, text, pItem->cchTextMax);
}
*pResult = 0;
}
this is my function call
OnGetdispinfoList(nullptr, nullptr);
of course the nullptr are not right, iam glad for any help.
You don't need to call message handlers, they will be called by system after you:
Set proper window styles (LVS_OWNERDATA)
Set item count
Add your OnGetdispinfoList to the message map

treeview 3-state and 2-state checkboxes on different nodes

I have to create a treeview control in a class that inherits from CTreeCtrl. After many tries, I have created 3-state checkboxes for every node. And here comes my question - I need some child nodes to have only 2 states (checked or unchecked). Any clue on how can I reach that goal? I tried to add an if statement to change the behavior like this:
else if (itemState == INDEXTOSTATEIMAGEMASK(3))
{
SetItemState(hItem, INDEXTOSTATEIMAGEMASK(1), TVIS_STATEIMAGEMASK);
And it’s working! Except when an element is "selected", then it’s like ignoring every behavior I did in the OnLButtonClick event.
Is there some other event I don’t know about? Is there an easier way to get a 2-state checkbox only on some nodes and not others?
This is all i use to control the tree
void CSelDepTreeCtrl::OnLButtonDown(UINT nFlags, CPoint point)
{
CTreeCtrl::OnLButtonDown(nFlags, point);
UINT Flags = 0;
HTREEITEM hItem = HitTest(point, &Flags);
if ((NULL != hItem) && (TVHT_ONITEMSTATEICON & Flags))
{
auto itemText = GetItemText(hItem);
if (itemText.Left(1) == "C") {
if ((nFlags & (MK_SHIFT | MK_CONTROL)) || NULL == GetParentItem(hItem)) {
SetCheckChild(hItem, GetCheck(hItem));
}
}
if (itemText.Left(1) == "L") {
if ((nFlags & (MK_SHIFT | MK_CONTROL)) || NULL == GetParentItem(hItem)) {
SetCheckChild(hItem, GetCheck(hItem));
}
}
if (itemText.Left(1) == "S") {
auto itemState = GetItemState(hItem, TVIS_STATEIMAGEMASK);
if (itemState == INDEXTOSTATEIMAGEMASK(1))
{
SetItemState(hItem, INDEXTOSTATEIMAGEMASK(2), TVIS_STATEIMAGEMASK);
}
else if (itemState == INDEXTOSTATEIMAGEMASK(2))
{
SetItemState(hItem, INDEXTOSTATEIMAGEMASK(1), TVIS_STATEIMAGEMASK);
}
else if (itemState == INDEXTOSTATEIMAGEMASK(3))
{
SetItemState(hItem, INDEXTOSTATEIMAGEMASK(1), TVIS_STATEIMAGEMASK);
}
}
}
}

How to get menu ID from HMENU?

I'm trying to override WM_HELP message for my dialog window when a user presses F1 key. The window has several buttons that display context menus via TrackPopupMenu, as well as the main menu (on top.) So there's more than just one menu in this window.
I can trap menu message as such when processing WM_HELP, but I need to know which context menu sent this message:
BOOL CMyDialog::OnHelpInfo(HELPINFO* pHelpInfo)
{
if(pHelpInfo->iContextType == HELPINFO_MENUITEM)
{
HMENU hMenu = (HMENU)pHelpInfo->hItemHandle;
//How to get menu ID from HMENU?
}
//...
}
So I need to find a way to get menu resource ID from HMENU -- this one that was used to create it:
Any idea how to do it?
You'll have to "somewhat bruteforce" it. You can approach it from the top-down. Make a function like this:
BOOL IsMenuItemIDInMenu(UINT nMenuID, UINT nMenuItemID)
{
//Checks if 'nMenuItemID' belongs to 'nMenuID'
BOOL bRes = FALSE;
if(nMenuID &&
nMenuItemID)
{
HMENU hMenu = ::LoadMenu(GetModuleHandle(NULL), MAKEINTRESOURCE(nMenuID));
if(hMenu)
{
//Look for it
bRes = __searchForMenuItem(hMenu, nMenuItemID);
//Free menu
::DestroyMenu(hMenu);
}
}
return bRes;
}
BOOL __searchForMenuItem(HMENU hMenu, UINT nMenuItemID)
{
ASSERT(hMenu);
int nCnt = ::GetMenuItemCount(hMenu);
if(nCnt != -1)
{
for(int i = 0; i < nCnt; i++)
{
//Is it a submenu
HMENU hSubMenu = ::GetSubMenu(hMenu, i);
if(!hSubMenu)
{
UINT nID = ::GetMenuItemID(hMenu, i);
if(nID != -1 &&
nID == nMenuItemID)
{
//Found it
return TRUE;
}
}
else
{
//Process submenu
if(__searchForMenuItem(hSubMenu, nMenuItemID))
{
//Found it in submenu
return TRUE;
}
}
}
}
return FALSE;
}
And then when you get a menu ID that was highlighted when F1 was pressed, see which of your menus it belongs to:
if(pHelpInfo->iContextType == HELPINFO_MENUITEM)
{
if(IsMenuItemIDInMenu(IDR_MENU_1, pHelpInfo->iCtrlId))
{
}
else if(IsMenuItemIDInMenu(IDR_MENU_2, pHelpInfo->iCtrlId))
{
}
}

Retrieving the LPARAM with an item in treectrl using CTreeCtrl

I have to add a list of sub keys recursively to an item in a Tree control.
I am using InsertItem as shown below. I am attaching the path of this element so that i can retrieve when clicked on the tree control.I am able to add the value but not able to retrieve it.
void CMyDlg::FillTreeWithRegistryKeysEx(CString sPath, HTREEITEM hItem)
{
CString sRegKey = sPath.Left(sPath.Find(_T("\\")));
sPath = sPath.Mid((sPath.Find(_T("\\")) + 1));
sPath = CleanRegistryKey(sPath);
int nKeyCount;
CString sSubKey = _T("");
HKEY handle = GetHkey(sRegKey);
HTREEITEM hReItem = NULL;
HKEY phkey;
std::vector<CString> sSubFolders;
if (RegOpenKeyEx(handle, sPath, 0, KEY_ALL_ACCESS, &phkey) == ERROR_SUCCESS)
sSubFolders = EnumRegistryKey(phkey);
nKeyCount = sSubFolders.size();
for (int nIndex = 0; nIndex < nKeyCount; nIndex++)
{
sSubKey = sPath + _T("\\") +sSubFolders.at(nIndex);
hReItem = m_cTreeCtrl.InsertItem(TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_PARAM | TVIF_STATE, sSubFolders.at(nIndex),
icoClosedFolder, icoOpenFolder, 0, 0, (LPARAM)(LPCTSTR)sSubKey, hItem, TVI_LAST);
FillTreeWithRegistryKeys(handle, sSubKey, hReItem);
}
RegCloseKey(phkey);
}
While retrieving the string is always blank.
void CMyDlg::OnTvnSelchangedTree(NMHDR *pNMHDR, LRESULT *pResult)
{
LPNMTREEVIEW pNMTreeView = reinterpret_cast<LPNMTREEVIEW>(pNMHDR);
LPARAM lp = pNMTreeView->itemNew.lParam;
CString sKey = (LPCTSTR)lp;
}
what is going wrong?
The application-defined LPARAM passed to InsertItem is a string object that goes out of scope once the function completes not to mention it is replaced with each loop iteration with a new string. I don't think that is going to work.
The TreeView control however allocates its own copy of the text, so you are safe to fill in the pszText member with local buffers as it appears you are doing.
As pointed out in previous answer, you are using LPARAM to point to a string which is destroyed when the function exits. The solution is store the strings in permanent storage. There are many ways to do this. One option is to use CStringArray member data:
class CMyDlg : public CDialog
{
CStringArray m_strings;
...
};
Store the registry keys in m_strings.
Add the tree item with InsertItem, this returns HTREEITEM.
Use tree.SetItemData to match the tree item with an index in m_strings
Use tree.GetItemData to retrieve that index
Example:
BOOL CMyDlg::OnInitDialog()
{
CDialog::OnInitDialog();
...
m_strings.RemoveAll();
addKey(L"SOFTWARE\\Microsoft\\Internet Explorer", tree.GetRootItem());
return TRUE;
}
void CMyDlg::addKey(CString path, HTREEITEM parent)
{
CRegKey rg;
if (ERROR_SUCCESS != rg.Open(HKEY_CURRENT_USER, path))
return;
for (int i = 0;; i++)
{
wchar_t buf[300];
DWORD bufsize = 300;
if (rg.EnumKey(i, buf, &bufsize) != ERROR_SUCCESS) break;
HTREEITEM hitem = tree.InsertItem(buf, parent);
CString subkey = path + L"\\" + buf;
m_strings.Add(subkey);
tree.SetItemData(hitem, m_strings.GetCount() - 1);
addKey(subkey, hitem);
}
}
void CMyDlg::OnTvnSelchangedTree(NMHDR *pNMHDR, LRESULT *pResult)
{
HTREEITEM hitem = tree.GetSelectedItem();
if (!hitem) return;
int i = tree.GetItemData(hitem);
if (i >= 0 && i < m_strings.GetCount())
SetWindowText(m_strings[i]);
}

When code accidently starts to work without a real change

I have run into similar situations before when my code is not working properly and on my quest to solve the problem I made some changes than comment those changes and boom it fixed the problem. All of sudden just a an 'edit' in the file somewhere has fixed the issue but there was no real change in the code. I ran into similar problem again and I am just wondering how does this happen?
void CDlgResizeHelper::Init(HWND hparent)
{
m_hParent = hparent;
m_CtrlsList.clear();
if (::IsWindow(m_hParent))
{
::GetWindowRect(m_hParent, m_OrigParentSize);
// get all child windows and store their original sizes and positions
HWND hCtrl = ::GetTopWindow(m_hParent);
while (hCtrl)
{
CtrlSize cs;
cs.hctrl = hCtrl;
::GetWindowRect(hCtrl, cs.orig_size);
::ScreenToClient(m_hParent, &cs.orig_size.TopLeft());
::ScreenToClient(m_hParent, &cs.orig_size.BottomRight());
// CString msg;
// msg.Format("Old Size: %d %d %d %d\r\n", cs.orig_size.left, cs.orig_size.top, cs.orig_size.right, cs.orig_size.bottom );
// TRACE( msg );
m_CtrlsList.push_back(cs);
hCtrl = ::GetNextWindow(hCtrl, GW_HWNDNEXT);
}
}
}
This class/function resizes controls based on the dialog size. It was working in debug version but the same code doesn't work (=resize properly) in release version. I made changes and added the three lines in the loop above for TRACE function. It starts to work properly in release version as well. Than I commented these lines, it still works in release build. I removed them and it doesn't work in release build anymore. I have to have these lines just commented present for release build to do the right thing. How can this be justified? What could really cause this 'edit' of file which is really no change in the real code fix the problem?
I also want to add that I have tried 'edit' or commented new code else where in the file that doesn't necessarily fixes the problem. I only need to have the commented code in the above function that would fix it.
Update Here is the complete class. I must say this class is available free somewhere on web and I am not the original author.
Init is called from the OnInitDialog of the dialog which needs resizing. It just stores the coordinates of all controls.
OnSize() actually does the resizing.
CDlgResizeHelper::CDlgResizeHelper()
{
}
void CDlgResizeHelper::Init(HWND hparent)
{
m_hParent = hparent;
m_CtrlsList.clear();
if (::IsWindow(m_hParent))
{
::GetWindowRect(m_hParent, m_OrigParentSize);
// get all child windows and store their original sizes and positions
HWND hCtrl = ::GetTopWindow(m_hParent);
while (hCtrl)
{
CtrlSize cs;
cs.hctrl = hCtrl;
::GetWindowRect(hCtrl, cs.orig_size);
::ScreenToClient(m_hParent, &cs.orig_size.TopLeft());
::ScreenToClient(m_hParent, &cs.orig_size.BottomRight());
CString msg;
msg.Format("Old Size: %d %d %d %d\r\n", cs.orig_size.left, cs.orig_size.top, cs.orig_size.right, cs.orig_size.bottom );
Sleep( 50 );
m_CtrlsList.push_back(cs);
hCtrl = ::GetNextWindow(hCtrl, GW_HWNDNEXT);
}
}
}
void CDlgResizeHelper::Remove(HWND hwnd)
{
CtrlList::iterator it;
for (it = m_CtrlsList.begin(); it != m_CtrlsList.end(); ++it)
{
if (it->hctrl == hwnd)
{
m_CtrlsList.erase(it);
return;
}
}
}
void CDlgResizeHelper::Update(HWND hwnd)
{
if (m_hParent && hwnd)
{
CtrlList::iterator it;
for (it = m_CtrlsList.begin(); it != m_CtrlsList.end(); ++it)
{
if (it->hctrl == hwnd)
{
::GetWindowRect(hwnd, &(it->orig_size));
::ScreenToClient(m_hParent, &(it->orig_size.TopLeft()));
::ScreenToClient(m_hParent, &(it->orig_size.BottomRight()));
}
}
}
}
void CDlgResizeHelper::Add(HWND hwnd)
{
if (m_hParent && hwnd)
{
CtrlSize cs;
cs.hctrl = hwnd;
::GetWindowRect(hwnd, cs.orig_size);
::ScreenToClient(m_hParent, &cs.orig_size.TopLeft());
::ScreenToClient(m_hParent, &cs.orig_size.BottomRight());
m_CtrlsList.push_back(cs);
}
}
void CDlgResizeHelper::OnSize()
{
if (::IsWindow(m_hParent))
{
CRect currparentsize;
::GetWindowRect(m_hParent, currparentsize);
double xratio = ((double) currparentsize.Width()) / m_OrigParentSize.Width();
double yratio = ((double) currparentsize.Height()) / m_OrigParentSize.Height();
HDWP hdwp;
hdwp = BeginDeferWindowPos((int)m_CtrlsList.size());
// resize child windows according to their fix attributes
CtrlList::const_iterator it;
for (it = m_CtrlsList.begin(); it != m_CtrlsList.end(); ++it)
{
CRect currctrlsize;
HorizFix horiz_fix = it->horiz_fix;
VertFix vert_fix = it->vert_fix;
if (horiz_fix & LEFT)
currctrlsize.left = it->orig_size.left;
else
currctrlsize.left = (LONG)( ((horiz_fix & WIDTH) && (horiz_fix & RIGHT)) ? (it->orig_size.left + currparentsize.Width() - m_OrigParentSize.Width()) : (it->orig_size.left * xratio));
if (horiz_fix & RIGHT)
currctrlsize.right = it->orig_size.right + currparentsize.Width() - m_OrigParentSize.Width();
else
currctrlsize.right = (LONG)((horiz_fix & WIDTH) ? (currctrlsize.left + it->orig_size.Width()) : (it->orig_size.right * xratio));
if (vert_fix & TOP)
currctrlsize.top = it->orig_size.top;
else
currctrlsize.top = (LONG)(((vert_fix & HEIGHT) && (vert_fix & BOTTOM)) ? (it->orig_size.top + currparentsize.Height() - m_OrigParentSize.Height()) : (it->orig_size.top * yratio));
if (vert_fix & BOTTOM)
currctrlsize.bottom = it->orig_size.bottom + currparentsize.Height() - m_OrigParentSize.Height();
else
currctrlsize.bottom = (LONG)((vert_fix & HEIGHT) ? (currctrlsize.top + it->orig_size.Height()) : (it->orig_size.bottom * yratio));
UINT flags = SWP_NOZORDER;
if (! it->resize)
flags |= SWP_NOSIZE;
hdwp = ::DeferWindowPos(hdwp, it->hctrl, NULL, currctrlsize.left, currctrlsize.top, (it->resize)? currctrlsize.Width() : 0, (it->resize)? currctrlsize.Height() : 0, flags);
if (hdwp == NULL)
return;
} //end for (it = m_CtrlsList.begin(); it != m_CtrlsList.end(); ++it)
EndDeferWindowPos(hdwp);
} //end if (::IsWindow(m_hParent))
}
BOOL CDlgResizeHelper::Fix(HWND a_hCtrl, HorizFix a_hFix, VertFix a_vFix, bool resize /= true/)
{
CtrlList::iterator it;
for (it = m_CtrlsList.begin(); it != m_CtrlsList.end(); ++it)
{
if (it->hctrl == a_hCtrl)
{
it->horiz_fix = a_hFix;
it->vert_fix = a_vFix;
it->resize = resize;
return TRUE;
}
}
return FALSE;
}
BOOL CDlgResizeHelper::Fix(int a_itemId, HorizFix a_hFix, VertFix a_vFix, bool resize /= true/)
{
return Fix(::GetDlgItem(m_hParent, a_itemId), a_hFix, a_vFix, resize);
}
BOOL CDlgResizeHelper::Fix(HorizFix a_hFix, VertFix a_vFix, bool resize /= true/)
{
CtrlList::iterator it;
for(it = m_CtrlsList.begin(); it!=m_CtrlsList.end(); ++it)
{
it->horiz_fix = a_hFix;
it->vert_fix = a_vFix;
it->resize = resize;
}
return TRUE;
}
UINT CDlgResizeHelper::Fix(LPCTSTR a_pszClassName, HorizFix a_hFix, VertFix a_vFix, bool resize /= true/)
{
char cn_buf[200];
memset(cn_buf, 0, 200);
UINT cnt = 0;
CtrlList::iterator it;
for (it = m_CtrlsList.begin(); it!= m_CtrlsList.end(); ++it)
{
::GetClassName(it->hctrl, cn_buf, sizeof(cn_buf));
if (strcmp(cn_buf, a_pszClassName) == 0)
{
cnt++;
it->horiz_fix = a_hFix;
it->vert_fix = a_vFix;
it->resize = resize;
}
}
return cnt;
}
This sounds like you have uninitialized variable used somewhere. It can get different values depending on build mode and it just so happens that it is assigned something harmless in debug.
Try running CppCheck application against your code. Won't hurt anyway.