Click here to Skip to main content
Click here to Skip to main content

Locking ListView Column Size

, 20 Sep 2005
Rate this:
Please Sign up or sign in to vote.
A custom ListView that allows the column size to be locked.

Sample image

Introduction

It happens to every CPian at some point in time. They actually have to submit an article. For the first time in my .NET development career, I couldn't find an answer to my problem. So, here it is, my first article. It is a fairly simple control, but I found several other questions on this topic without an answer.

Background

The main purpose for this control is to make an easier to use system for non-technical people. The ListView seems like a good way of displaying information, without having to go with a full blown Grid control. The problem is there is no way to lock the size of the column headers. I can hear the support calls now, like "I can't see the totals anymore". Issues like this are difficult to troubleshoot and cause frustration on the user's part. Plus this application which I was developing would be touch screen driven, so it would be easy to move the columns, but somewhat difficult to get them back to the right spot.

So the search began for a C# solution. But nothing seemed to be available. I did find a C++ article on this topic by Paul DiLascia from the June 2003 MSDN Magazine - C++ Q & A article. Now I was armed with the basic knowledge, but this had to be translated to C#. Being new to .NET, I headed to CodeProject and found Georgi Atanasov's article on Customizing the Header Control in a ListView. This filled in the details needed to make the control function as I wanted.

Using the code

The control is quite simple once all of the details are available. First we will subclass the standard ListView control. The mouse tracking messages need to be captured and discarded to prevent sizing of the column headers.

public class myListView : System.Windows.Forms.ListView
{
    /// <summary>
    /// Capture messages for the list view control.
    /// </summary>
    protected override void WndProc( ref Message message )
    {
        const int WM_NOTIFY = 0x004E;
        const int HDN_FIRST = (0-300);
        const int HDN_BEGINTRACKA = (HDN_FIRST-6);
        const int HDN_BEGINTRACKW = (HDN_FIRST-26);
        bool callBase = true;

        switch ( message.Msg )
        {
        case WM_NOTIFY:
            NMHDR nmhdr = (NMHDR)message.GetLParam(typeof(NMHDR));
            switch(nmhdr.code)
            {
            case HDN_BEGINTRACKA:  //Process both ANSI and
            case HDN_BEGINTRACKW:  //UNICODE versions of the message.
                if(locked)
                {
                    //Discard the begin tracking to prevent dragging of the 
                    //column headers.
                    message.Result = (IntPtr)1;
                    callBase = false;
                } //if
                break ;
            } //switch
            break;
        } //switch

        if(callBase)
        {
            // pass messages on to the base control for processing
            base.WndProc( ref message ) ;
        } //if
    } //WndProc()
}

The begin tracking notification is part of the WM_NOTIFY message. Notice that both the ANSI and UNICODE versions of the message are captured.

This works great. The user can no longer size the column headers, but it doesn't look quite right. The cursor still changes to the sizing cursor on mouse over. To handle this, the messages going to the header of the ListView must be captured. When this is done, the WM_SETCURSOR can be discarded. Now the cursor does not change and everything looks correct.

/// <summary>
/// Class used to capture window messages for the header of the list view
/// control.
/// </summary>

private class HeaderControl : NativeWindow
{
    private myListView parentListView = null;

    [DllImport("User32.dll",CharSet = CharSet.Auto,SetLastError=true)]
    public static extern IntPtr SendMessage(IntPtr hWnd, int msg, 
                                   IntPtr wParam, IntPtr lParam);

    public HeaderControl(myListView m)
    {
        parentListView = m;
        //Get the header control handle
        IntPtr header = SendMessage(m.Handle, 
            (0x1000+31), IntPtr.Zero, IntPtr.Zero);
        this.AssignHandle(header);                
    } //constructor HeaderControl()

    protected override void WndProc(ref Message message)
    {
        const int WM_LBUTTONDBLCLK = 0x0203;
        const int WM_SETCURSOR = 0x0020;
        bool callBase = true;

        switch ( message.Msg )
        {
       case WM_SETCURSOR:
            if(parentListView.LockColumnSize)
            {
                //Don't change cursor to sizing cursor.
                message.Result = (IntPtr)1; //Return TRUE from message handler
                callBase = false;           //Don't call the base class.
             } //if
             break;
        } //switch

        if(callBase)
        {
            // pass messages on to the base control for processing
            base.WndProc( ref message ) ;
        } //if
    } //WndProc()
} //class HeaderControl

The control is looking good now. However, there is still a side effect. The user can double click on the header and the column auto-sizes. This turns out to be easy, we just capture and discard the double click for the header. Here is the updated WndProc() to correct this problem.

/// <summary>

/// Class used to capture window messages for the header of the list view
/// control.
/// </summary>
private class HeaderControl : NativeWindow
{
    //...
    
    protected override void WndProc(ref Message message)
    {
        const int WM_LBUTTONDBLCLK = 0x0203;
        const int WM_SETCURSOR = 0x0020;
        bool callBase = true;

        switch ( message.Msg )
        {
        case WM_LBUTTONDBLCLK:                    
        case WM_SETCURSOR:
            if(parentListView.LockColumnSize)
            {
                //Don't change cursor to sizing cursor. Also ignore
                //double click, which sizes the column to fit the data.
                message.Result = (IntPtr)1;    //Return TRUE from message handler
                callBase = false;        //Don't call the base class.
             } //if
             break;
        } //switch

        if(callBase)
        {
            // pass messages on to the base control for processing
            base.WndProc( ref message ) ;
        } //if
    } //WndProc()
} //class HeaderControl

Thanks to the team on Code Project, yet another side effect was discovered. Geert Baeyaert pointed out that CTRL+ was not being handled. The ListView resizes all columns on CTRL+. So after a bit of digging, I found how to capture and discard the CTRL+ key stroke. Here is the bit of code to handle CTRL+:

/// <summary>
/// Capture CTRL+ to prevent resize of all columns.
/// </summary>
protected override void OnKeyDown(KeyEventArgs e)
{
    if(e.KeyValue==107 && e.Modifiers==Keys.Control && locked)
    {
        e.Handled = true;
    }
    else
    {
        base.OnKeyDown (e);
    } //if
} //OnKeyDown()

The control now functions as desired. The option to enable and disable the size of the columns is wrapped in a property called "LockColumnSize", which is shown under the properties in the Behavior section. The property is active at design time, so the column width will need to be set before locking the columns.

Points of Interest

Programming .NET is a whole new way of thinking. My background is programming C for embedded systems. Sometimes the embedded world is easier. You can look at the schematics and datasheets and they clearly tell you what is possible and how to do it. In .NET, there is an ocean of information and it is somewhat difficult to find the solution you are looking for.

I hope you find the control useful and I look forward to hearing comments. I need all the help I can get in learning how to do .NET code properly.

History

  • 2005-09-20: Modified to capture CTRL+ to prevent resizing of all columns.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

Share

About the Author

Chris Morgan
Web Developer
United States United States
No Biography provided

Comments and Discussions

 
GeneralGreat article / control, but seems to be x86 only PinmemberWalter Wittel5-Oct-11 7:01 
GeneralAwesome control PinmemberVivek_Minhas31-Mar-09 0:10 
GeneralC++ WTL style PinmemberT800G26-Nov-08 11:51 
QuestionItem Selection PinmemberMember 373617713-May-08 3:35 
GeneralMuch simpler way Pinmemberspintz15-Nov-07 5:10 
GeneralRe: Much simpler way Pinmemberleosco27-Nov-11 8:05 
Generallast item removal Pinmembernumbers1thru928-Dec-06 8:24 
GeneralRe: last item removal PinmemberChris Morgan15-Jan-07 8:48 
GeneralRe: last item removal Pinmembernumbers1thru915-Jan-07 12:25 
GeneralI have translated into VB.NET, but i met a problem. PinmemberLukecao14-Sep-06 1:01 
GeneralRe: I have translated into VB.NET, but i met a problem. PinmemberChris Morgan14-Sep-06 5:28 
GeneralRe: I have translated into VB.NET, but i met a problem. PinmemberLukecao14-Sep-06 23:02 
GeneralRe: I have translated into VB.NET, but i met a problem. PinmemberChris Morgan15-Sep-06 3:30 
GeneralAn easier solution in .NET 2.0 PinmemberF2denmark5-Jun-06 2:16 
GeneralRe: An easier solution in .NET 2.0 [modified] Pinmembergaryjohn_200016-Sep-07 12:56 
GeneralRe: An easier solution in .NET 2.0 PinmemberAndrew Phillips16-Oct-07 19:49 
GeneralRe: An easier solution in .NET 2.0 Pinmembershuami3-Jan-08 18:44 
GeneralRe: An easier solution in .NET 2.0 PinmemberMember 182304426-May-10 5:41 
GeneralRe: An easier solution in .NET 2.0 PinmemberRenfield7811-Feb-11 3:39 
GeneralRe: An easier solution in .NET 2.0 PinmemberMagina Ogre21-Mar-12 23:27 
GeneralMinimum Column Width PinmemberANIL KUMAR SHARMA (INDIA)25-May-06 23:26 
Generallistview sorting Pinmemberportia vandemere9-Jan-06 11:05 
AnswerRe: listview sorting Pinmemberiskra_em1-Dec-06 3:49 
GeneralCTRL + PinmemberGeert Baeyaert19-Sep-05 23:47 
GeneralRe: CTRL + PinmemberChris Morgan20-Sep-05 4:47 
QuestionCan this code work in Linux ? PinmemberEric.Wang15-Sep-05 16:30 
AnswerRe: Can this code work in Linux ? PinmemberRodrigo Dias16-Sep-05 0:11 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web01 | 2.8.141223.1 | Last Updated 20 Sep 2005
Article Copyright 2005 by Chris Morgan
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid