Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

C# List View v1.3

0.00/5 (No votes)
2 Mar 2004 35  
A fully featured completely managed C# ListView.

Introduction

Welcome to the fourth iteration of my Glacial ListView Control v1.3. I initially began this project back in December of 2002 when I went to write a ListView for a project I was contracting on, that needed to display scores from team based games (football, basketball, etc.). I started with the stock list control provided with VS.NET. After fumbling through way too many wndprocs, hacks, and dead ends, I decided that it would be a good idea to have an extremely customizable ListView for this and many future projects. So I started this project which is a ListView written entirely in C#.

On the first iteration v1.0, I tried to model everything based on a similar control I had written in MFC (which didn't work out very well). My second pass at the control v1.1 focused on optimization and .NET-centric changes to my code base with a few important feature upgrades. The third pass was all about features and specifically control embedding. This fourth update mainly focuses on tightening everything up, fixing bugs, and a lot more embedding work.

One thing many of you have found out is that I make frequent bug fixes and updates to this control. In order to keep from driving the good people at The Code Project crazy with lots of updates though, I post the minor updates on my website. You can get these sub-updates right here at Glacial Listview Control. You can also get more detailed change logs there.

At this point, this control has simply too many features to document here. It's been a real challenge to keep things clean enough in the source to be able to continually add features. The more things I add, the better my documentation and coding practices have to be or things will become unmanageable in a hurry. I am trying to create documentation to go along with this control in the form of a user guide to make using everything much easier.

Features

v1.3

Items collection editor, Activated embedding, Checkboxes (not embedded control check boxes), Help Files, Hover events.

Row borders, Control Embedding, New control modes (XP, Super Flat, Normal), XP Styles, Improved Sorting, Alternate Row Colors, Improved Image/Icon support, SubItem Cell word wrap, Optimization, Hot tracking for columns and items, Up/Down sorting, Focus Rectangle, Semi-transparent selections, User object variables at item and subitem levels, Multi-select, resizable columns, grid options, multi-line items, multi-line truncation, sorting, auto-sizing item height, Text + Alignment + Color, background color overrides, basic ListView functionality.

Highlights

Background/Foreground colors full control: You now have the ability to control the background or foreground color of the control at almost every level. I use a hierarchical system to control which color shows up. I take the lowest common override to determine the background color of a given cell. For instance, you can have a background color set for the control, the item and the subitem. A given cell will show up the subitem override. If you have only overridden the row background then that color will show up for the entire row. Same for foreground color (text color).

Items Collection Editor: You asked for it, so I did it. You can now add items in the collection editor as well as edit sub items visually. I've tried to make the collection editor as simple to use as possible.

Sorting: 3 methods of sorting are available. Insertion sort, which is great for sorts < 1000 in length. Quicksort and Merge sort for lists that are much greater in length. I also included the ability to sort by number, which you can set in the column property.

Checkboxes: Adding a checkbox to your item/subitem is as simple as setting the property in the column. Checkboxes are drawn onto the control so you don't have to worry about dealing with embedded secondary controls.

Images: You can add an image or icon to column headers or items/subitems at will.

Hover Events: If you need to create a tooltip for a given column, you can subscribe to the hover event. Remember though, you must turn the hover events on for the hover event to fire.

Hot tracking: If you turn on hot tracking for the vertical and/or horizontal then you will see a highlight on the column/row the mouse is over with the color you have chosen.

UserObjects/Tag: In order to facilitate the ability to store your user data into the items, I have included Tag properties at both the Item and SubItem levels. NOTE: THE ITEM TAG IS NOT THE SAME AS SUBITEM[0] TAG. So don't get these two confused. They are intended to be different.

Alternate row colors: This ended up being a highly sought after feature so I implemented it recently. To get that 'checkbook' look and feel, simply turn on alternate row colors and set the color you desire.

Control Embedding: You can embed raw controls or embed activated controls easily. Give your listview that professional look by adding progress bars, DateTime controls or your own custom control.

Using the control

The interface is modeled after the stock ListView built into the .NET framework, so many of the methods used to operate a ListView will hold up in this implementation. Someone mentioned a desire to have more examples of how to use the various features so I have included more information.

Most of the primary features of this control can be found in the design editor when you place the control onto the page. Everything from hot tracking, to grid lines, to selection color and others can be done visually through the MS design time environment.

To use this control

  1. Include the reference to the GlacialList.DLL in the references section of your project.
  2. Add using GlacialComponents.Controls directive to the head of your class file.

To add this control to your toolbox

  1. Go to Tools/Customize Toolbox/.NET Framework components
  2. Hit browse and navigate to the GlacialList.DLL and add it.
GlacialList mylist = new GlacialList();

mylist.Columns.Add( "Column1", 100 ); // this can also be added 
         // through the design time support 
mylist.Columns.Add( "Column2", 100 ); 
mylist.Columns.Add( "Column3", 100 ); 
mylist.Columns.Add( "Column4", 100 ); 

GLItem item;

item = this.glacialList1.Items.Add( "Atlanta Braves" );
item.SubItems[1].Text = "8v";
item.SubItems[2].Text = "Live";
item.SubItems[2].BackColor = Color.Bisque;
item.SubItems[3].Text = "MLB.TV"; 

item = this.glacialList1.Items.Add( "Florida Marlins" );
item.SubItems[1].Text = "";
item.SubItems[2].Text = "Delayed";
item.SubItems[2].BackColor = Color.LightCoral;
item.SubItems[3].Text = "Audio";


item.SubItems[1].BackColor = Color.Aqua; // set the background 
      // of this particular subitem ONLY
item.UserObject = myownuserobjecttype; // set a private user object
item.Selected = true; // set this item to selected state
item.SubItems[1].Span = 2; // set this sub item to span 2 spaces

ArrayList selectedItems = mylist.SelectedItems; 
           // get list of selected items

Embedding

There are two types of embedding in GlacialList. The first is Standard Control Embedding and is basically a one to one relationship between cells and the embedded control. Controls that are not visible in the ListView are 'hidden' but not destructed. The second is Activated Embedding, this type of embedding only shows up when you double click on a cell and is the same for every cell in a column.

One of the big challenges I faced in adding embedded controls is a problem none of the other list controls have dealt with. How to make the embedded controls show up BEHIND borders. Since the borders are almost always drawn onto the control, it was not possible to simply draw over the embedded controls. I got around this by adding 5 BorderStrip controls that I created to give the effect of the border on top of embedded controls. Other controls either don't have this feature or get rid of borders altogether to fix the problem.

Standard Control Embedding

The above graphic shows an embedded progress bar control.

One note about embedding right up front. I am giving you the tools to do whatever you want with a ListView. However, if you load 100,000 controls onto the surface then you have only yourself to blame if you scroll at a blazing 1 frame per hour. You must make intelligent decisions about what tradeoffs are best for whatever end result you are trying to achieve.

When I first tackled the problem of control embedding, I knew it wasn't going to be easy. To get solid, professional results I would have to make many tradeoffs for functionality and performance. Before I began coding into the control, I wrote several test projects first to check out various theories I had on how large numbers of visible controls would perform on an active surface. These tests allowed me to optimize the feature set without killing performance.

One of the tradeoffs I had to deal with was whether to destruct or Hide a control when it goes out of view. If you destruct and reconstruct the control each time, you save on memory and handles but you sacrifice speed. If you Hide the controls that go out of view then the ListView starts bogging the system when you get large numbers of items. I decided that if someone was using embedded controls it was unlikely that they would have 100k+ items, so I 'Hide' controls that are not in current view.

In order to make use of the control embedding, you simply need to add a control to the SubItem.Control property. You can also override that control at any time by setting the ForceText property of the subitem which overrides everything and displays whatever is in the Text attribute.

// add a progress bar control to a sub item
// setting item 0 and subitem 0

ProgressBar pb = new ProgressBar();
pb.Value = 50; // set it to some arbitrary value
item[0].SubItems[0].Control = pb;

Activated Embedding

The above graphic shows an activated embedded textbox making this cell editable.

Activated embedding is by far the more useful of the two embedding types and also a bit more complicated. Activated embedding allows you to embed a control without really embedding the control. An activated embedded control will only show up when someone double clicks on a cell. You set the activated embedded control in the column definition.

I struggled with many different ways to do activated embedding until I came up with the system of requiring the GLActivatedEmbedded interface to be implemented. In order to use the activated embedded type, you need to go into the column area and make the appropriate setting. Activated embedded types are valid for entire columns. You can't have more than one type per column nor can you mix activated embedding with standard control embedding.

// Add a column, then set its embedded type
GLColumn column = this.glacialList2.Columns.Add( "First column", 100 );
column.ActivatedEmbeddedType = GLActivatedEmbeddedTypes.TextBox;

Or set it through the type in the Column properties of the column collection editor.

If you want to use your own type as an activated embedded control then you need to set the Activated Embedded type to UserType. Then you need to add the GLActivatedEmbedded interface to your control and implement its members. Here, for example, is the implementation for the text box control.

// snipet from my textbox built in implementation
// of the activated embedded control
protected GLItem m_item = null;
protected GLSubItem m_subItem = null;
protected GlacialList m_Parent = null;

// called when control is activated
public bool GLLoad( GLItem item, GLSubItem subItem, GlacialList listctrl )
{
    this.BorderStyle = BorderStyle.None;
    this.AutoSize = false;

    m_item = item;
    m_subItem = subItem;
    m_Parent = listctrl;

    this.Text = subItem.Text;

    return true;
}

// called when control is to be destroyed
public void GLUnload()
{
    m_subItem.Text = this.Text;
}


// form1.cs
column.ActivatedEmbeddedControlType = new GLTextBox();

At this point, every cell in the column can now use the activated embedded control!

XP Header Style

In order to achieve the XP look for the control, you need to do two things. First you need to set the ControlStyle to XP, second you need to put Application.EnableVisualStyles(); at the beginning of your application.

Super Flat Style

This is a style I came up with to satisfy a need I had to have some very lightweight reports. As you can see, you can set the alternating colors field as well to make things more lively.

Design Time

One of the really nice things I like about VS.NET is the design time support. A nice part of that framework is the CollectionEditor. The collection editor allows you to add/remove/edit collection items as well as edit their properties.

To add collection editor support to your collection at design time, you need to take several steps.

  1. Create a type converter for the underlying type that is held in the collection.
    public class GLColumnConverter : TypeConverter 
    {
      public override bool CanConvertTo(ITypeDescriptorContext context, 
                                        Type destinationType)
      {
        if (destinationType == typeof(InstanceDescriptor)) 
          return true;
        return base.CanConvertTo(context, destinationType);
      }
      public override object ConvertTo(ITypeDescriptorContext context, 
                                       CultureInfo culture, object value, 
                                       Type destinationType)
      {
        if (destinationType == typeof(InstanceDescriptor) 
                 && value is GLColumn)
        {
          GLColumn column = (GLColumn)value; 
          ConstructorInfo ci = 
                 typeof(GLColumn).GetConstructor(new Type[] {});
          if (ci != null)
            return new InstanceDescriptor(ci, null, false);
        }
        return base.ConvertTo(context, culture, value, destinationType); 
      } 
    }
  2. Add the [TypeConverter("YourNameSpace.YourTypeConverter")] attribute to your collected type. This will allow your type to be serialized at design time.
    [TypeConverter("GlacialComponents.Controls.GLColumnConverter")]
    public class GLColumn
    {
      ...
  3. Create a custom collection editor class and add a refresh (otherwise your design time graphic won't update with the new information).
    public class CustomCollectionEditor : CollectionEditor
    {
      public CustomCollectionEditor(Type type) : base(type) {}
      public override object EditValue(ITypeDescriptorContext context, 
                                       IServiceProvider isp, object value)
      {
        GlacialList originalControl = (GlacialList)context.Instance;
        object returnObject = base.EditValue( context, isp, value );
        originalControl.Refresh();//.Invalidate( true );
        return returnObject;
      }
    }
  4. Add Collection Editor attribute to the instance of the collection.
    [
    Category("Behavior"),
    Description("Column Collection"),
    DesignerSerializationVisibility(
      DesignerSerializationVisibility.Content),
      Editor(typeof(CustomCollectionEditor), typeof(UITypeEditor)),
    Browsable(true)
    ]
    public GLColumnCollection Columns
    {
        get    { return m_Columns; }
    }

This is all you need to bring design time support to your collection!

Some Frequently Asked Questions

Q. XP Styles aren't showing up for me even though I have everything set correctly.

A. You need to make a call to Application.EnableVisualStyles() before the Application.Run(...) of your application.

Q. I don't see the fake button to the right of visible columns in the header.

A. The fake button on the stock ListView never made sense to me. This is not a bug, this is exactly the way I want it.

Q. How do I make a cell editable?

A. Activated embedding. Go to the column definition and set the activated embedded type to TextBox.

Q. Why does your vertical scrollbar stop at the base of the header?

A. Why shouldn't it? The scrollbar controls the client region not the header. The stock ListView is wrong in its implementation of that. :-)

Q. It won't let me 'Add' subitems, what am I doing wrong?

A. One of my favorite features of this control is that subitems are added and removed automatically and behind the scenes. If you add an item or column, rest assured, the subitem exists already.

Where am I going with this in the future?

This project will fork in two directions in the future. First, I intend to make a 'Pro' version of this control that I can market commercially. I intend to rewrite most of this control based on what I've learned and call it version 2.0. However, I also intend to keep this codebase and create a version 1.4 and above that will remain free here on The Code Project. I will do this by backporting major features (like TreeView) that I intend to add to 2.0. I hope you can all support me as I try to move into the controls business while continuing to provide free controls to people here. The versions I post on The Code Project will always be free and clear for all those who need it.

For v1.4, I am looking to bring a tree view to the control. I'm not sure if I am going to integrate it or if I am going to make it a separate control that subclasses the ListView (most likely). But that's the next big thing on my plate. I also would like to really tighten up the code. There are now so many features in this list control that I can't test them all any time I make a given change. Please be diligent in sending me bug reports as I need them to improve this control.

Conclusion

My fourth pass at this control really brings the control into what I always wanted in a list control. It's very flexible, fast, and has a large number of features. I recently integrated it into my full Glacial Source Control version control system which is one of the main reasons I started this project which went very smoothly. I am particularly interested in bugs and feature requests. I would request that you make feature requests on the message board here though so others can comment. I hope you enjoy the control.

License

You are free to use this version of the ListView control in both personal and commercial applications. However, you may only redistribute this in its compiled form (modified or unmodified), you may not redistribute the source or modified source. When using this control, please include the line "Glacial ListView - Copyright Glacial Components Software 2004 - http://www.glacialcomponents.com/" reference in either the about or in the documentation. This will help me out and allow me to continue to provide you with free controls.

History

  • February 24th, 2004: v1.30 Activated Embedding, Features
  • November 6th, 2003: v1.21 Bug fixes
  • October 25th, 2003: v1.2 Embedding, Features
  • July 10th, 2003: v1.1 Optimizations.
  • April 25th, 2003: v1.0 Initial post including basic features.

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