PRB: Cannot Assign Images to a ColumnHeader Control in a Windows Forms ListView Control in Visual C# .NET (314933)



The information in this article applies to:

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

This article was previously published under Q314933
For a Microsoft Visual Basic .NET version of this article, see 316202.

This article refers to the following Microsoft .NET Framework Class Library namespaces:
  • System.Windows.Forms
  • System.Runtime.InteropServices

SYMPTOMS

When you attempt to assign images to a ColumnHeader control in a Windows Forms ListView control, the operation is unsuccessful.

CAUSE

This behavior occurs because the .NET implementation of the System.Windows.Forms.ColumnHeader class does not include the capability to assign an image to the ColumnHeader control. You can assign only text to a ColumnHeader control.

RESOLUTION

To work around this issue in a .NET Windows Forms application, use Interop Services to send messages to the underlying Win32 ListView control to direct it to add the images to specific columns. For further details, see the "More Information" section of this article.

MORE INFORMATION

A ColumnHeader control is an item in a Windows Forms ListView control that contains heading text. You can use the Add method of the ListView.ColumnHeaderCollection class to add ColumnHeader objects to a ListView control. Because this class does not accommodate images, a .NET Windows Forms application can use Interop Services to add the images. The application can send an LVM_GETHEADER message to the Windows Forms ListView control to obtain the ColumnHeader object. Then, the application can send an HDM_SETIMAGELIST message to the ColumnHeader control to set the image list that you want. Finally, the application can fill out an LVCOLUMN structure and send it to the ListView control through an LVM_SETCOLUMN message to add an image from the image list to one of the columns. (The following code, in steps 4 through 7, demonstrates this technique.)

To do this, follow these steps:
  1. Use Visual C# .NET to start a new Windows application.
  2. In Form1, add a ListView control, a Button control, and an ImageList control.

    The default names for these objects are listView1, button1, and imageList1, respectively.
  3. In the imageList1 Properties dialog box, click the ellipsis (...) next to the Images property to open the Images Collection Editor dialog box, and then add two images to the control.
  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 the Form1 window after the Private statements:
    public const UInt32 LVM_GETHEADER =  4127;
    public const UInt32 HDM_SETIMAGELIST = 4616;
    public const UInt32 LVM_SETCOLUMN = 4122;
    public const uint LVCF_FMT = 1;
    public const uint LVCF_IMAGE = 16;
    public const int LVCFMT_IMAGE = 2048;
    
    // Define the LVCOLUMN for use with interop
    [StructLayout(LayoutKind.Sequential, Pack=8, CharSet=CharSet.Auto)]
    public struct LVCOLUMN 
    {
    	public uint     mask;
    	public int      fmt;
    	public int      cx;
    	public IntPtr   pszText;
    	public int      cchTextMax;
    	public int      iSubItem;
    	public int      iImage;
    	public int      iOrder;
    }
    
    // Declare two overloaded SendMessage functions. The
    // difference is in the last parameter.
    [DllImport("user32.dll")]
    public static extern IntPtr 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 LVCOLUMN lParam);        
    					
  6. In the Form1_Load event, add the following code:
    listView1.View = View.Details;
    listView1.Columns.Add("Column 0", 60, HorizontalAlignment.Left);
    listView1.Columns.Add("Column 1", 60, HorizontalAlignment.Left);
    					
  7. In button1_Click event, add the following code:
    IntPtr hc;
    int i;
    // Assign the ImageList to the header control.
    // The header control includes all columns.
    // Get a handle to the header control.
    hc = SendMessage(listView1.Handle,LVM_GETHEADER,(UInt32)0,(UInt32)0);
    
    // Add the image list to the header control.
    SendMessage(hc,HDM_SETIMAGELIST,(UInt32)0,(UInt32)imageList1.Handle);
    
    // Set the image for each column.
    // 
    // In the following code, we use successive images in the image list to place on 
    // successive columns in the ColumnHeader by looping through all columns.
    // 
    //  The LVCOLUMN is also used to define alignment,
    //  so by using it here, we are resetting the alignment if it was defined
    //  in the designer. If you need to set the alignment, you will need to change
    //  the code below to set it here.
    // 
    for (i=0; i<listView1.Columns.Count; i++)
    {
    	// Use the LVM_SETCOLUMN message to set the column's image index. 
    	LVCOLUMN col;
    	//  col.mask: include LVCF_FMT | LVCF_IMAGE 
    	col.mask = LVCF_FMT | LVCF_IMAGE ;
    
    	// LVCFMT_IMAGE 
    	col.fmt = LVCFMT_IMAGE;
    
    	// The image to use from the image list.
    	col.iImage = i;
    
    	//  Initialize the rest to zero.
    	col.pszText = (IntPtr)0;
    	col.cchTextMax = 0;
    	col.cx = 0;
    	col.iSubItem = 0;
    	col.iOrder = 0;
    
    	// Send the LVM_SETCOLUMN message.
    	// The column that we are assigning the image to is defined in the third parameter.
             SendMessage(listView1.Handle,LVM_SETCOLUMN,(UInt32)i,ref col);
    }
    					
  8. Press F5 to run the application. Click button1 to verify that an image is added to each column header.

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