EDN Admin
Well-known member
Hello Folks:
Developing 32 bit app on Win 7 Ultra 64, VS 2010 Pro, C++, Win32, no MFC.
Ive had a miserable weekend, and Monday wasnt good either. Perhaps, with your help, Tuesday will be better.
I have an application that needs a TreeView. I used the Resource Editor to lay out the dialog, and specify the style items for the TreeView.
Each item of this TreeView needs to use CustomDraw to properly position its text.
My app is well over 100K lines. The only positive thing thats happened in the last 3 days is that Ive been able to duplicate my numerous problems in a 155 line program.
The TreeView uses buttons, those plus signs and minus signs inside the squares, and Id like it to use lines.
Problem 1:
TreeView wont let me select on an item if its drawn with CustomDraw.
Apparently, defining text for a TreeView item doesnt impose a width on that item. If I create some text for the item when I create it, like this:
<pre> insert_struct.item.mask |= TVIF_TEXT;
insert_struct.item.pszText = "Test item";
[/code]
The TreeView item now has a width, and I can click on the text to select it. Adding text with CustomDraw doesnt seem to have a width, so there is nothing to click on.
How do I give an item a width for clicking?
If I specify "Full Row Select" for the TreeView, nothing I draw with CustomDraw is displayed. I see only a column of plus signs in squares on the left hand side.
Im not experienced in CustomDraw, so perhaps Im making a mistake. It seems odd that removing full width select causes the CustomDraw items to be displayed.
Workaround 1:
Turn off the full row select.
Subclass the TreeView. If the left button is pressed when the cursor is to the right of what Im guessing is the start of the text area, I post a message to the dialog proc with the TreeView items handle. The dialog proc can then
set the select flag on the item.
In the interest of keeping this sample small, I havent included the subclass code. But I believe that all select related problems go away if I can get the TreeView to recognize the width of the CustomDraw text.
Problem 2:
And this is the killer, I havent found a workaround and I cant let customers see this.
When starting the dialog, when the TreeView first gets focus, no item is selected. If I expand any item, the item at the first line of the TreeView becomes selected, and is highlighted. This only happens once.
If I start the dialog and select items, I find that the TreeView doesnt de-select everything else. It seems to allow any number of items to be selected. My app only wants one item selected, so I have code that walks through the
TreeView and de-selects everything before selecting the TreeView item the subclass found under the cursor.
This works fine until I first expand on an item. Then the first item in the TreeView becomes selected. The item is hilighted, and now I have two items selected. The one I want to select, and the first item in the list.
As stated, the sample doesnt have the subclass code to show this.
Problem 3:
As you can see, the CustomDraw draws text under the TreeViews plus sign in the little square. How do I determine the left most edge of the text area for any item, at any level, of the treeview?
You can see the text fail to print by using the Resource Editor to set "Full Row Select" to true
Here is the source:
<
<pre>#include <windows.h>
#include <commctrl.h>
#include "resource.h"
INT_PTR CALLBACK treeview_select_problem_dialog_box(HWND hdlg,
UINT message,
WPARAM wParam,
LPARAM lParam);
void test_fill_tree(HWND treeview_control_handle);
INT_PTR process_treeview_customdraw(NMTVCUSTOMDRAW *tv_customdraw_ptr);
int WINAPI WinMain (HINSTANCE hinstance, HINSTANCE hPrevInstance,
PSTR szCmdLine, int iCmdShow)
{
DialogBox(hinstance,
"TREEVIEW_FULL_VIEW_SELECT_PROBLEM_DIALOG", NULL,
(DLGPROC)treeview_select_problem_dialog_box);
}
INT_PTR CALLBACK treeview_select_problem_dialog_box(HWND hdlg,
UINT message,
WPARAM wParam,
LPARAM lParam)
{
INT_PTR return_val = 0;
HWND treeview_control_handle = NULL;
LONG_PTR dialog_return_value = 0;
NMHDR *notification_hdr_ptr = NULL;
switch (message)
{
case WM_INITDIALOG:
treeview_control_handle = GetDlgItem(hdlg, IDC_TREE_CONTROL);
test_fill_tree(treeview_control_handle);
return FALSE;
case WM_NOTIFY:
dialog_return_value = 0;
notification_hdr_ptr = (NMHDR *)lParam;
// From WM_NOTIFY documentation:
// An application should use the hwndFrom or idFrom member of the NMHDR
// structure (passed as the lParam parameter) to identify the control.
switch(notification_hdr_ptr->idFrom)
{
case IDC_TREE_CONTROL:
switch(notification_hdr_ptr->code)
{
case NM_CUSTOMDRAW:
dialog_return_value = process_treeview_customdraw(
(NMTVCUSTOMDRAW *)notification_hdr_ptr);
break;
}
SetWindowLongPtr(hdlg, DWL_MSGRESULT, dialog_return_value);
return (INT_PTR)dialog_return_value;
}
// In general, return value for WM_NOTIFY is ignored.
return TRUE;
case WM_COMMAND:
switch(LOWORD (wParam))
{
case IDOK:
case IDCANCEL: // Needed for upper right "X" to function.
switch(HIWORD(wParam))
{
case BN_CLICKED:
SetLastError(0);
EndDialog(hdlg, true);
break;
}
return 0;
}
}
return 0;
}
void test_fill_tree(HWND treeview_control_handle)
{
TV_INSERTSTRUCT insert_struct; // struct to config out tree control
int item_count = 0;
HTREEITEM treeview_item_handle = NULL; // Tree item handle
memset (&insert_struct, 0x00, sizeof insert_struct);
while(item_count < 10)
{
insert_struct.hParent=NULL; // top most level need no handle
insert_struct.hInsertAfter=TVI_LAST; // work as root level
insert_struct.item.mask = TVIF_PARAM | TVIF_CHILDREN;
// This will have children
insert_struct.item.cChildren = 1; // One of more children.
#if 0
insert_struct.item.mask |= TVIF_TEXT;
insert_struct.item.pszText = "Test item";
#endif
treeview_item_handle =
(HTREEITEM) SendMessage(treeview_control_handle, TVM_INSERTITEM, 0,
LPARAM(&insert_struct));
++item_count;
}
}
INT_PTR process_treeview_customdraw(NMTVCUSTOMDRAW *tv_customdraw_ptr)
{
NMCUSTOMDRAW *customdraw_ptr = &tv_customdraw_ptr->nmcd;
HTREEITEM treeview_item_handle = (HTREEITEM)customdraw_ptr->dwItemSpec;
INT_PTR return_val = 0;
const char *test_text = "Hello World";
switch(customdraw_ptr->dwDrawStage)
{
case CDDS_PREPAINT:
// Before the painting cycle begins.
// This will be the first notification.
// Return this so
// "The control notifies the parent of any item-related drawing operations."
// return_val = CDRF_NOTIFYITEMDRAW;
return_val = CDRF_NOTIFYITEMDRAW;
break;
case CDDS_ITEMPREPAINT:
TextOut(customdraw_ptr->hdc,
customdraw_ptr->rc.left,
customdraw_ptr->rc.top,
test_text,
strlen(test_text));
return_val = CDRF_SKIPPOSTPAINT;
}
return return_val;
}
[/code]
<
<br/>
Here is the .rc file:
<
<pre>// Microsoft Visual C++ generated resource script.
//
#include "resource.h"
#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "afxres.h"
/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
// English (United States) resources
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//
1 TEXTINCLUDE
BEGIN
"resource.h "
END
2 TEXTINCLUDE
BEGIN
"#include ""afxres.h""rn"
"
Developing 32 bit app on Win 7 Ultra 64, VS 2010 Pro, C++, Win32, no MFC.
Ive had a miserable weekend, and Monday wasnt good either. Perhaps, with your help, Tuesday will be better.
I have an application that needs a TreeView. I used the Resource Editor to lay out the dialog, and specify the style items for the TreeView.
Each item of this TreeView needs to use CustomDraw to properly position its text.
My app is well over 100K lines. The only positive thing thats happened in the last 3 days is that Ive been able to duplicate my numerous problems in a 155 line program.
The TreeView uses buttons, those plus signs and minus signs inside the squares, and Id like it to use lines.
Problem 1:
TreeView wont let me select on an item if its drawn with CustomDraw.
Apparently, defining text for a TreeView item doesnt impose a width on that item. If I create some text for the item when I create it, like this:
<pre> insert_struct.item.mask |= TVIF_TEXT;
insert_struct.item.pszText = "Test item";
[/code]
The TreeView item now has a width, and I can click on the text to select it. Adding text with CustomDraw doesnt seem to have a width, so there is nothing to click on.
How do I give an item a width for clicking?
If I specify "Full Row Select" for the TreeView, nothing I draw with CustomDraw is displayed. I see only a column of plus signs in squares on the left hand side.
Im not experienced in CustomDraw, so perhaps Im making a mistake. It seems odd that removing full width select causes the CustomDraw items to be displayed.
Workaround 1:
Turn off the full row select.
Subclass the TreeView. If the left button is pressed when the cursor is to the right of what Im guessing is the start of the text area, I post a message to the dialog proc with the TreeView items handle. The dialog proc can then
set the select flag on the item.
In the interest of keeping this sample small, I havent included the subclass code. But I believe that all select related problems go away if I can get the TreeView to recognize the width of the CustomDraw text.
Problem 2:
And this is the killer, I havent found a workaround and I cant let customers see this.
When starting the dialog, when the TreeView first gets focus, no item is selected. If I expand any item, the item at the first line of the TreeView becomes selected, and is highlighted. This only happens once.
If I start the dialog and select items, I find that the TreeView doesnt de-select everything else. It seems to allow any number of items to be selected. My app only wants one item selected, so I have code that walks through the
TreeView and de-selects everything before selecting the TreeView item the subclass found under the cursor.
This works fine until I first expand on an item. Then the first item in the TreeView becomes selected. The item is hilighted, and now I have two items selected. The one I want to select, and the first item in the list.
As stated, the sample doesnt have the subclass code to show this.
Problem 3:
As you can see, the CustomDraw draws text under the TreeViews plus sign in the little square. How do I determine the left most edge of the text area for any item, at any level, of the treeview?
You can see the text fail to print by using the Resource Editor to set "Full Row Select" to true
Here is the source:
<
<pre>#include <windows.h>
#include <commctrl.h>
#include "resource.h"
INT_PTR CALLBACK treeview_select_problem_dialog_box(HWND hdlg,
UINT message,
WPARAM wParam,
LPARAM lParam);
void test_fill_tree(HWND treeview_control_handle);
INT_PTR process_treeview_customdraw(NMTVCUSTOMDRAW *tv_customdraw_ptr);
int WINAPI WinMain (HINSTANCE hinstance, HINSTANCE hPrevInstance,
PSTR szCmdLine, int iCmdShow)
{
DialogBox(hinstance,
"TREEVIEW_FULL_VIEW_SELECT_PROBLEM_DIALOG", NULL,
(DLGPROC)treeview_select_problem_dialog_box);
}
INT_PTR CALLBACK treeview_select_problem_dialog_box(HWND hdlg,
UINT message,
WPARAM wParam,
LPARAM lParam)
{
INT_PTR return_val = 0;
HWND treeview_control_handle = NULL;
LONG_PTR dialog_return_value = 0;
NMHDR *notification_hdr_ptr = NULL;
switch (message)
{
case WM_INITDIALOG:
treeview_control_handle = GetDlgItem(hdlg, IDC_TREE_CONTROL);
test_fill_tree(treeview_control_handle);
return FALSE;
case WM_NOTIFY:
dialog_return_value = 0;
notification_hdr_ptr = (NMHDR *)lParam;
// From WM_NOTIFY documentation:
// An application should use the hwndFrom or idFrom member of the NMHDR
// structure (passed as the lParam parameter) to identify the control.
switch(notification_hdr_ptr->idFrom)
{
case IDC_TREE_CONTROL:
switch(notification_hdr_ptr->code)
{
case NM_CUSTOMDRAW:
dialog_return_value = process_treeview_customdraw(
(NMTVCUSTOMDRAW *)notification_hdr_ptr);
break;
}
SetWindowLongPtr(hdlg, DWL_MSGRESULT, dialog_return_value);
return (INT_PTR)dialog_return_value;
}
// In general, return value for WM_NOTIFY is ignored.
return TRUE;
case WM_COMMAND:
switch(LOWORD (wParam))
{
case IDOK:
case IDCANCEL: // Needed for upper right "X" to function.
switch(HIWORD(wParam))
{
case BN_CLICKED:
SetLastError(0);
EndDialog(hdlg, true);
break;
}
return 0;
}
}
return 0;
}
void test_fill_tree(HWND treeview_control_handle)
{
TV_INSERTSTRUCT insert_struct; // struct to config out tree control
int item_count = 0;
HTREEITEM treeview_item_handle = NULL; // Tree item handle
memset (&insert_struct, 0x00, sizeof insert_struct);
while(item_count < 10)
{
insert_struct.hParent=NULL; // top most level need no handle
insert_struct.hInsertAfter=TVI_LAST; // work as root level
insert_struct.item.mask = TVIF_PARAM | TVIF_CHILDREN;
// This will have children
insert_struct.item.cChildren = 1; // One of more children.
#if 0
insert_struct.item.mask |= TVIF_TEXT;
insert_struct.item.pszText = "Test item";
#endif
treeview_item_handle =
(HTREEITEM) SendMessage(treeview_control_handle, TVM_INSERTITEM, 0,
LPARAM(&insert_struct));
++item_count;
}
}
INT_PTR process_treeview_customdraw(NMTVCUSTOMDRAW *tv_customdraw_ptr)
{
NMCUSTOMDRAW *customdraw_ptr = &tv_customdraw_ptr->nmcd;
HTREEITEM treeview_item_handle = (HTREEITEM)customdraw_ptr->dwItemSpec;
INT_PTR return_val = 0;
const char *test_text = "Hello World";
switch(customdraw_ptr->dwDrawStage)
{
case CDDS_PREPAINT:
// Before the painting cycle begins.
// This will be the first notification.
// Return this so
// "The control notifies the parent of any item-related drawing operations."
// return_val = CDRF_NOTIFYITEMDRAW;
return_val = CDRF_NOTIFYITEMDRAW;
break;
case CDDS_ITEMPREPAINT:
TextOut(customdraw_ptr->hdc,
customdraw_ptr->rc.left,
customdraw_ptr->rc.top,
test_text,
strlen(test_text));
return_val = CDRF_SKIPPOSTPAINT;
}
return return_val;
}
[/code]
<
<br/>
Here is the .rc file:
<
<pre>// Microsoft Visual C++ generated resource script.
//
#include "resource.h"
#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "afxres.h"
/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
// English (United States) resources
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//
1 TEXTINCLUDE
BEGIN
"resource.h "
END
2 TEXTINCLUDE
BEGIN
"#include ""afxres.h""rn"
"