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

ListView Control with built-in support for hiding columns using a context menu, in C#

0.00/5 (No votes)
6 Dec 2005 1  
A ListView control with built-in support for hiding columns using a context menu.

Sample Image - ListView.jpg

Introduction

Every time I use a ListView, I think that I should provide a context menu to support the hiding of columns, which is very much required if the number of columns are more and you need to scroll to see the last column. I often think why the ListView doesn't come with this support built-in? So I decided to implement a ListView with built-in support for hiding columns, using a context menu. Using this extended listview which I named as ListViewEx, I get a context menu built-in which can hide columns. The best part is I don't have to create the menu items. Every time I add a new column, the menu for hiding it is ready by default :-).

Background

A ListView control allows you to display a list of items with an item text and, optionally, an icon to identify the type of item. When the View property of the control is set to View.Details, the items can be displayed in different columns. A ColumnHeader class represents a single column in the ListView control. A ListView contains a ColumnHeaderCollection class which stores the column headers that are displayed in the ListView control when the View property is set to View.Details. The ListView.ColumnHeaderCollection stores ColumnHeader objects that define the text to display for a column as well as how the column header is displayed in the ListView control when displaying columns. When a ListView displays columns, the items and their subitems are displayed in their own columns.

Implementation

I have implemented the ListViewEx control by extending the ListView, ColumnHeaderCollection, and ColumnHeader classes.

ColumnHeaderEx Class

This class extends the ColumnHeader class and provides the following properties and an event to notify that the visibility of the column has changed.

        /// <summary>

        /// Property to change the visibility of the column

        /// </summary>

        public bool Visible {
            get{return columnVisible;}
            set{ShowColumn(value);}
        }

        /// <summary>

        /// Menu item which represents the column.

        /// This menuitem can be used to add to the 

        /// context menu, which can inturn used to

        /// Hide/Show the column

        /// </summary>

        public MenuItem ColumnMenuItem {
            get{return menuItem;}
        }
       /// <SUMMARY>


        /// <summary>

        /// This event is raised when the visibility of column

        /// is changed.

        /// </summary>

        public event EventHandler VisibleChanged;

When the visibility of the column is changed, the subscriber for this event (ColumnHeaderCollectionEx) is notified and the menuitem is checked/unchecked depending on weather the column is hidden or displayed. The visibility of the column can be changed by changing the Visible property or by clicking the context menu, which is handled here alone. The following code does the described activity:

        /// <summary>

        /// Method to show/hide column

        /// </summary>

        /// <PARAM name="visible">visibility</PARAM>

        private void ShowColumn(bool visible) {
            if(columnVisible != visible) {
                columnVisible = visible;
                menuItem.Checked = visible;
                if(VisibleChanged != null) {
                    VisibleChanged(this, EventArgs.Empty);
                }
            }
        }

        /// <summary>

        /// Handler to handel toggel of menu item.

        /// </summary>

        /// <PARAM name="sender"></PARAM>

        /// <PARAM name="e"></PARAM>

        private void MenuItemClick(Object sender, System.EventArgs e) {
            MenuItem menuItem = (MenuItem)sender;
            // Ensure Column is hidden/shown accordingly

            ShowColumn(!menuItem.Checked);
        }

ColumnHeaderCollectionEx Class

This class extends the ColumnHeaderCollection and contains a list of ColumnHeadersExs. Columns can be added to the collection using the Add or AddRange methods which are listed as below. Whenever a column is added to the collection, we subscribe to the VisibleChanged changed event of the column headers and keep a reference to the column in a list for further use.

        /// <summary>

        /// Method adds a single column header to the collection.

        /// </summary>

        /// <PARAM name="str">Text to display</PARAM>

        /// <PARAM name="width">Width of column</PARAM>

        /// <PARAM name="textAlign">Alignment</PARAM>

        /// <returns>new ColumnHeaderEx added</returns>

        public override ColumnHeader Add(string str, 
               int width, HorizontalAlignment textAlign) {
            ColumnHeaderEx column = new ColumnHeaderEx(str, 
                                         width, textAlign);
            this.Add (column);
            return column;
        }
        
        /// <summary>

        /// Method adds a single column header to the collection.

        /// </summary>

        /// <PARAM name="column"></PARAM>

        /// <returns>The zero-based index into the collection 

        /// where the item was added.</returns>

        public override int Add(ColumnHeader column) {
            return this.Add (new ColumnHeaderEx(column));
        }

        /// <summary>

        /// Adds an array of column headers to the collection.

        /// </summary>

        /// <PARAM name="values">An array of ColumnHeader 

        /// objects to add to the collection. </PARAM>

        public override void AddRange(ColumnHeader[] values) {
            // Add range of column headers

            for(int index = 0; index < values.Length; index++) {
                this.Add (new ColumnHeaderEx(values[index]));
            }
        }

        /// <summary>

        /// Adds an existing ColumnHeader to the collection.

        /// </summary>

        /// <PARAM name="column">The ColumnHeader to 

        /// add to the collection. </PARAM>

        /// <returns>The zero-based index into the collection 

        /// where the item was added.</returns>

        public int Add(ColumnHeaderEx column) {
            // Add the column to the base

            int retValue = base.Add (column);
            // Keep a refrence in columnList

            columnList.Add(column.ColumnID, column);
            // Add the its menu to main menu

            ContextMenu.MenuItems.Add(column.ColumnMenuItem);
            // Subscribe to the visiblity change event of the column

            column.VisibleChanged += new EventHandler(ColumnVisibleChanged);
            return retValue;
        }

When a column is hidden, the VisibleChanged event is fired which is handled in this class. Here, we remove the column from the base but we still have it in our list. When a column is shown, we find out the index where the column has to be displayed and insert it back. This is listed below:

        /// <summary>

        /// Handler to handel the visiblity change of columns

        /// </summary>

        /// <PARAM name="sender">ColumnHeaderEx</PARAM>

        /// <PARAM name="e"></PARAM>

        private void ColumnVisibleChanged(object sender, EventArgs e) {
            ColumnHeaderEx column = (ColumnHeaderEx)sender;

            if(column.Visible == true) {
                // Show the hidden column

                // Get the position where the hidden column has to be shown

                ColumnHeaderEx prevHeader = FindPreviousVisibleColumn(column);
                if(prevHeader == null) {
                    // This is the first column, so add it at 0 location

                    base.Insert(0, column);
                }
                else {
                    // Got the location, place it their.

                    base.Insert(prevHeader.Index + 1, column);
                }
            }
            else {
                // Hide the column.

                // Remove it from the base, dont worry we have the 

                // refrence in columnList to get it back

                base.Remove(column);
            }
        }

        /// <summary>

        /// This method is used to find the first visible column

        /// which is present in front of the column specified

        /// </summary>

        /// <PARAM name="column">refrence columns for search</PARAM>

        /// <returns>null if no visible colums are in front of

        /// the column specified, else previous columns returned</returns>

        private ColumnHeaderEx FindPreviousVisibleColumn(ColumnHeaderEx column) {
            // Get the position of the search column

            int index = columnList.IndexOfKey(column.ColumnID);
            if(index > 0) {
                // Start a recursive search for a visible column

                ColumnHeaderEx prevColumn = 
                   (ColumnHeaderEx)columnList.GetByIndex(index - 1);
                if((prevColumn != null) && (prevColumn.Visible == false)) {
                    prevColumn = FindPreviousVisibleColumn(prevColumn);
                }
                return prevColumn;
            }
            // No visible columns found in font of specified column

            return null;
        }

ListViewEx Class

This class extends the ListView class. Here we don't do much, we ensure that ColumnHeaderCollectionEx is used instead of ColumnHeaderCollection, that's all. This is done by overriding the Columns property.

        /// <summary>

        /// Gets the collection of all column headers 

        /// that appear in the control.

        /// </summary>

        public new ColumnHeaderCollectionEx Columns {
            get{return columnHeadersEx;}
        }

How to use the code

This ListControlEx can be used exactly the way ListControl is used with respect to adding columns, removing columns, etc. But if you want to hide a column programmatically, you could do the following:

            listViewEx.Columns[4].Visible = false;
            listViewEx.Columns[5].Visible = false;

Or if you don't want a menu for a particular column, try this:

            // We will avoid removing the first column by the user.

            // Dont provide the menu to remove simple...

            listViewEx.Columns[0].ColumnMenuItem.Visible = false;

Conclusion

Most of the functionality have been described here. If you find mistakes, you can correct them. Or you can send me mails explaining the bugs or mistakes you found.

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