TreeView with items that have nodes icons and without icons - c++

I have a TreeView control in a Windows C++ application that has an ImageList set.
I am trying to insert an node that does not have icon (without TVIF_IMAGE flag) but the icon is still displayed.
TVINSERTSTRUCT tvis = { 0 };
tvis.hParent = hParent;
tvis.hInsertAfter = hInsertAfter;
tvis.item.mask = TVIF_TEXT;
tvis.item.pszText = (LPTSTR) lpszItem;
tvis.item.iImage = 0;
tvis.item.iSelectedImage = 0;
tvis.item.state = nState;
tvis.item.stateMask = nStateMask;
tvis.item.lParam = lParam;
::SendMessage(m_hWnd, TVM_INSERTITEM, 0, (LPARAM)&tvis);
Is that possible/ supported ?

The thing is you are inserting an item with [default] image 0. You not only need -1, but you also need TVIF_IMAGE:
tvis.item.mask = TVIF_TEXT | TVIF_IMAGE;
tvis.item.iImage = -1;
Here is the effect of this change compared you your snippet (source code):

Try setting the image flag to -1 rather than 0;

Related

Partially transparent window OpenGL/Win32

I have read every existing question on this tricky (niche) subject, but I am stuck. I have a Win32 window with an OpenGL context. I want my window to be partially transparent.
My result so far is that the entirety of the window is transparent. I want only the black area to be transparent, so that I can draw some 3D objects and they will look like they are coming of the window.
First, in my window class, I have set hbrBackground to black.
Windows::WindowClass windowClass;
windowClass.cbSize = sizeof(Windows::WindowClass);
windowClass.style = Windows::CS_VREDRAW | Windows::CS_HREDRAW;
windowClass.lpfnWndProc = WndProc;
windowClass.cbClsExtra = 0;
windowClass.cbWndExtra = 0;
windowClass.hInstance = moduleHandle;
windowClass.hIcon = Windows::LoadIcon(0u, (X*)Windows::IDI_QUESTION);
windowClass.hCursor = Windows::LoadCursor(0u, (X*)Windows::IDC_ARROW);
windowClass.hbrBackground = Windows::CreateSolidBrush(0x00000000);
windowClass.lpszMenuName = nullptr;
windowClass.lpszClassName = (X*)name;
windowClass.hIconSm = Windows::LoadIcon(0u, (X*)Windows::IDI_QUESTION);
I have created my window with the WS_EX_LAYERED flag.
windowHandle = Windows::CreateWindow(Windows::WS_EX_LAYERED, (X*)name, "", Windows::WS_POPUP, w / 4, h / 4, w / 2, h / 2, 0u, 0u, moduleHandle, 0u);
In my pixel format, I have enabled alpha and composition.
PixelFormatDescriptor format;
format.nSize = sizeof(PixelFormatDescriptor);
format.nVersion = 1;
format.dwFlags = PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW | PFD_DOUBLEBUFFER | PFD_SUPPORT_COMPOSITION;
format.iPixelType = PFD_TYPE_RGBA;
format.cColorBits = 32;
format.cRedBits = 0;
format.cRedShift = 0;
format.cGreenBits = 0;
format.cGreenShift = 0;
format.cBlueBits = 0;
format.cBlueShift = 0;
format.cAlphaBits = 8;
format.cAlphaShift = 0;
format.cAccumBits = 0;
format.cAccumRedBits = 0;
format.cAccumGreenBits = 0;
format.cAccumBlueBits = 0;
format.cAccumAlphaBits = 0;
format.cDepthBits = 24;
format.cStencilBits = 8;
format.cAuxBuffers = 0;
format.iLayerType = PFD_MAIN_PLANE;
format.bReserved = 0;
format.dwLayerMask = 0;
format.dwVisibleMask = 0;
format.dwDamageMask = 0;
I have tried the blur region "trick", but it has no effect. My result is not related to this piece of code.
struct DwmBlurBehind
{
U4 dwFlags;
S4 fEnable;
X* blurRegionHandle;
S4 fTransitionOnMaximized;
};
DwmBlurBehind blurBehind;
blurBehind.dwFlags = Windows::DWM_BB_ENABLE | Windows::DWM_BB_BLURREGION;
blurBehind.fEnable = true;
blurBehind.blurRegionHandle = Windows::CreateRectRgn(0, 0, -1, -1);
blurBehind.fTransitionOnMaximized = false;
Windows::DwmEnableBlurBehindWindow(windowHandle, &blurBehind);
Finally, I have set the LWA_COLORKEY and LWA_ALPHA attributes. This is what gave me the effect displayed. However, the color key does not seem to be taken into account (I have tried non-zero values as well).
Windows::SetLayeredWindowAttributes(windowHandle, 0, 170, Windows::LWA_COLORKEY | Windows::LWA_ALPHA);
I did not forget to enable blending.
GL::Enable(GL::BLEND);
GL::BlendFunc(GL::SRC_ALPHA, GL::ONE_MINUS_SRC_ALPHA);
What you want to do requires window compositing which has been around since Windows Vista, so essentially every version of Windows you have to care about (Windows XP and earlier are at End of Life).
The key steps to take is, to enable DWM intra window compositing by enabling "Blur Behind Window" and use a WM_POPUP window; if you do not use WM_POPUP style the window manager will draw decorations and your OpenGL rendering will "hover" above that.
DWM_BLURBEHIND bb = {0};
bb.dwFlags = DWM_BB_ENABLE;
bb.fEnable = TRUE;
bb.hRgnBlur = NULL;
DwmEnableBlurBehindWindow(hWnd, &bb);
MARGINS margins = {-1};
impl_DwmExtendFrameIntoClientArea(hWnd, &margins);
Next, you must create an OpenGL context using the newer "attribute" API instead of using the pixelformat descriptor selection. With the attribute API you can select a transparent with alpha window format.
int attribs[] = {
WGL_DRAW_TO_WINDOW_ARB, TRUE,
WGL_DOUBLE_BUFFER_ARB, TRUE,
WGL_SUPPORT_OPENGL_ARB, TRUE,
WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB,
WGL_TRANSPARENT_ARB, TRUE,
WGL_COLOR_BITS_ARB, 32,
WGL_RED_BITS_ARB, 8,
WGL_GREEN_BITS_ARB, 8,
WGL_BLUE_BITS_ARB, 8,
WGL_ALPHA_BITS_ARB, 8,
WGL_DEPTH_BITS_ARB, 24,
WGL_STENCIL_BITS_ARB, 8,
0, 0
};
INT iPF;
UINT num_formats_choosen;
if( !wglChoosePixelFormatARB(
hDC,
attribs,
NULL,
1,
&iPF,
&num_formats_choosen) ) {
fprintf(stderr, "error choosing proper pixel format\n");
return NULL;
}
if( !num_formats_choosen ) {
return NULL;
}
PIXELFORMATDESCRIPTOR pfd;
memset(&pfd, 0, sizeof(pfd));
/* now this is a kludge; we need to pass something in the PIXELFORMATDESCRIPTOR
* to SetPixelFormat; it will be ignored, mostly. OTOH we want to send something
* sane, we're nice people after all - it doesn't hurt if this fails. */
DescribePixelFormat(hDC, iPF, sizeof(pfd), &pfd);
if( !SetPixelFormat(hDC, iPF, &pfd) ) {
fprintf(stderr, "error setting proper pixel format\n");
ReleaseDC(hWnd, hDC);
DestroyWindow(hWnd);
return NULL;
}
I've got a complete working example for this in my wglarb wrapper repository over at GitHub:
https://github.com/datenwolf/wglarb/blob/master/test/layered.c
You might be missing the call to UpdateLayeredWindow . IIRC, not calling UpdateLayeredWindow results in some weird behavior. YMMV.
More details here
According with SetLayeredWindowAttributes doc you need to pass
a COLORREF structure that specifies the transparency color key to be
used when composing the layered window. All pixels painted by the
window in this color will be transparent. To generate a COLORREF, use
the RGB macro.
Review the second parameter of your call to this function.

C++ win32 ListView columns

I have a problem with my code.
First,the essence of this code is to create a ListView with some columns, using win32.
The problem is when I try to add columns to my ListView and try to display it doesn't show me the columns. Here is my code, thanks for any help.
HWND function::CreateListView (HWND hwndParent)
{
INITCOMMONCONTROLSEX icex; // Structure for control initialization.
icex.dwICC = ICC_LISTVIEW_CLASSES;
InitCommonControlsEx(&icex);
RECT rcClient;
// The parent window's client area.
GetClientRect (hwndParent, &rcClient);
HWND hWndListView = CreateWindow(WC_LISTVIEW, "ViewList",WS_BORDER| WS_CHILD | LVS_REPORT | LVS_EDITLABELS,500,300,300,300,hwndParent,NULL,hInst,NULL);
return (hWndListView);
}
VOID function::SetView(HWND hWndListView, DWORD dwView)
{
// Retrieve the current window style.
DWORD dwStyle = GetWindowLong(hWndListView, GWL_STYLE);
// Set the window style only if the view bits changed.
if ((dwStyle & LVS_TYPEMASK) != dwView)
{
SetWindowLong(hWndListView,
GWL_STYLE,
(dwStyle & ~LVS_TYPEMASK) | dwView);
} // Logical OR'ing of dwView with the result of
}
BOOL InitListViewColumns(HWND hWndListView)
{
char szText[256] ="test"; // Temporary buffer.
LVCOLUMN lvc;
int iCol;
// Initialize the LVCOLUMN structure.
// The mask specifies that the format, width, text,
// and subitem members of the structure are valid.
lvc.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM|LVS_REPORT;
// Add the columns.
for (iCol = 0; iCol < C_COLUMNS; iCol++)
{
lvc.iSubItem = iCol;
lvc.pszText = "LOL";
lvc.cx = 100; // Width of column in pixels.
if ( iCol < 2 )
lvc.fmt = LVCFMT_LEFT; // Left-aligned column.
else
lvc.fmt = LVCFMT_RIGHT; // Right-aligned column.
// Load the names of the column headings from the string resources.
LoadString(hInst,iCol,szText, sizeof(szText)/sizeof(szText[0]));
// Insert the columns into the list view.
if (ListView_InsertColumn(hWndListView, iCol, &lvc) == -1)
return FALSE;
}
return TRUE;
}
You forgot to specify the WS_VISIBLE style when calling CreateWindow to create the listview. The listview and the columns are there, just not visible.
The buffer you pass to LoadString is never used because you never set lvc.pszText = szText so all the columns are named "LOL".
Edit: My answer applies to the code after it had been edited with fixes from the comments. LVS_REPORT is still not a valid LVCF_* flag but because it has the same value as LVCF_FMT it does no harm in this particular code but should still be removed because the code is technically incorrect.

When using DialogBoxIndirect, how do I get text the user entered when the dialog closes?

I'm using DialogBoxIndirect() to create a modal dialog in memory. One of the controls that I'm adding to the dialog has the EDIT class, so users can type in information in the dialog. When the dialog is closed, how do I figure out what the user typed into the EDIT field? I don't have an HWND for the EDIT field or the dialog itself, all I have is the id. The only way I know of is GetWindowText(), but that requires an HWND.
Code snippet:
//-----------------------
// Define Edit Input
//-----------------------
lpw = lpwAlign(lpw); // Align DLGITEMTEMPLATE on DWORD boundary
lpdit = (LPDLGITEMTEMPLATE)lpw;
lpdit->x = 10; lpdit->y = 10;
lpdit->cx = 150; lpdit->cy = 25;
lpdit->id = ID_TEXT2; // Text input
lpdit->dwExtendedStyle = WS_EX_CLIENTEDGE;
lpdit->style = WS_CHILD | WS_VISIBLE;
lpw = (LPWORD)(lpdit + 1);
*lpw++ = 0xFFFF;
*lpw++ = 0x0081; // Edit class
lpwsz = (LPWSTR)lpw;
nchar = MultiByteToWideChar(CP_ACP, 0, lpszMessage, -1, lpwsz, 50);
lpw += nchar;
*lpw++ = 0; // No creation data
//-----------------------
// Define an OK button.
//-----------------------
lpw = lpwAlign(lpw); // Align DLGITEMTEMPLATE on DWORD boundary
lpdit = (LPDLGITEMTEMPLATE)lpw;
lpdit->x = 10; lpdit->y = 40;
lpdit->cx = 35; lpdit->cy = 13;
lpdit->id = IDOK; // OK button identifier
lpdit->style = WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON;
lpw = (LPWORD)(lpdit + 1);
*lpw++ = 0xFFFF;
*lpw++ = 0x0080; // Button class
lpwsz = (LPWSTR)lpw;
nchar = MultiByteToWideChar(CP_ACP, 0, "OK", -1, lpwsz, 50);
lpw += nchar;
*lpw++ = 0; // No creation data
GlobalUnlock(hgbl);
ret = DialogBoxIndirect(hinst, (LPDLGTEMPLATE)hgbl, GetFocus(), (DLGPROC)GenericDlgProc);
// How do I get the text here, that the user entered into control id ID_TEXT2?
You can use GetDlgItemText() to get text from an edit control using its ID; basically what this does is a GetDlgItem() followed by a GetWindowText() all in one useful function call.
However by the time your DialogBoxIndirect() call has returned it's too late to do this - the dialog is gone, and the controls along with it. You can't read a control's value once it's been destroyed.
The usual way to deal with this is to handle WM_DESTROY in your DialogProc, and save the control values there (alternatively, if you have an OK and a Cancel button, you might do this in the WM_COMMAND handler for IDOK instead).

TreeView add Shell Icons

I already created treeview where I can add some items. Basicly I want to tree-view all directories and files with icons associated to them.
So I have these functions:
Adding items to treeview
HTREEITEM AddItemToTree(HWND hwndTree, char *text, int nLevel)
{
TVINSERTSTRUCT tvins;
static HTREEITEM hPrev = (HTREEITEM)TVI_FIRST;
static HTREEITEM hRootItem = NULL;
static HTREEITEM hPrevLev2Item = NULL;
AddIconToTree(hwndTree, text); //////////// THIS IS THE FUNCTION BELOW...
tvi.mask = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIS_STATEIMAGEMASK;
tvi.iImage = 0;
tvi.iSelectedImage = 0;
tvi.pszText = GetFileNameFromPath(text);
tvins.hInsertAfter = hPrev;
tvins.item = tvi;
if(nLevel == 1)
{
tvins.hParent = TVI_ROOT;
}
else if(nLevel == 2)
{
tvins.hParent = hRootItem;
}
else
{
TVITEM tviSetup;
tviSetup.hItem = hPrev;
tviSetup.mask = TVIF_PARAM;
TVITEM * tviLocal = &tviSetup;
TreeView_GetItem(hwndTree, tviLocal);
if(nLevel > tviLocal->lParam)
{
tvins.hParent = hPrev;
}
else
{
HTREEITEM hPrevLocal = TreeView_GetParent(hwndTree, hPrev);
tviLocal->hItem = hPrevLocal;
TreeView_GetItem(hwndTree, tviLocal);
for(int i = nLevel; i <= tviLocal->lParam;)
{
HTREEITEM hPrevLocalTemp = TreeView_GetParent(hwndTree, hPrevLocal);
hPrevLocal = hPrevLocalTemp;
tviLocal->hItem = hPrevLocal;
TreeView_GetItem(hwndTree, tviLocal);
}
tviLocal->mask = TVIF_TEXT;
TreeView_GetItem(hwndTree, tviLocal);
tvins.hParent = hPrevLocal;
}
}
hPrev = (HTREEITEM)SendMessage(hwndTree, TVM_INSERTITEM, 0, (LPARAM)(LPTVINSERTSTRUCT)&tvins);
if(hPrev == 0)
{
return false;
}
if(nLevel == 1)
{
hRootItem = hPrev;
}
return hPrev;
}
ADDING ICONS TO TREEVIEW:
int AddIconToTree(HWND hwndTree, char *strPath)
{
SHFILEINFO sfi;
memset(&sfi, 0, sizeof(sfi));
SHGetFileInfo(strPath, FILE_ATTRIBUTE_NORMAL, &sfi, sizeof(sfi), SHGFI_ICON | SHGFI_USEFILEATTRIBUTES | SHGFI_SMALLICON);
int index = sfi.iIcon;
ICONINFO iconinfo;
GetIconInfo(sfi.hIcon, &iconinfo);
HBITMAP hBitmap = iconinfo.hbmColor;
DestroyIcon(sfi.hIcon);
himg = ImageList_Create(16, 16, ILC_COLOR32, 1, 1);
int iImageList = ImageList_Add(himg, hBitmap, NULL);
DeleteObject(hBitmap);
//TreeView_SetImageList(hwndTree, himg, TVSIL_NORMAL);
SendMessage(hwndTree, TVM_SETIMAGELIST, (WPARAM)TVSIL_NORMAL, (LPARAM)(HIMAGELIST)himg);
MessageBox(hwnd, strPath, "Path:", MB_OK); /* Because of this msgbox I found out what is
really happening, because without it everything I have seen when I run the program was
treeview with icon of the last file, which was folder...So blank icon.*/
return index;
}
My main problem is, that when I'm setting some icon, it is set not only for the one item as I'm expecting, but on all items in treeview. So basicly every item's icon is overwritten by new item's icon. By the way I know that if I want to get icon of folder, I need to use FILE_ATTRIBUTE_DIRECTORY...
So that's it.
Any help would be greatly appriciated!
Thank You in advance :-)
In your AddIconToTree function you're creating a brand new image list every time, which will only ever have one icon in it. You need to maintain the same image list and add additional icons to it rather than re-creating it for every item.
Alternatively, you can get a handle to the shell imagelist with the SHGetImageList function and then assign it to the tree directly. If you don't need to add any of your own images to the tree's image list this is a much easier way of displaying system icons for files and folders as the shell imagelist handles all that for you.
// To initialise the tree's image list - do this one time only
HIMAGELIST himg;
if (SUCCEEDED(SHGetImageList(SHIL_SMALL, IID_IImageList, reinterpret_cast<void**>(&himg))))
SendMessage(hwndTree, TVM_SETIMAGELIST, (WPARAM)TVSIL_NORMAL, (LPARAM)himg);
Then your AddIconToTree function simply becomes:
int AddIconToTree(HWND hwndTree, char *strPath)
{
SHFILEINFO sfi;
memset(&sfi, 0, sizeof(sfi));
// SHGFI_SYSICONINDEX will return the icon's index within the shell image list
SHGetFileInfo(strPath, FILE_ATTRIBUTE_NORMAL, &sfi, sizeof(sfi),
SHGFI_SYSICONINDEX | SHGFI_USEFILEATTRIBUTES);
return sfi.iIcon;
}
And when you actually add items to the list, make sure you assign the index to the item:
tvi.mask = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIS_STATEIMAGEMASK;
tvi.iImage = tvi.iSelectedImage = AddIconToTree(hwndTree, text);

c++ Virtual ListView in Tile view, can't get subitems to appear

I have a straight win32 c++ app and I'm filling the window with a ListView whose view type is set to LV_VIEW_TILE and I'm also setting the style to LVS_OWNERDATA.
I'm having trouble trying to work out how to get the subitems to show. This code creates the view.
DWORD exstyle =WS_EX_CLIENTEDGE|LVS_EX_DOUBLEBUFFER|LVS_EX_JUSTIFYCOLUMNS|LVS_EX_INFOTIP;
g_hwndList = CreateWindowEx(exstyle, WC_LISTVIEW, NULL, WS_CHILD | WS_VISIBLE | LVS_ICON | LVS_OWNERDATA, 0, 0, 0, 0, hWnd, (HMENU) 2702, hInst, NULL);
ListView_SetView(g_hwndList, LV_VIEW_TILE);
LVTILEVIEWINFO tileViewInfo = { };
tileViewInfo.cbSize = sizeof(LVTILEVIEWINFO);
tileViewInfo.dwFlags = LVTVIF_AUTOSIZE;
tileViewInfo.dwMask = LVTVIM_COLUMNS;
tileViewInfo.cLines = 1;
BOOL tst = ListView_SetTileViewInfo(g_hwndList, &tileViewInfo);
I only want one more subitem/column to appear. In my LVN_GETDISPINFO I currently have this:
static int colfmt[1];
colfmt[0] = LVCFMT_LEFT;
static int order[1];
order[0] = 1;
if ((nimfo->item.mask & LVIF_COLUMNS) == LVIF_COLUMNS) {
nimfo->item.cColumns = 1;
nimfo->item.piColFmt = PINT(colfmt);
nimfo->item.puColumns = PUINT(order);
}
if ((nimfo->item.mask & LVIF_TEXT) == LVIF_TEXT) {
nimfo->item.pszText = di->LABEL;
}
if ((nimfo->item.mask & LVIF_IMAGE) == LVIF_IMAGE) {
nimfo->item.iImage = di->IMAGE_INDEX;
}
I can't work out at what point and where I need to supply the subitem/column text, I'm never seeing the nimfo->item.subitem changing from 0 and for each call for LVIF_TEXT the structure values are always the same.
So at what point do I need to supply the extra textual data?
Many thanks.
I, rather stupidly, wasn't adding any columns and therefore wasn't being asked for the other items.
added this and everything works
LVCOLUMN col = {};
col.mask = LVCF_SUBITEM;
col.iSubItem = 0;
ListView_InsertColumn(g_hwndList, 0, &col);