Home

Windows Controls: The List View

 

Fundamentals of the List View

 

Introduction

A list view is a list of items that presents many options:

  • The items can each appear as a large icon and a label
     
    List View: Large Icons
  • The items can each appear as a small icon and a label:
     
    List View: List
  • The items can be made to show some details (related-information) each:
      
    List View Style: Small Icons

List View Creation

The list view control is made available in the .NET Framework through the ListView class that is represented in the Windows Forms section of the Toolbox by the list view button. To add a list view to your application, you can click list view in the Toolbox and click the form or another container.

To programmatically create a list view, you can declare a variable of type ListView, use the new operator to instantiate it and add it to its host's list of controls through a call to the Controls.Add() method. Here is an example:

open System
open System.Drawing
open System.Windows.Forms

// Form: Exercise
let exercise = new Form()
exercise.Width  <- 452
exercise.Height <- 218
exercise.Text   <- "Countries Statistics"

// List View: Countries
let lvwCountries = new ListView()
lvwCountries.Location <- new Point(12, 12)
lvwCountries.Width <- 420
lvwCountries.Height <- 160

exercise.Controls.Add lvwCountries

Application.Run exercise

Countries Statistics

After this declaration, an empty rectangular control is created and added to your application. You can then start populating it.

Introduction to the Items of a List View

 

The Collection of List View Items

The items of a list view are stored in a property called Item, which is of type ListViewItemCollection. The ListViewItemCollection class implements the IList, the ICollection, and the IEnumerable interfaces.

Programmatically Creating an Item

To help you programmatically create a new item, the ListViewItemCollection class is equipped with the Add() method which is overloaded with three versions. One of the versions of this method uses the following signature:

abstract Add : 
        text:string -> ListViewItem  
override Add : 
        text:string -> ListViewItem

This method expects a string that will display as the new item. Here is an example:

open System
open System.Drawing
open System.Windows.Forms

// Form: Exercise
let exercise = new Form()
exercise.Width  <- 452
exercise.Height <- 218
exercise.Text   <- "Countries Statistics"

// List View: Countries
let lvwCountries = new ListView()
lvwCountries.Location <- new Point(12, 12)
lvwCountries.Width    <- 420
lvwCountries.Height   <- 160

lvwCountries.Items.Add "Egypt" |> ignore

exercise.Controls.Add lvwCountries

Application.Run exercise

This would produce:

Countries Statistics

As the Items property is in fact a collection, each item is represented by the Item property of the ListViewItemCollection class. This Item property is based on the ListViewItem class. The ListViewItem class is equipped with various constructors, the default of which allows you to instantiate an item without giving much detail.

Instead of directly passing a string to the ListViewItemCollection.Add() method, you can first create a ListViewItem object and pass it to the following version of the ListViewItemCollection.Add() method:

abstract Add : 
        value:ListViewItem -> ListViewItem  
override Add : 
        value:ListViewItem -> ListViewItem

This method expects a ListViewItem value. One way you can use it consists of providing the string the item would display. To do this, you can use the following constructor of the ListViewItem class:

new :
        text:string -> ListViewItem

This constructor expects as argument the text that the new item will display. Here is an example:

open System
open System.Drawing
open System.Windows.Forms

// Form: Exercise
let exercise = new Form()
exercise.Width  <- 452
exercise.Height <- 218
exercise.Text   <- "Countries Statistics"

// List View: Countries
let lvwCountries = new ListView()
lvwCountries.Location <- new Point(12, 12)
lvwCountries.Width <- 420
lvwCountries.Height <- 160

lvwCountries.Items.Add "Egypt" |> ignore

let lviPortugal : ListViewItem = new ListViewItem("Portugal")
lvwCountries.Items.Add lviPortugal |> ignore

exercise.Controls.Add lvwCountries

Application.Run exercise

Countries Statistics

Adding a Range of Items

You can use any of these techniques to create as many items as necessary. Alternatively, if you have many items to create, you can first store them in an array of ListViewItem values, then call the AddRange() method. The signature of this method is:

member AddRange : 
        items:ListView.ListViewItemCollection -> unit

This method takes as argument an array of ListViewItem objects. Here is an example:

open System
open System.Drawing
open System.Windows.Forms

// Form: Exercise
let exercise = new Form()
exercise.Width  <- 452
exercise.Height <- 218
exercise.Text   <- "Countries Statistics"

// List View: Countries
let lvwCountries = new ListView()
lvwCountries.Location <- new Point(12, 12)
lvwCountries.Width <- 420
lvwCountries.Height <- 160

lvwCountries.Items.Add "Egypt" |> ignore

let lviPortugal : ListViewItem = new ListViewItem("Portugal")
lvwCountries.Items.Add lviPortugal |> ignore

let lviCountries : ListViewItem array = [| new ListViewItem("Australia"); new ListViewItem("Mali"); new ListViewItem("Sweden"); new ListViewItem("Venezuela") |]
lvwCountries.Items.AddRange lviCountries

Countries Statistics

Alternatively, you can create an array of strings and pass it to the following constructor of the ListView class:

new : 
        items:string[] -> ListViewItem

Grouping the Items of a List View

 

Introduction

To better organize the items of a list view, you can put them in groups. Each group can hold a different set of items and/or a different number of items, but all items must be of the same type.

To support groups, the ListView class is equipped with a property named Groups. This property is based on an enumeration named ListViewGroupCollection:

member Groups : ListViewGroupCollection with get

ListViewGroupCollection is a classic collection class that implements the IList, the ICollection, and the IEnumerable interfaces, which give it the ability to add and manage groups.

type ListViewGroupCollection =  
    class 
        interface IList 
        interface ICollection 
        interface IEnumerable 
    end

To support a group of items, the .NET Framework provides a class named ListViewGroup.

Characteristics of a Group of Items

The items that belong to a group must make it obvious. To start, the group must identify itself, which is done with a title or label. The title of a group is also called a header. This is represented by the Header property. Besides a title, a group has a name, which is represented by the Name property.

By default, when the title displays, it is aligned to the left. If you want, you can position it either to the center or the right section. The alignment is controlled by the HeaderAlignment property that is of type HorizontalAlignment.

Applying a Group to an Item

After creating the groups, you can assign one to any item of your choice. To let you get a list of items that belong to a group, the ListViewGroup class is equipped with a property named Items. This property is of type ListViewItemCollection:

member Items : ListView.ListViewItemCollection with get

To know the list view to which a group belongs, get the value of the ListView property, which is of type ListView:

member ListView : ListView with get

The Styles of a List View

 

Introduction

A list view provides various options to display its items. To support this, the ListView class is equipped with the View property that is based on the View enumeration:

member View : View with get, set

Its members are:

  • LargeIcon: In this view, the control displays a list of items in the order they were added from left to right and from top to bottom. This means that items start displaying on the top left section of the control to right. When the first line is filled and there are still items, the list continues to the next line. Each item uses a square size corresponding to a 32x32 pixel
  • SmallIcon: Like the LargeIcon style, this view displays the list of items from the left to the right then to the next line if necessary. This time, each item uses a square region corresponding to a 16x16 pixel size
  • List: Like the SmallIcon option, each item uses a 16x16 pixel square to display. This time, the list is arranged in columns: the first item appears to the left side of the control. The next item (usually in alphabetical order) appears under it, and so on. If there are more items to fit in one column, the list continues with a new column to the right of the previous one. This continues until the list is complete
  • Details: In this view, instead of showing just the string of the (main) item, each item can provide more detailed information in a column

As seen so far, you can use one of four different displays on a list view. Furthermore, you can give the user the ability to change views as needed. The different displays of the list view are controlled by the View property of the ListView class. To specify the type of view to use, assign the desired member of the View enumerator to the ListView.View property.

The Icons of List View Items

A list view has built-in capability to display icons. The list view uses two sets of pictures in either 16x16 pixels or larger than that. This means that two icons would be associated to each item.

The first category of icons should have a 16x16 pixels size. These are referred to as small icons. In the second category of pictures, one of the types uses a 32x32 pixels size. In Microsoft Windows 7, the set of 32x32 pixels list is referred to as medium icons. In the .NET Framework, these are referred to as large icons.

Before using the pictures, you should store them in image lists. Each set must be stored in its own ImageList object.

To support the various sets of icons, the ListView class is equipped with a property named LargeImageList for the 32x32-pixel icons and another property named SmallImageList for the 16x16-pixel icons. After creating both ImageList objects, you can assign each to the appropriate property.

After assigning the icons to the list view items, each view style can use the appropriate item to display:

  • LargeIcon: In this view, each item is displayed with its assigned 32x32 pixels icon. The string of the item displays under its corresponding icon:
     
    List View: Large Icons
  • List: Each item appears with the 16x16 pixels small icon to its left:
     
    List View: List
  • SmallIcon: Same as the List option
  • Details: Same as the List option

The Columns of a List View

 

Introduction

One of the characteristics of a list view is that it can provide more information about each item of its list. Each type of item can be equipped with its own list of sub-items. The view would appear as follows:

List View Style: Details

Before creating the sub-items of a list view, you may need to plan them first to identify the types of information you want to provide. To guide the user with the type of information that each item would display, you can create a column for each type.

To support columns, the ListView class is equipped with a property named Columns that is a collection based on the ColumnHeaderCollection class:

member Columns : ListView.ColumnHeaderCollection with get

Creating Columns

To create a column, you can call the ColumnHeaderCollection.Add() method that is overloaded with various versions. One of the versions of this method uses the following signature:

abstract Add : 
        text:string * 
        width:int * 
        textAlign:HorizontalAlignment -> ColumnHeader  
override Add : 
        text:string * 
        width:int * 
        textAlign:HorizontalAlignment -> ColumnHeader

The first argument of this method is referred to as the column's caption. It is text that would display in the column header. The second argument is a natural number that represents the distance from the left to the right borders of the column. The last argument specifies how the caption of the column would be aligned. The options are the same as those of the text box: Left, Center, or Right. The default value is Left.

Here is an example of creating a column by calling this method:

open System
open System.Drawing
open System.Windows.Forms

// Form: Exercise
let exercise = new Form()
exercise.Width  <- 452
exercise.Height <- 218
exercise.Text   <- "Countries Statistics"

// List View: Countries
let lvwCountries = new ListView()
lvwCountries.Location <- new Point(12, 12)
lvwCountries.Width <- 420
lvwCountries.Height <- 160

lvwCountries.View <- View.Details
lvwCountries.Columns.Add "Name", 120, HorizontalAlignment.Left) |> ignore

exercise.Controls.Add lvwCountries

Application.Run exercise

This would produce:

As mentioned earlier, a column is in fact an object of type ColumnHeader. This class is equipped with all the necessary characteristics that define a column header:

  • Caption: The Text property holds the string that displays on top of the column
  • Width: This property represents the width of the column
  • Text Alignment: The TextAlign property specifies the horizontal alignment of its string. This property uses a value of type HorizontalAlignment, which is the same as that of the text box
  • Index: Since the columns are stored in a collection, this property allows you to get the index of this column in the collection it belongs to
  • The Parent List View: If you want to know what list view the current column header belongs to, you can access its ColumnHeader.ListView property

Instead of defining a column in the Add() method, you can first create an object based on the ColumnHeader class and then pass it to the following version of the ColumnHeaderCollection.Add() method:

abstract Add : 
        value:ColumnHeader -> int  
override Add : 
        value:ColumnHeader -> int

This method takes as argument a ColumnHeader object. Here is an example:

open System
open System.Drawing
open System.Windows.Forms

// Form: Exercise
let exercise = new Form()
exercise.Width  <- 452
exercise.Height <- 218
exercise.Text   <- "Countries Statistics"

// List View: Countries
let lvwCountries = new ListView()
lvwCountries.Location <- new Point(12, 12)
lvwCountries.Width <- 420
lvwCountries.Height <- 160

lvwCountries.View <- View.Details
lvwCountries.Columns.Add "Name", 120, HorizontalAlignment.Left) |> ignore

let colArea : ColumnHeader = new ColumnHeader()
colArea.Text <- "Area"
colArea.Width <- 80
colArea.TextAlign <- HorizontalAlignment.Right
lvwCountries.Columns.Add colArea |> ignore

exercise.Controls.Add lvwCountries

Application.Run exercise

This would produce:

Countries Statistics

Instead of adding one column at a time as we have done above, you can first create an array of ColumnHeader objects and pass it to the ListView.ColumnHeaderCollection.AddRange() method. Its signature is:

abstract AddRange : 
        values:ColumnHeader[] -> unit  
override AddRange : 
        values:ColumnHeader[] -> unit

Here is an example of using it:

open System
open System.Drawing
open System.Windows.Forms

// Form: Exercise
let exercise = new Form()
exercise.Width  <- 452
exercise.Height <- 218
exercise.Text   <- "Countries Statistics"

// List View: Countries
let lvwCountries = new ListView()
lvwCountries.Location <- new Point(12, 12)
lvwCountries.Width <- 420
lvwCountries.Height <- 160

lvwCountries.View <- View.Details
lvwCountries.Columns.Add "Name", 120, HorizontalAlignment.Left) |> ignore

let colArea : ColumnHeader = new ColumnHeader()
colArea.Text <- "Area"
colArea.Width <- 80
colArea.TextAlign <- HorizontalAlignment.Right
lvwCountries.Columns.Add colArea |> ignore

let colPopulation : ColumnHeader  = new ColumnHeader()
colPopulation.Text <- "Population"
colPopulation.Width <- 78
colPopulation.TextAlign <- HorizontalAlignment.Right

let colCapital : ColumnHeader = new ColumnHeader()
colCapital.Text <- "Capital"
colCapital.Width <- 96
colCapital.TextAlign <- HorizontalAlignment.Left

let colCode : ColumnHeader = new ColumnHeader()
colCode.Text <- "Code"
colCode.Width <- 40
colCode.TextAlign <- HorizontalAlignment.Center

let cols : ColumnHeader array = [| colPopulation; colCapital; colCode |]
lvwCountries.Columns.AddRange cols

exercise.Controls.Add lvwCountries

Application.Run exercise

This would produce:

Countries Statistics

Column Insertion

If you call the AddRange() method, its list of columns is created at the end of any existing column, unless there was no other column. If you call the Add() method to create a column, the new column is added at the end of the existing columns, unless it is the first column. If you don't want the new column to simply be created at the end of the other column(s), if any, you can call the ColumnHeaderCollection.Insert() method. It is overloaded with two versions and their signatures are:

member Insert : 
        index:int * 
        value:ColumnHeader -> unit
member Insert : 
        index:int * 
        text:string * 
        width:int * 
        textAlign:HorizontalAlignment -> unit

In both versions, the first argument specifies the index where the new column will be created inside the Columns collection.

The Number of Columns of a List View

As reviewed above, the columns of a list view are stored in a collection. To know the number of columns of a list view, you can check its ColumnHeaderCollection.Count property.

Locating Columns

To find out if a certain column is part of a list view, you can call the ColumnHeaderCollection.Contains() method. Its signature is:

member Contains : 
        value:ColumnHeader -> bool

This method takes as argument a defined ColumnHeader object and scans the list of columns looking for it. If it finds it, it returns true. If it doesn't find a column that matches this object, it returns false. As opposed to looking for a column, you can perform two searches in one by calling the ColumnHeaderCollection.IndexOf() method. Its signature is:

member IndexOf : 
        value:ColumnHeader -> int

This method looks for the value ColumnHeader. If it finds it, it returns the column's index from the collection. If the method doesn't find it, it returns -1.

Deleting Columns

If you don't need a column any more, you can delete it. In the same way, you can delete all columns of a list view. To delete a ColumnHeader object, you can call the ColumnHeaderCollection.Remove() method. Its signature is:

abstract Remove : 
        column:ColumnHeader -> unit  
override Remove :
        column:ColumnHeader -> unit

To delete a column based on its position in the collection, you can call the ColumnHeaderCollection.RemoveAt() method. Its signature is:

abstract RemoveAt :
        index:int -> unit
override RemoveAt :
        index:int -> unit

To delete all columns of a list view, you can call the ListView.ColumnHeaderCollection.Clear() method. Its signature is:

abstract Clear : unit -> unit  
override Clear : unit -> unit
 
   
 

Using a List View

 

Selecting an Item

As mentioned previously, a list view is made of a list of items. An item can be identified by its index:

  • On a large image or a small image style, the item on the top-left side has an index of 0. The second item from the left has an index of 1, and so on, up to the subsequent lines, if any
  • On a list style, the top item on the left column has an index of 0. The second item from top on the left column has an index of 1, and so on, up to the subsequent columns, if any
  • On a details style, the most top item has an index of 0. The second item from the top has an index of 1, and so on 

When a list view comes up, it displays its list of items. To use an item, the user can click it. When an item has been clicked, the control fires a SelectedIndexChanged event. The SelectedIndexChanged event is of type EventArgs, which means it does not carry any significant information except to let you know that an item has been clicked. Worse, this event does not even let you know the index of the item that was clicked. This means that you must work on your own to identify the item that was clicked.

When the user clicks an item, you can use a technique to identify the item that was selected, when another item gets selected, the the item that was previously selected fires an ItemSelectionChanged event.

Editing a Label

Clicking an item once allows the user to select it. You may create an application that allows the user to edit an item, that is, to change the string that the item displays. To edit an item, the user can click (once) an item, then click (once) the item again. This puts it into edit mode. The user can then use the keyboard to change the string of the item:

To support the ability to let the user edit an item, the ListView class is equipped with the LabelEdit Boolean property. The default value of this property is false, which would prevent the user from editing the item in the list view. If you set this property to true, the user can click, then click the item to edit it.

When the user starts editing, the control fires a BeforeLabelEdit event. This event allows you to take care of some early processing, such as checking what the user is doing or canceling the editing.

If you allow the user to edit the item, after the user has edited it, the control fires an AfterLabelEdit event.

Both the BeforeLabelEdit and the AfterLabelEdit events are of type LabelEditEventArgs. One of the pieces of information that this class provides is the index of the item that is being edited. This index is held by the Item property of the LabelEditEventArgs class. To get the string resulting from the user editing, the LabelEditEventArgs class is equipped with the Label property. When the control fires a BeforeLabelEdit or the a AfterLabelEdit event, to help you decide whether to accept or reject the change, the LabelEditEventArgs class is equipped with the CancelEdit Boolean property.

Activating an Item

When an item has been selected, it becomes highlighted. If you use the LabelEdit property to allow the user to edit an item, this is a local edition. In the next sections, we will learn how to add sub-items to a list view item. When an item is equipped with sub-items, you may want your application to allow the user to change the item, one of its sub-items, or everything. Before doing anything, an item must be activated. There are three ways an item has been activated.

The technique used to activate an item is supported by the Activation property. The Activation property is based on the ItemActivation enumeration that has three members:

  • Standard: When the Activation property is set to Standard, to activate an item, the user must double-click the item
  • OneClick: With this option, the user would click an item once to active it
  • TwoClick: If you set the Activation property to this member, the user would have to click the item, then click it again to activate it (clicking once and clicking again once is not the same as double-clicking)

When an item has been activated, the control fires an ItemActivate event. You can use either this or the SelectedIndexChanged event to process the item.

Using Columns

Besides the items, the user can also use the columns. For example, you can allow the user to re-arrange or sort the list of items with the user clicks a column. When the user a column header, the control fires a ColumnClick event. The ColumnClick event is of type ColumnClickEventArgs. The ColumnClickEventArgs class is equipped with the Column integer property that allows you to identify the index of the column that was clicked.

Besides clicking a column header, the user can also resize a column by dragging the line separator between two columns. If the user decides to resize a column and starts dragging the line separator, the control fires ColumnWidthChanging event. After the user has resized a column, the control fires a ColumnWidthChanged event.

Identifying an Item

The only actual property that the nested ListViewItemCollection class is equipped with is the default Item property. This property allows you to identify an item. There are two versions of the Item property. You can identify an item using its index, which is its zero-based position. As an alternative, you can identify an item using its string.

The Sub-Items of an Item

 

Introduction

The idea of having columns is to provide more information about each item of a list view instead of a simple string for each. Consider the following example:

open System
open System.Drawing
open System.Windows.Forms

// Form: Exercise
let exercise = new Form()
exercise.Width  <- 452
exercise.Height <- 218
exercise.Text   <- "Countries Statistics"

// List View: Countries
let lvwCountries = new ListView()
lvwCountries.Location <- new Point(12, 12)
lvwCountries.Width <- 420
lvwCountries.Height <- 160

lvwCountries.View <- View.Details
lvwCountries.Columns.Add("Name", 120, HorizontalAlignment.Left) |> ignore

let colArea : ColumnHeader = new ColumnHeader()
colArea.Text <- "Area"
colArea.Width <- 80
colArea.TextAlign <- HorizontalAlignment.Right
lvwCountries.Columns.Add colArea |> ignore

let colPopulation : ColumnHeader  = new ColumnHeader()
colPopulation.Text <- "Population"
colPopulation.Width <- 78
colPopulation.TextAlign <- HorizontalAlignment.Right

let colCapital : ColumnHeader = new ColumnHeader()
colCapital.Text <- "Capital"
colCapital.Width <- 96
colCapital.TextAlign <- HorizontalAlignment.Left

let colCode : ColumnHeader = new ColumnHeader()
colCode.Text <- "Code"
colCode.Width <- 40
colCode.TextAlign <- HorizontalAlignment.Center

let cols : ColumnHeader array = [| colPopulation; colCapital; colCode |]
lvwCountries.Columns.AddRange cols

lvwCountries.Items.Add "Egypt" |> ignore

let lviPortugal : ListViewItem = new ListViewItem("Portugal")
lvwCountries.Items.Add lviPortugal |> ignore

let mutable lviCountry : ListViewItem = new ListViewItem("Australia")
lvwCountries.Items.Add lviCountry |> ignore
lviCountry <- new ListViewItem("Mali")
lvwCountries.Items.Add lviCountry |> ignore
lviCountry <- new ListViewItem("Sweden")
lvwCountries.Items.Add lviCountry |> ignore

exercise.Controls.Add lvwCountries

Application.Run exercise

This would produce:

Countries Statistics

Creating Sub Items

To support sub-items, the ListViewItem class is equipped with a property called SubItems. This property is of type ListViewSubItemCollection. To create a sub-item, you can directly specify its text by passing a string to the ListViewSubItemCollection.Add() method. The ListViewSubItemCollection.Add() method is overloaded with three versions. The version referred to in this case uses the following signature:

member Add : 
        text:string -> ListViewItem.ListViewSubItem

To identify each piece of information concerning a sub-item, the ListViewSubItemCollection class is equipped with a property called Item, which in turn is based on the ListViewSubItem class. As you can see, the above Add() method returns a ListViewSubItem value.

Here are two examples of calling the above Add() method:

open System
open System.Drawing
open System.Windows.Forms

// Form: Exercise
let exercise = new Form()
exercise.Width  <- 452
exercise.Height <- 218
exercise.Text   <- "Countries Statistics"

// List View: Countries
let lvwCountries = new ListView()
lvwCountries.Location <- new Point(12, 12)
lvwCountries.Width <- 420
lvwCountries.Height <- 160

lvwCountries.View <- View.Details
lvwCountries.Columns.Add("Name", 120, HorizontalAlignment.Left) |> ignore

let colArea : ColumnHeader = new ColumnHeader()
colArea.Text <- "Area"
colArea.Width <- 80
colArea.TextAlign <- HorizontalAlignment.Right
lvwCountries.Columns.Add colArea |> ignore

let colPopulation : ColumnHeader  = new ColumnHeader()
colPopulation.Text <- "Population"
colPopulation.Width <- 78
colPopulation.TextAlign <- HorizontalAlignment.Right

let colCapital : ColumnHeader = new ColumnHeader()
colCapital.Text <- "Capital"
colCapital.Width <- 96
colCapital.TextAlign <- HorizontalAlignment.Left

let colCode : ColumnHeader = new ColumnHeader()
colCode.Text <- "Code"
colCode.Width <- 40
colCode.TextAlign <- HorizontalAlignment.Center

let cols : ColumnHeader array = [| colPopulation; colCapital; colCode |]
lvwCountries.Columns.AddRange cols

let lviEgypt : ListViewItem = lvwCountries.Items.Add "Egypt"
lviEgypt.SubItems.Add "1,001,450" |> ignore
lviEgypt.SubItems.Add "74,718,797" |> ignore
lviEgypt.SubItems.Add "Cairo" |> ignore
lviEgypt.SubItems.Add "eg" |> ignore

let lviPortugal : ListViewItem = new ListViewItem("Portugal")
lviPortugal.SubItems.Add "92,391" |> ignore
lviPortugal.SubItems.Add "10,102,022" |> ignore
lviPortugal.SubItems.Add "Lisbon" |> ignore
lviPortugal.SubItems.Add "pt" |> ignore
lvwCountries.Items.Add lviPortugal |> ignore

let mutable lviCountry : ListViewItem = new ListViewItem("Australia")
lvwCountries.Items.Add lviCountry |> ignore
lviCountry <- new ListViewItem("Mali")
lvwCountries.Items.Add lviCountry |> ignore
lviCountry <- new ListViewItem("Sweden")
lvwCountries.Items.Add lviCountry |> ignore

exercise.Controls.Add lvwCountries

Application.Run exercise

This would produce:

Countries Statistics

As mentioned above, each sub-item is of type ListViewSubItem. The ListViewSubItem class is equipped with three constructors. The default constructor allows you to create an empty sub-item. After declaring a sub-item, you can specify its text by assigning the desired string to the ListViewSubItem.Text property. Instead of directly passing the text of a sub-item to the ListViewSubItemCollection.Add() method as done above, you can first define a ListViewSubItem object using the following constructor of the ListViewSubItem class:

new : 
        owner:ListViewItem * 
        text:string -> ListViewSubItem

The first argument of this constructor specifies the ListViewItem object to which this sub-item will belong. The second argument is simply the string that this sub-item will display. After defining a ListViewSubItem object, you can pass it to the following version of the ListViewSubItemCollection.Add() method:

member Add : 
        item:ListViewItem.ListViewSubItem -> ListViewItem.ListViewSubItem

Here are three examples of using it:

open System
open System.Drawing
open System.Windows.Forms

// Form: Exercise
let exercise = new Form()
exercise.Width  <- 452
exercise.Height <- 150
exercise.Text   <- "Countries Statistics"

// List View: Countries
let lvwCountries = new ListView()
lvwCountries.Location <- new Point(12, 12)
lvwCountries.Width <- 420
lvwCountries.Height <- 100

lvwCountries.View <- View.Details
lvwCountries.Columns.Add("Name", 120, HorizontalAlignment.Left) |> ignore

let colArea : ColumnHeader = new ColumnHeader()
colArea.Text <- "Area"
colArea.Width <- 80
colArea.TextAlign <- HorizontalAlignment.Right
lvwCountries.Columns.Add colArea |> ignore

let colPopulation : ColumnHeader  = new ColumnHeader()
colPopulation.Text <- "Population"
colPopulation.Width <- 78
colPopulation.TextAlign <- HorizontalAlignment.Right

let colCapital : ColumnHeader = new ColumnHeader()
colCapital.Text <- "Capital"
colCapital.Width <- 96
colCapital.TextAlign <- HorizontalAlignment.Left

let colCode : ColumnHeader = new ColumnHeader()
colCode.Text <- "Code"
colCode.Width <- 40
colCode.TextAlign <- HorizontalAlignment.Center

let cols : ColumnHeader array = [| colPopulation; colCapital; colCode |]
lvwCountries.Columns.AddRange cols

let lviEgypt : ListViewItem = lvwCountries.Items.Add "Egypt"
lviEgypt.SubItems.Add "1,001,450" |> ignore
lviEgypt.SubItems.Add "74,718,797" |> ignore
lviEgypt.SubItems.Add "Cairo" |> ignore
lviEgypt.SubItems.Add "eg" |> ignore

let lviPortugal : ListViewItem = new ListViewItem("Portugal")
lviPortugal.SubItems.Add "92,391" |> ignore
lviPortugal.SubItems.Add "10,102,022" |> ignore
lviPortugal.SubItems.Add "Lisbon" |> ignore
lviPortugal.SubItems.Add "pt" |> ignore
lvwCountries.Items.Add lviPortugal |> ignore

let mutable lviAustralia : ListViewItem = new ListViewItem("Australia")
let mutable subAustralia : ListViewItem.ListViewSubItem = new ListViewItem.ListViewSubItem(lviAustralia, "7,686,850")
lviAustralia.SubItems.Add subAustralia |> ignore

subAustralia <- new ListViewItem.ListViewSubItem(lviAustralia, "19,731,984")
lviAustralia.SubItems.Add subAustralia |> ignore
subAustralia <- new ListViewItem.ListViewSubItem(lviAustralia, "Canberra")
lviAustralia.SubItems.Add subAustralia |> ignore
subAustralia <- new ListViewItem.ListViewSubItem(lviAustralia, "au")
lviAustralia.SubItems.Add subAustralia |> ignore
lvwCountries.Items.Add lviAustralia |> ignore

let mutable lviMali : ListViewItem = new ListViewItem("Mali")
let mutable subMali :  ListViewItem.ListViewSubItem = new ListViewItem.ListViewSubItem(lviMali, "1.24 million")
lviMali.SubItems.Add subMali |> ignore
subMali <- new ListViewItem.ListViewSubItem(lviMali, "11,626219")
lviMali.SubItems.Add subMali |> ignore
subMali <- new ListViewItem.ListViewSubItem(lviMali, "Bamako")
lviMali.SubItems.Add subMali |> ignore
subMali <- new ListViewItem.ListViewSubItem(lviMali, "ml")
lviMali.SubItems.Add subMali |> ignore
lvwCountries.Items.Add lviMali |> ignore

let mutable lviSweden : ListViewItem = new ListViewItem("Sweden")
let mutable subSweden : ListViewItem.ListViewSubItem = new ListViewItem.ListViewSubItem(lviSweden, "449,964")
lviSweden.SubItems.Add subSweden |> ignore
subSweden <- new ListViewItem.ListViewSubItem(lviSweden, "8,878,085")
lviSweden.SubItems.Add subSweden |> ignore
subSweden <- new ListViewItem.ListViewSubItem(lviSweden, "Stockholm")
lviSweden.SubItems.Add subSweden |> ignore
subSweden <- new ListViewItem.ListViewSubItem(lviSweden, "se")
lviSweden.SubItems.Add subSweden |> ignore
lvwCountries.Items.Add lviSweden |> ignore

exercise.Controls.Add lvwCountries

Application.Run exercise

This would produce:

Countries Statistics

If you call the ListViewSubItemCollection.Add() method to create a sub-item, the new sub-item would be added to end of the list. If you want, you can insert the new sub-item somewhere inside the collection. To do this, you would call the ListViewSubItemCollection.Insert() method. Its signature is:

member Insert : 
        index:int * 
        item:ListViewItem.ListViewSubItem -> unit

The first argument is the index that the new sub-item will occupy after being inserted. The second argument is the sub-item to create.

Managing Sub-Items

When you create a new sub-item, it uses a default font and a black color on a white background. If you want, you can change the way a sub-item aesthetically displays. To allow these changes, the ListViewItem class is equipped with the UseItemStyleForSubItems Boolean property, whose default value is true. When this property is set to true, the compiler refers to the item that "owns" the current sub-item to paint the sub-item, as we will see in the next section. If you plan to change these aspects, you must first set this property to false.

After setting the ListViewItem.UseItemStyleForSubItems property to false, you can set the following properties of the ListViewSubItem class as you wish:

  • Font: This allows you to apply a font of your choice for a particular sub-item:
    member Font : Font with get, set
    This would be done as follows:
    let mutable lviSweden : ListViewItem = new ListViewItem("Sweden")
    lviSweden.UseItemStyleForSubItems <- false
    let mutable subSweden : ListViewItem.ListViewSubItem = new ListViewItem.ListViewSubItem(lviSweden, "449,964")
    . . .
    subSweden <- new ListViewItem.ListViewSubItem(lviSweden, "Stockholm")
    subSweden.Font <- new Font(FontFamily.GenericSansSerif, 14.00F, FontStyle.Italic)
    lviSweden.SubItems.Add subSweden |> ignore
    . . .

    Countries Statistics

  • ForeColor: Instead writing a sub-item's text in black, you can assign the desired color to this property to use a different color. This would be done as follows:
    let mutable subSweden : ListViewItem.ListViewSubItem = new ListViewItem.ListViewSubItem(lviSweden, "449,964")
    subSweden.ForeColor <- Color.Red
    lviSweden.SubItems.Add subSweden |> ignore
    . . .

    Countries Statistics

  • BackColor: This property allows an individual sub-item to display on top of a specify color. This would be done as follows:
    subSweden <- new ListViewItem.ListViewSubItem(lviSweden, "8,878,085")
    subSweden.BackColor <- Color.Blue
    lviSweden.SubItems.Add subSweden |> ignore
    . . .

    Countries Statistics

To restore these settings on the sub-item, you can call the ListViewItem.ListViewSubItem.ResetStyle() method. Its signature is:

member ResetStyle : unit -> unit

When called, this method resets the font, the text color, and the background color.

Managing the Items of a List View

 

The Font, Text Color, and Background of an Item

After adding an item to a list view, the new item assumes some default styles involving the font, the color, and the background. To enhance the appearance of the items, you can change these characteristics that are primarily controlled by the ListViewItem.UseItemStyleForSubItems Boolean property. Its default value is true. You can use it to change the properties of an item as follows:

  • Font: If you assign a new font to the item, this property allows you to apply a font of your choice to a particular sub-item. This would be done as follows:
    let mutable lviAustralia : ListViewItem = new ListViewItem("Australia")
    lviAustralia.Font <- new Font("Georgia", 8.00F, FontStyle.Bold)
    let mutable subAustralia : ListViewItem.ListViewSubItem = new ListViewItem.ListViewSubItem(lviAustralia, "7,686,850")
    lviAustralia.SubItems.Add subAustralia |> ignore
    subAustralia <- new ListViewItem.ListViewSubItem(lviAustralia, "19,731,984")
    lviAustralia.SubItems.Add subAustralia |> ignore
    subAustralia <- new ListViewItem.ListViewSubItem(lviAustralia, "Canberra")
    lviAustralia.SubItems.Add subAustralia |> ignore
    subAustralia <- new ListViewItem.ListViewSubItem(lviAustralia, "au")
    lviAustralia.SubItems.Add subAustralia |> ignore
    lvwCountries.Items.Add lviAustralia |> ignore

    Countries Statistics

  • ForeColor: You can assign the desired color to ListViewItem.ForeColor property to paint the item with a color of your choice. This would be done as follows:
    let mutable lviSweden : ListViewItem = new ListViewItem("Sweden")
    lviSweden.ForeColor <- Color.Red
    let mutable subSweden : ListViewItem.ListViewSubItem = new ListViewItem.ListViewSubItem(lviSweden, "449,964")
    lviSweden.SubItems.Add subSweden |> ignore
    subSweden <- new ListViewItem.ListViewSubItem(lviSweden, "8,878,085")
    lviSweden.SubItems.Add subSweden |> ignore
    subSweden <- new ListViewItem.ListViewSubItem(lviSweden, "Stockholm")
    lviSweden.SubItems.Add subSweden |> ignore
    subSweden <- new ListViewItem.ListViewSubItem(lviSweden, "se")
    lviSweden.SubItems.Add subSweden |> ignore
    lvwCountries.Items.Add lviSweden |> ignore

    Countries Statistics

  • BackColor: The ListViewItem.BackColor property allows you to paint the background of an item with a color of your choice. This would be done as follows:
    let lviEgypt : ListViewItem = lvwCountries.Items.Add "Egypt"
    lviEgypt.BackColor <- Color.Blue
    lviEgypt.SubItems.Add "1,001,450" |> ignore
    lviEgypt.SubItems.Add "74,718,797" |> ignore
    lviEgypt.SubItems.Add "Cairo" |> ignore
    lviEgypt.SubItems.Add "eg" |> ignore

    Countries Statistics

Locating a List View Item

The items of a list view are stores in a collection represented by the ListView.Items property. To know the number of items in the list, you can retrieve the value of the ListViewItemCollection.Count property. Each member of this collection has an index represented by the ListViewItemCollection.Index property.

You can also locate an item using the coordinates of a point inside its bounding area. To use this technique, you can call the GetItemAt() method of the ListView class. Its signature is:

member GetItemAt : 
        x:int * 
        y:int -> ListViewItem

This method expects the coordinates (x, y) of a point. If an item is found at that point, this method returns it. If there is no item at that point, the method returns 0.

Deleting Items

To delete an item from a list view, you can call the ListViewItemCollection.Remove() method. Its signature is:

abstract Remove : 
        item:ListViewItem -> unit  
override Remove : 
        item:ListViewItem -> unit

This method takes as argument the ListViewItem object to be deleted. If you are already positioned at that item, you can call its own ListViewItem.Remove() method. Its signature is:

abstract Remove : unit -> unit  
override Remove : unit -> unit

To delete an item based on its index, you can call the ListViewItemCollection.RemoveAt() method whose signature is:

abstract RemoveAt : 
        index:int -> unit  
override RemoveAt : 
        index:int -> unit

When calling this method, you must pass the index of the undesired item. If the item is found, it would be deleted. If you provide a negative index or one that is higher than the ListViewItemCollection.Count property, the compiler would throw an ArgumentOutOfRangeException exception.

To delete all items from a list view, you can call the ListView.Clear() method. Its signature is:

member Clear : unit -> unit

When called, this method removes all items of the list view.

Characteristics of a List View

 

Column Header Style

If you create the columns of a list view, when the user displays the detail view, the column headers appear and behave like regular buttons. This also means that the user can click a column header and you can take action. If you don't want this appearance and this behavior, you can make the columns appear flat. This characteristics is controlled by the HeaderStyle property of the ListView class. This property is based on the ColumnHeaderStyle enumerator. Its members are:

  • Clickable: This is the default style. The columns headers appear and behave like buttons:
    lvwCountries.HeaderStyle <- ColumnHeaderStyle.Clickable

    Countries Statistics

  • Nonclickable: The columns are flat and don't change their appearance when the user clicks one:
    lvwCountries.HeaderStyle <- ColumnHeaderStyle.Nonclickable

    Countries Statistics

  • None: No column header appears:
    lvwCountries.HeaderStyle <- ColumnHeaderStyle.None

    Countries Statistics

Selecting Items in a List View

To select an item in the list, the user can click it. The selected item indicates this by being highlighted. To select another item, the user can click it and this automatically dismisses the previous selection. If you want, you can give the user the ability to select more than one item or you can prevent the user from selecting more than one item. This characteristic is controlled by the MultiSelect property of the ListView class. Its default value is true, which allows the user to select one or more items. If you set it to false, the user can select only one item at a time.

You can also allow the user to select an item by positioning the mouse over it. This characteristic is controlled by the HoverSelection property of the ListView class.

When an item has been selected or more than one item are selected, the selected items are stored in a list represented by the SelectedItems property of the ListView class. The ListView.SelectedItems property is an object based on the ListView.SelectedListViewItemCollection class. If the ListView.MultiSelect property is set to false, this collection would contain only one item.

The number of items selected in the control is known as the Count property of the SelectedListViewItemCollection class. Each item selected can be identified through the Item indexed property of the SelectedListViewItemCollection class.

The SelectedListViewItemCollection class holds a list of the objects that are selected and each is identified as a ListViewItem. If you are more interested in the positions of the items selected and not necessarily their objects, you can use the SelectedIndices property of the ListView class. Each item selected has its index stored in this list. The ListView.SelectedIndices property is based on the ListView.SelectedIndexCollection class.

After selecting an item, if the user clicks another control, the item that was selected would not be highlighted anymore. If you want the control to continue showing the current selection even when the list view loses focus, set the value of the HideSelection Boolean property of the ListView class accordingly.

Full Row Selection

By default, to select an item, the user must click the item itself and not one of its sub-items. If you want an item and its sub-items to be selected when the user clicks anything on their line, you can change the value of the ListView.FullRowSelect Boolean property. Its default value is set to false, which obliges the user to click the item itself. If you set this property to true, the whole row would be highlighted when either you or the user selects it.

ApplicationTopic Applied: Allowing Full Row Selection

  1. On the form, click the list view
  2. In the Properties window, double-click FullRowSelect property to set its value to True

Grid Lines

When using the detail view, to make a list view more indicative, you can underline each row. This characteristic is controlled by the GridLines Boolean property of the ListView class. The default value of this property is false. If you set it to true, horizontal grid lines would appear among items throughout the list view, including empty rows:

lvwCountries.GridLines <- true

Countries Statistics

List Items and Check Boxes

Besides, or instead of, icons, you can display check boxes with the items of a list view. This characteristic is controlled by the CheckBoxes property. Its default value is false, which omits displaying the check boxes. If you set it to true, a check box would appear on the left of each item of the list view:

lvwCountries.CheckBoxes <- true;

Countries Statistics

 
 
   
 

Previous Copyright © 2012-2015, FunctionX Next