Can we add icons for specific tree items?
I am adding items with icon using following function:
HTREEITEM InsertItem(LPCTSTR lpszItem,int nImage,int nSelectedImage,HTREEITEM hParent = TVI_ROOT,HTREEITEM hInsertAfter = TVI_LAST);
To skip icon for an item, i am using -1 value for nImage and nSelectedImage. By doing this, icon is not appearing but space is coming.
Have you looked at CTreeCtrl::SetItem?
The easiest is to fill and pass a TVITEM structure.
typedef struct tagTVITEM {
UINT mask;
HTREEITEM hItem;
UINT state;
UINT stateMask;
LPTSTR pszText;
int cchTextMax;
int iImage;
int iSelectedImage;
int cChildren;
LPARAM lParam;
} TVITEM, *LPTVITEM;
You set the mask to TVIF_IMAGE and specify the iImage value.
To begin, you need to create a CImageList object that stays valid for the duration of the CTreeCtrl. You usually add it to the class as a variable. Example:
m_imgList.Create(IDB_BMP_CHECK_IMAGELIST, 16, 10, 0x0000FF00);
Once it is initialised you can call CTreeCtrl::SetImageList. Example:
m_treeCtrl.SetImageList(&m_imgList, LVSIL_SMALL);
Thereafter you can use the image index values.
Related
I have been trying to get a list of installed fonts on Windows including font style.
After investigation, I found out that I need to use: EnumFontFamiliesEx.
I use it BUT I get only the font name and not all the styles of this font.
for example: for font: "Verdana"
There are four different styles, But I get only one - the regular.
My question is: how can I get the fonts list including all the styles?
My code:
void getFonts()
{
LOGFONT lf;
memset(&lf, 0, sizeof(lf));
lf.lfCharSet = DEFAULT_CHARSET;
HDC hDC = GetDC(NULL);
EnumFontFamiliesEx(hDC, &lf, (FONTENUMPROC)(EnumFontFamExProc), NULL, 0);
}
int CALLBACK EnumFontFamExProc(
ENUMLOGFONTEX *lpelfe,
NEWTEXTMETRICEX *lpntme,
DWORD FontType,
LPARAM lParam
)
{
UTF8String fontName = (lpelfe->elfFullName);
return 1;
}
ENUMLOGFONTEX *lpelfe - contains the font. but I don't get all the different styles
after more investigation, I found out that if I change the lfFaceName to a specific font the method returns all styles.
// To enumerate all styles of all fonts for the ANSI character set
lf.lfFaceName[0] = '\0';
lf.lfCharSet = ANSI_CHARSET;
// To enumerate all styles of Arial font that cover the ANSI charset
hr = StringCchCopy( (LPSTR)lf.lfFaceName, LF_FACESIZE, "Arial" );
So I'm not sure what I should do, I need to get all the styles for all the installed fonts.
Thank you in advance
See the details for EnumFontFamiliesEx, specifically for the lfFaceName parameter:
If set to an empty string, the function enumerates one font in each available typeface name. If set to a valid typeface name, the function enumerates all fonts with the specified name.
The most common scenario is to get a list of families to display in a font-selection UI (e.g., dropdown), not all of the individual faces. For that reason, if you call with lfFacename set to "", that's what you'll get: the list of families. If you really want to get all of the individual faces—with multiple faces per family—then you need to call recursively within the EnumFontFamiliesExProc call back, passing the family name as lfFaceName on the inner loop — you can just use the LOGFONT passed to the call back as the argument into the inner-loop call to EnumFontFamiliesEx:
int CALLBACK GdiFontEnumeration::EnumFontFamiliesExCallback(
_In_ const ENUMLOGFONTEX *lpelfe,
_In_ const NEWTEXTMETRICEX *lpntme,
_In_ DWORD dwFontType,
_In_ LPARAM lParam
)
{
// If no facename parameter was passed in the command line, then
// lParam will be 0 the first time the callback is called for a
// given family. We'll call EnumFontFamiliesEx again passing the
// LOGFONT, and that way we'll get callbacks for the variations
// within the given family. When making the inner-loop call, set
// lParam = 1 so that we don't keep recursing.
if (lParam == 0)
{
LOGFONT lf = lpelfe->elfLogFont;
HDC hdc = CreateDC(L"DISPLAY", NULL, NULL, NULL);
int result = EnumFontFamiliesEx(hdc, &lf, (FONTENUMPROC)EnumFontFamiliesExCallback, 1, 0);
}
else
...
This little program will list all the fonts installed on your system. You can separate them by the style just looking for the suffix. It uses C++17 features so remember to compile it with the correct standard.
i = Italic
b = Bold
bi = Bold and Italic
...
#include <string>
#include <iostream>
#include <filesystem>
int main()
{
std::string path = "C:\\Windows\\Fonts";
for (const auto& entry : std::filesystem::directory_iterator(path))
std::cout << entry.path() << std::endl;
}
I created a dialog with a tree control which fetches data on to a list control when clicked on any particular node of the treecontrol. This is how i tried inserting nodes.
CString *sCommonAppkey = new CString(_szApp + sIsPath);
HTREEITEM hrCommon = m_cTreeCtrl.InsertItem(TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_PARAM, _T("Common"), icoPlanit, icoPlanit, 0, 0, (LPARAM)(LPCTSTR)sCommonAppkey, NULL, NULL);
When clicked on a node it is being directed to the event handler "OnTvnSelchangedExample"
and the data is fetched from the path specified in "lparam" parameter in the insertitem method of HTREEITEM.
void **CExample**::OnTvnSelchangedExample(NMHDR *pNMHDR, LRESULT *pResult)
{
LPNMTREEVIEW pNMTreeView = reinterpret_cast<LPNMTREEVIEW>(pNMHDR);
LPARAM lp = pNMTreeView->itemNew.lParam;
CString *sTempKey = (CString *)lp;
CString path = sTempKey->GetBuffer();
}
I can access the lparam value only in the event handler.
Now i want to implement search functionality for the entire tree's data.
so i need the lparam value of all Tree handles sequentially by iterating through it, so that i can search for the specific text in the tree.
So without clicking on any node of the tree, is there any possibility to get the lparam value of Tree handle(HTREEITEM)
You can iterate through the tree from the root with TreeView_GetChild, there the handle is the tree handle. To get the handle, call TreeView_GetItem.
TVITEMEX item;
item.mask = TVIF_PARAM;
item.hItem = hrCommon;
TreeView_GetItem(handle_, &item);
CString* text = (CString*)item.lParam;
The tree traversal is easy to implement using recursion:
void CMyTreeCtrl::Iterate(HTREEITEM hItem)
{
if (hItem)
{
// Use the tree node corresponding to hItem
// .....
// End of using hItem
hItem = GetNextItem(hItem, TVGN_CHILD);
while (hItem)
{
Iterate(hItem);
hItem = GetNextItem(hItem, TVGN_NEXT);
}
}
else
{
HTREEITEM hItem = GetNextItem(NULL, TVGN_ROOT);
while (hItem)
{
Iterate(hItem);
hItem = GetNextItem(hItem, TVGN_NEXT);
}
}
}
If you want to get the item data you need to simply call GetItemData(hItem). It returns DWORD_PTR. So in your case you need to cast it to CString*. That's it.
IMPORTANT: In this example CMyTreeCtrl is derived from CTreeCtrl.
Trying to use a CScrollBar in my MFC C++ application for Windows7.
I receive all messages just fine and have a handler that looks something like this:
void Dialog::OnHScroll(UINT nSBCode, UINT apos, CScrollBar* pScrollBar)
{
SCROLLINFO si;
si.cbSize = sizeof( si );
si.fMask = SIF_TRACKPOS;
m_slider.GetScrollInfo(&si,SIF_TRACKPOS|SIF_POS|SIF_PAGE);
int nTrackPos = si.nTrackPos; //0 except on TB_THUMBTRACK
int nPos = si.nPos; //0 except on TB_THUMBTRACK
UINT nPage = si.nPage; //seems correct always but I dont need it
The reason I try to extract the position using GetScrollInfo is because they may be bigger than what fits inside a 16bit var, and therefore I cannot use the pos being passed as the argument.
My problem however is that I only get a valid position when dragging the bar and receiving the TB_THUMBTRACK as well as the ending TB_ENDTRACK for drag operations. If I click in the scrollbar or use the arrows at each end all positions (argument pos, and everything in the SCROLLINFO struct except page) will be 0.
Does anyone know how to get the correct positions for all messages? Ie TB_LINEUP, TB_LINEDOWN etc.
Take a look at the sample code for the WM_HSCROLL event handler that's shown in MSDN:
MSDN Documentation
I am working on an old MFC application. There is a TreeView control in the application. OnItemExpanding function is overridden. I am getting children of a TreeViewItem by it is expanded. If a node is expanded for the first time its children are populated. If there are no children to an item then the expand icon (+ sign) removes from the TreeViewItem.
Now problem is that I expanded one node which doesn't have children. After doing some work children are added to that node. But now I cannot get newly added children as expand icon is missing. How I can refresh that particular node in the TreeView. I have created a refresh button. In that I am able to find my current selected node in the TreeView, but what next.
Here is the code.
void CMyTreeView::OnItemExpanding(CTreeCtrl& tree, NMHDR* pNMHDR, LRESULT* pResult)
{
//This is only called when I click on expand (+ sign)
//some check here which populates children.
}
void CMyTreeView::RefreshNode(CTreeCtrl& tree, HTREEITEM selectedNode)
{
// What should I do here?
}
You have to set cChildren of TVITEM to 'one':
TVITEM tvItem = {0};
tvItem.mask = TVIF_HANDLE | TVIF_CHILDREN;
tvItem.hItem = selectedNode;
tvItem.cChildren = 1;
tree.SetItem(&tvItem);
You are trying to reinvent what common controls library already can do for you.
What you need to do is, when you insert a "folder" item set itemex.cChildren = I_CHILDRENCALLBACK which will tell the tree to send you TVN_GETDISPINFO notification when it needs to know if the item has children. It will then similarly send TVN_GETDISPINFO for every individual child.
It will only send the notifications when it is absolutely necessary, so you won't need to do any expensive stuff in vain.
I would say, you need to change the ItemState: http://msdn.microsoft.com/de-de/library/ce034e69%28v=vs.80%29.aspx
BOOL SetItemState(
HTREEITEM hItem,
UINT nState,
UINT nStateMask
);
Take a look at the HTREEITEM:
typedef struct tagTVITEM {
UINT mask;
HTREEITEM hItem;
UINT state;
UINT stateMask;
LPTSTR pszText;
int cchTextMax;
int iImage;
int iSelectedImage;
int cChildren;
LPARAM lParam;
} TVITEM, *LPTVITEM;
cChildren Type: int
Flag that indicates whether the item has associated child items. This member can be one of the following values.
I'd like to implement a function with the prototype
/* Locates the menu item of the application which caused the given menu 'mnu' to
* show up.
* #return true if the given menu 'mnu' was opened by another menu item, false
* if not.
*/
bool getParentMenuItem( HMENU mnu, HMENU *parentMenu, int *parentMenuIdx );
Given a HMENU handle, I'd like to be able to find out which menu item (if any) in the application opened it. This is basically the reverse of the GetSubMenu function.
My current approach is to look into each HMENU of the top level windows of the application and check for whether I can find a menu item which would open the given sub menu when activated. I do this recursively, using GetMenuItemCount/GetSubMenu.
This is rather inefficient though, and it fails for menus which are opened by context menu items. Hence, I'm wondering:
Does anybody have a nice idea how to find the menu item (if any) which opens a given HMENU when activated?
UPDATE: An idea which just came to my mind; it should be possible (using the SetWindowsHookEx function) to install a hook which gets notified of all input events which happened in a menu. Whenever a menu item activation is detected, memorize the menu item (identified by a (HMENU,int) pair) and the HMENU which will get opened by the menu item in a global map. The getParentMenuItem function above could then simply perform a lookup into the map.
UPDATE to the update: The hooking idea described in the update above won't work as it is since it will of course only recognize menu item -> menu associations for items which have been activated at some point.
This feels a bit ugly though since it reqiures me to keep a lot of state (the map); are there any easier possibilities?
You could try setting MENUINFO.dwMenuData to the parent menu handle for all menus you create in your application:
MENUINFO mi;
mi.cbSize = sizeof(MENUINFO);
mi.dwMenuData = (ULONG_PTR)<parent HMENU if this is a sub menu>
mi.fMask = MIM_MENUDATA;
SetMenuInfo(hCreatedMenu, &mi);
Then you only need to query this dwMenuData field in your function:
bool getParentMenuItem(HMENU mnu, HMENU *parentMenu, int *parentMenuIdx)
{
MENUINFO mi;
mi.cbSize = sizeof(MENUINFO);
mi.fMask = MIM_MENUDATA;
if (!GetMenuInfo(mnu,&mi) || mi.dwMenuData == 0)
return false;
*parentMenu = (HMENU)mi.dwMenuData;
// not sure how or why you need the parentMenuIdx, but you should be able
// to derive that from the parent HMENU
return true;
}
Edit: If you don't have control over how all menus are created, you could use a WH_CALLWNDPROC hook to trap when a menu is first created. A good article (with source code) describes how this can be done - you could then look at trying to inject the parent HMENU into the created menu using the method described above.
I just found the same need. I have my menus defined in the .rc file, and I want to gray out access to a popup menu if all of its subitems get grayed out. (You can argue that this inhibits discovery, but, in this particular case, it's what we need).
As previous responders mentioned, if you are creating menus programmatically, you can store an item's parent menu handle as ancillary information.
But for menus defined in the resource file using the POPUP keyword, you can't associate an ID with a POPUP and you can't easily climb up the menu tree programmatically. You have to recursively search down for a menu item, and keep track of the parents.
I wrote the following code to do this. EnableSubmenuItem works like EnableMenuItem to enable or disable a menu item by ID. It then checks the item's parent menu. If all items in its parent menu are disabled, the parent gets disabled. Conversely if any subitem is enabled, the parent gets enabled.
(BTW the original question, how to find parent hMenu of a menu item, is addressed by routine FindParentMenu, in the following code).
////////////////////////////////////////////////////////////////////////////////
// MenuContainsID - return TRUE if menu hMenu contains an item with specified ID
////////////////////////////////////////////////////////////////////////////////
static BOOL MenuContainsID (HMENU hMenu, UINT id)
{
int pos; // use signed int so we can count down and detect passing 0
MENUITEMINFO mf;
ZeroMemory(&mf, sizeof(mf)); // request just item ID
mf.cbSize = sizeof(mf);
mf.fMask = MIIM_ID;
for (pos = GetMenuItemCount(hMenu); --pos >= 0; ) // enumerate menu items
if (GetMenuItemInfo(hMenu, (UINT) pos, TRUE, &mf)) // if we find the ID we are looking for return TRUE
if (mf.wID == id)
return TRUE;
return FALSE;
}
////////////////////////////////////////////////////////////////////////////////
// MenuItemIsSubmenu - returns TRUE if item # pos (position) of menu hMenu is a
// submenu. Sets phSubMenu to menu handle if so
////////////////////////////////////////////////////////////////////////////////
static BOOL MenuItemIsSubmenu (HMENU hMenu, UINT pos, HMENU *phSubMenu)
{
MENUITEMINFO mf;
ZeroMemory(&mf, sizeof(mf)); // request just submenu handle
mf.cbSize = sizeof(mf);
mf.fMask = MIIM_SUBMENU;
if (! GetMenuItemInfo(hMenu, pos, TRUE, &mf)) // failed to get item?
return FALSE;
*phSubMenu = mf.hSubMenu; // pass back by side effect
return (mf.hSubMenu != NULL); // it's a submenu if handle is not NULL
}
////////////////////////////////////////////////////////////////////////////////
// MenuItemIsEnabled - returns true if item # pos (position) of menu hMenu is
// enabled (that is, is not disabled or grayed)
////////////////////////////////////////////////////////////////////////////////
static BOOL MenuItemIsEnabled (HMENU hMenu, UINT pos)
{
return ! (GetMenuState(hMenu, pos, MF_BYPOSITION) & (MF_GRAYED | MF_DISABLED));
}
////////////////////////////////////////////////////////////////////////////////
// FindParentMenu - returns handle of the submenu of menu bar hMenu that contains
// an item with id "id". Position of this submenu is passed by side effect to
// pParentPos, and /its/ parent menu is passed by side effect through phGrandparentMenu.
////////////////////////////////////////////////////////////////////////////////
static HMENU FindParentMenu (HMENU hMenu, UINT id, HMENU *phGrandparentMenu, UINT *pparentPos)
{
int pos; // use signed int so we can count down and detect passing 0
HMENU hSubMenu, hx;
for (pos = GetMenuItemCount(hMenu); --pos >= 0; ) {
if (MenuItemIsSubmenu(hMenu, (UINT) pos, &hSubMenu)) {
if (MenuContainsID(hSubMenu, id)) {
*phGrandparentMenu = hMenu; // set grandparent info by side effect
*pparentPos = (UINT) pos;
return hSubMenu; // we found the item directly
}
if ((hx = FindParentMenu(hSubMenu, id, phGrandparentMenu, pparentPos)) != NULL)
return hx; // we found the item recursively (in a sub-sub menu). It set grandparent info
}
}
return NULL;
}
////////////////////////////////////////////////////////////////////////////////
// AllSubitemsAreDisabled - returns TRUE if all items in a submenu are disabled
////////////////////////////////////////////////////////////////////////////////
static BOOL AllSubitemsAreDisabled (HMENU hMenu)
{
int pos; // use signed int so we can count down and detect passing 0
for (pos = GetMenuItemCount(hMenu); --pos >= 0; )
if (MenuItemIsEnabled(hMenu, (UINT) pos))
return FALSE; // finding one enabled item is enough to stop
return TRUE;
}
////////////////////////////////////////////////////////////////////////////////
// EnableSubMenuItem - like EnableMenuItem, enables or disables a menu item
// by ID (only; not position!) where hMenu is top level menu.
// Added bonus: If the item is in a pop-up menu, and all items in the popup are
// now disabled, we disable the popup menu itself.
//
// Example:
// EnableSubMenuItem(hMainMenu, IDM_CONFIGURE, MF_GRAYED);
//
////////////////////////////////////////////////////////////////////////////////
void EnableSubMenuItem (HMENU hMenu, UINT id, UINT enable)
{
HMENU hParentMenu, hGrandparentMenu;
UINT parentPos;
// EnableMenuItem does its job recursively and takes care of the item
EnableMenuItem(hMenu, id, enable | MF_BYPOSITION);
// But popup menus don't have IDs so we have find the parent popup menu, and its parent (the
// grandparent menu), so we can enable or disable the popup entry by position
if ((hParentMenu = FindParentMenu(hMenu, id, &hGrandparentMenu, &parentPos)) != NULL)
EnableMenuItem(hGrandparentMenu, parentPos,
MF_BYPOSITION | (AllSubitemsAreDisabled(hParentMenu) ? MF_GRAYED : MF_ENABLED));
}