PRB: Windows Forms TreeView Control Does Not Handle State Image Lists in Visual C# .NET (313134)



The information in this article applies to:

  • Microsoft .NET Framework 1.1
  • Microsoft .NET Framework 1.0
  • Microsoft Visual C# .NET (2003)
  • Microsoft Visual C# .NET (2002)

This article was previously published under Q313134
This article references the following .NET Framework Class Library namespaces:
  • System.Windows.Forms
  • System.Runtime.InteropServices

SYMPTOMS

The Windows Forms TreeView control displays a hierarchy of nodes similar to the way that Windows Explorer displays folders in the left pane. With the Windows Forms TreeView control, the application cannot use a State image list to display a State icon. The State icon appears to the left of the icon that is displayed for a node in the control. This behavior differs from the behavior of the underlying Win32 TreeView control.

RESOLUTION

The .NET Windows Forms application can use Interop services to direct the underlying Win32 TreeView control to display a State icon. To do this, the application can assign a value to each field in the TVITEM structure and send a TVM_SETITEM message to the underlying Win32 TreeView control through Interop services.

MORE INFORMATION

A Win32 TreeView control has two image lists that are associated with it:
  • Normal image list, which contains selected, nonselected, and overlay images for the items of a TreeView control.
  • State image list, which you can use to indicate application-defined item states. A State image is displayed to the left of an item's selected or nonselected image.

Because the Windows Forms TreeView control does not handle the State image list, follow these steps:
  1. Use Interop services to send a TVM_SETIMAGELIST message with the TVSIL_STATE flag to the underlying Win32 control to set the State image list.
  2. Send a TVM_SETITEM message to tell the control on which node to display a specific image from the image list. You must define the Win32 API function SendMessage to send these messages.
Because the TVM_SETIMAGELIST message and the TVM_SETITEM message require different parameters when they are sent to a TreeView control, you must overload the SendMessage function (as the following code shows).

This code demonstrates how to add a State image list to a Windows Forms TreeView control and how to display an image from the control on the root node.

Step-by-Step Example

  1. Start Microsoft Visual Studio .NET, and then create a new Windows Application in Visual C# .NET. Form1 is added to the project by default.
  2. On Form1, add a TreeView, a Button, and an ImageList control. Their default names are TreeView1, Button1 and ImageList1 respectively.
  3. In the properties of ImageList1, click the ellipsis (...) button of the Images property, and then add at least two images. This is required for the sample to work.
  4. In the code window of Form1.cs, add the following statement after the other Using statements:
    using System.Runtime.InteropServices;
    					
  5. Copy and paste the following code in Form1 class after the Private statements:
    public const UInt32 TV_FIRST = 4352;
    public const UInt32 TVSIL_NORMAL = 0;
    public const UInt32 TVSIL_STATE   = 2;
    public const UInt32 TVM_SETIMAGELIST = TV_FIRST + 9;
    public const UInt32 TVM_GETNEXTITEM = TV_FIRST + 10;
    public const UInt32 TVIF_HANDLE = 16;
    public const UInt32 TVIF_STATE = 8;
    public const UInt32 TVIS_STATEIMAGEMASK = 61440;
    public const UInt32 TVM_SETITEM = TV_FIRST + 13;
    public const UInt32 TVGN_ROOT = 0;
    		
    // Use a sequential structure layout to define TVITEM for the TreeView.
    [StructLayout(LayoutKind.Sequential, Pack=8, CharSet=CharSet.Auto)]
    public struct TVITEM 
    {
    	public uint     mask;
    	public IntPtr   hItem;
    	public uint     state;
    	public uint     stateMask;
    	public IntPtr   pszText;
    	public int      cchTextMax;
    	public int      iImage;
    	public int      iSelectedImage;
    	public int      cChildren;
    	public IntPtr   lParam;
    }
    		
    // Declare two overloaded SendMessage functions. The
    // difference is in the last parameter: one is ByVal and the
    // other is ByRef.
    [DllImport("user32.dll")]
    public static extern UInt32 SendMessage( IntPtr hWnd, UInt32 Msg, 
    	UInt32 wParam, UInt32 lParam);
    
    [DllImport("User32", CharSet=CharSet.Auto)]
    public static extern IntPtr SendMessage(IntPtr hWnd, UInt32 msg, 
    	UInt32 wParam, ref TVITEM lParam);
    						
  6. In the Form1_Load event, copy and paste the following code:
    treeView1.Nodes.Add ("Node1");
    treeView1.Nodes.Add ("Node2");
    						
  7. In the Button1_Click event, copy and paste the following code:
    IntPtr hWnd;
    IntPtr hRoot;
    TVITEM tvi;
    hWnd = treeView1.Handle;
          
    
    // Send a TVM_SETIMAGELIST with TVSIL_STATE.
    SendMessage(hWnd,(UInt32)TVM_SETIMAGELIST,(UInt32)TVSIL_STATE,(UInt32)imageList1.Handle);
    			
    // Get a handle to the top row.
    hRoot = (IntPtr)SendMessage(hWnd,(UInt32)TVM_GETNEXTITEM,(UInt32)TVGN_ROOT ,(UInt32)0);
    
    // The following uses the TVM_SETITEM message to set the State 
    // of a given item. It uses the TVITEM structure.
    
    //  tvi.mask: include TVIF_HANDLE and TVIF_STATE
    tvi.mask = TVIF_HANDLE | TVIF_STATE;
    
    // To use the State image, tvi.State cannot be 0.  
    //Setting it to 1 means to use the second image in the image list.
    tvi.State = (uint)1; 
    // Left shift 12 to put info in bits 12 to 15
    tvi.State = tvi.State << 12; 
    // Set StateMask. -This is required to isolate State above.
    tvi.StateMask = TVIS_STATEIMAGEMASK;  
    
    // Define the item we want to set the State in.
    tvi.hItem = hRoot;  //For example, try the root.
    
    //  Initialize the rest to zero.
    tvi.pszText = (IntPtr)0;
    tvi.cchTextMax = 0;
    tvi.iImage = 0;
    tvi.iSelectedImage = 0;
    tvi.cChildren = 0;
    tvi.lParam = (IntPtr)0;
    
    // Send the TVM_SETITEM message.
    //  TVM_SETITEM = 4365
    SendMessage(hWnd,(UInt32)TVM_SETITEM,(UInt32)0,ref tvi);
    						
  8. Press the F5 key to run the application. Click Button1, and notice that the State image is added to the root node.

Modification Type:MajorLast Reviewed:3/24/2004
Keywords:kbGDI kbprb kbWindowsForms KB313134