views:

55

answers:

4

My project is .NET/WinForms.

I have a list view which is always filled with items. I would like it to have selection always. However, if I click on an empty area below list view items, it looses selection.

The list has multiple selection = true and hide selection = false.

A: 

You need to handle the SelectedIndexChanged event of the ListView and in case there is no item selected after this event occurs, programmatically re-select the last known selection.

Marek
A: 

The ListView has a SelectedIndexChanged event. Listen for this and then query the listView.SelectedItems.Count property - if it's zero, select the first / last selected item.

Adam
+1  A: 

Like the others have said, the SelectedIndexChanged event is what you should look at, however you should use it in collaboration with the ItemSelectionChanged event. Here's some code I've just cooked up:

// Holds the last selected index
private int _previousIndex = -1;

// Restores the previous selection if there are no selections
private void listView1_SelectedIndexChanged(object sender, EventArgs e)
{
    if (listView1.SelectedIndices.Count == 0)
    {
        if (_previousIndex >= 0)
        {
            listView1.SelectedIndices.Add(_previousIndex);
        }
    }
}

// Records the last selected index
private void listView1_ItemSelectionChanged(object sender, 
               ListViewItemSelectionChangedEventArgs e)
{
    if (e.IsSelected)
    {
        _previousIndex = e.ItemIndex;
    }
}

For pure code re-use purposes, it'd probably be worthwhile putting this code into a new UserControl and have a property that determines whether or not to allow the last selection to be lost:

public class CustomListView : ListView
{

    protected CustomListView()
    {
        this.SelectedIndexChanged += new EventHandler(CustomListView_SelectedIndexChanged);
        this.ItemSelectionChanged += new ListViewItemSelectionChangedEventHandler(CustomListView_ItemSelectionChanged);
    }

    void CustomListView_SelectedIndexChanged(object sender, EventArgs e)
    {
        if (this.MaintainLastSelection && this.SelectedIndices.Count == 0)
        {
            if (_previousIndex >= 0)
            {
                this.SelectedIndices.Add(_previousIndex);
            }
        }
    }

    void CustomListView_ItemSelectionChanged(object sender, ListViewItemSelectionChangedEventArgs e)
    {
        if (e.IsSelected)
        {
            _previousIndex = e.ItemIndex;
        }
    }

    private int _previousIndex = -1;

    public bool MaintainLastSelection
    {
        get { return _maintainLastSelection; }
        set { _maintainLastSelection = value; }
    }
    private bool _maintainLastSelection;

}
GenericTypeTea
+1  A: 

You need to prevent the native control from seeing the mouse click so it won't unselect an item. Add a new class to your project and paste the code shown below. Compile. Drop it from the top of the toolbox onto your form, replacing the existing one.

using System;
using System.Drawing;
using System.Windows.Forms;

class MyListView : ListView {
    protected override void WndProc(ref Message m) {
        // Swallow mouse messages that are not in the client area
        if (m.Msg >= 0x201 && m.Msg <= 0x209) {
            Point pos = new Point(m.LParam.ToInt32() & 0xffff, m.LParam.ToInt32() >> 16);
            var hit = this.HitTest(pos);
            switch (hit.Location) {
                case ListViewHitTestLocations.AboveClientArea :
                case ListViewHitTestLocations.BelowClientArea :
                case ListViewHitTestLocations.LeftOfClientArea :
                case ListViewHitTestLocations.RightOfClientArea :
                case ListViewHitTestLocations.None :
                    return;
            }
        }
        base.WndProc(ref m);
    }
}
Hans Passant
Thanks for your suggestion but it is a bit too low-level. I think I will go for manual setting selection back as suggested below.
Alex
Odd, I'd consider "manually setting selection back" a low-level hack.
Hans Passant