Click here to Skip to main content
6,933,325 members and growing! (13,390 online)
Email Password   helpLost your password?
Desktop Development » List Controls » ListView controls     Intermediate

Locking ListView Column Size

By Chris Morgan

A custom ListView that allows the column size to be locked.
C#, Windows, .NET1.1VS.NET2003, Dev
Posted:14 Sep 2005
Updated:20 Sep 2005
Views:66,313
Bookmarked:33 times
printPrint Friendly   add Share
      Discuss Discuss   Broken Article?Report  
16 votes for this article.
Popularity: 5.67 Rating: 4.71 out of 5

1

2

3
3 votes, 18.8%
4
13 votes, 81.3%
5

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

About the Author

Chris Morgan


Member

Occupation: Web Developer
Location: United States United States

Other popular List Controls articles:

 
Article Top
You must Sign In to use this message board.
FAQ FAQ 
 
Noise Tolerance  Layout  Per page   
 Msgs 1 to 22 of 22 (Total in Forum: 22) (Refresh)FirstPrevNext
GeneralAwesome control PinmemberVivek_Minhas0:10 31 Mar '09  
GeneralC++ WTL style PinmemberT800G11:51 26 Nov '08  
QuestionItem Selection PinmemberMember 37361773:35 13 May '08  
GeneralMuch simpler way Pinmemberspintz5:10 15 Nov '07  
Generallast item removal Pinmembernumbers1thru98:24 28 Dec '06  
GeneralRe: last item removal PinmemberChris Morgan8:48 15 Jan '07  
GeneralRe: last item removal Pinmembernumbers1thru912:25 15 Jan '07  
GeneralI have translated into VB.NET, but i met a problem. PinmemberLukecao1:01 14 Sep '06  
GeneralRe: I have translated into VB.NET, but i met a problem. PinmemberChris Morgan5:28 14 Sep '06  
GeneralRe: I have translated into VB.NET, but i met a problem. PinmemberLukecao23:02 14 Sep '06  
GeneralRe: I have translated into VB.NET, but i met a problem. PinmemberChris Morgan3:30 15 Sep '06  
GeneralAn easier solution in .NET 2.0 PinmemberF2denmark2:16 5 Jun '06  
GeneralRe: An easier solution in .NET 2.0 [modified] Pinmembergaryjohn_200012:56 16 Sep '07  
GeneralRe: An easier solution in .NET 2.0 PinmemberAndrew Phillips19:49 16 Oct '07  
GeneralRe: An easier solution in .NET 2.0 Pinmembershuami18:44 3 Jan '08  
GeneralMinimum Column Width PinmemberANIL KUMAR SHARMA (INDIA)23:26 25 May '06  
Hello Chris,
Thanks for your good article. But I am searching for just more control over column. As my column is reduced to such extent that it disappears. I wanna to control the minimum column width. The user can maximize it (good if there can be limit also based on max. of either data width or header width) but should not able to minimize it after certain extent so that column doesnt disappear, which is hard to cover back only after restarting applicaiton.

Thanks
Anil Kumar Sharma
(PL)
INDIA

Anil Kumar Sharma
(PL)
INDIA
Generallistview sorting Pinmemberportia vandemere11:05 9 Jan '06  
AnswerRe: listview sorting Pinmemberiskra_em3:49 1 Dec '06  
GeneralCTRL + PinmemberGeert Baeyaert23:47 19 Sep '05  
GeneralRe: CTRL + PinmemberChris Morgan4:47 20 Sep '05  
GeneralCan this code work in Linux ? PinmemberEric.Wang16:30 15 Sep '05  
GeneralRe: Can this code work in Linux ? PinmemberRodrigo Dias0:11 16 Sep '05  

General General    News News    Question Question    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

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

PermaLink | Privacy | Terms of Use
Last Updated: 20 Sep 2005
Editor: Smitha Vijayan
Copyright 2005 by Chris Morgan
Everything else Copyright © CodeProject, 1999-2010
Web17 | Advertise on the Code Project