我目前正在用winform c#制作一个列表视图,每次点击列表视图上的空位,选中的项目就会丢失。
列表视图控件有一个 HideSelection
属性,默认为 True
. 让它 False
然后你就可以走了... 在某些情况下,这就足够了。
我以为有一个属性可以防止这种情况发生,但现在我找不到了。
你可以试试这个。
private void ListView_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
ListView listView = sender as ListView;
if (listView.SelectedItems.Count == 0)
foreach (object item in e.RemovedItems)
listView.SelectedItems.Add(item);
}
我是这样完成的
private void lvReads_MouseUp(object sender, MouseEventArgs e)
{
if (lvReads.SelectedItems.Count == 0)
if (lvReads.Items.Count > 0)
lvReads.Items.Find(currentName, false)[0].Selected = true;
}
然后...
private void lvReads_SelectedIndexChanged(object sender, EventArgs e)
{
if (lvReads.SelectedItems.Count == 1)
{
selectedIndex = lvReads.SelectedIndices[0];
if (currentName != lvReads.Items[selectedIndex].Name)
{
//load item
}
currentName = lvReads.Items[selectedIndex].Name;
}
}
你必须继承ListView类并做一些低级的消息处理。
class ListViewThatKeepsSelection : ListView
{
protected override void WndProc(ref Message m)
{
// Suppress mouse messages that are OUTSIDE of the items 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);
}
}
这在WinForms中比在WPF中更难做到。WinForms有一个 SelectedIndexChanged
事件,它不会告诉你任何关于已经选择的内容。加上 每当选择或取消选择一行时,它都会被触发。
所以,如果一行被选中后,你又选择了另一行,你就会收到两个 SelectedIndexChanged
事件。
问题是,在事件#1期间,ListView没有任何选择,你不知道事件#2是否会选择第二行。
你能做的最好的办法是等到你的应用程序空闲下来(在选择改变后的几毫秒),如果listview仍然没有任何选择,就把最后选择的行放回去。
private void listView1_SelectedIndexChanged(object sender, EventArgs e)
{
ListView lv = (ListView)sender;
if (lv.SelectedIndices.Count == 0)
{
if (!this.appIdleEventScheduled)
{
this.appIdleEventScheduled = true;
this.listViewToMunge = lv;
Application.Idle += new EventHandler(Application_Idle);
}
}
else
this.lastSelectedIndex = lv.SelectedIndices[0];
}
void Application_Idle(object sender, EventArgs e)
{
Application.Idle -= new EventHandler(Application_Idle);
this.appIdleEventScheduled = false;
if (listViewToMunge.SelectedIndices.Count == 0)
listViewToMunge.SelectedIndices.Add(this.lastSelectedIndex);
}
private bool appIdleEventScheduled = false;
private int lastSelectedIndex = -1;
private ListView listViewToMunge;
我知道这个问题问了10年了。但我也面临同样的问题,刚才找到了一个简单而优雅的解决方案,真心想分享一下。
有一个代码(在VB.NET中,但在C#中写同样的代码没有什么大问题)。
Public Class SettingsBox ' Form that contains ListView (lvScreen)
Private nScreenTracer As Integer
Private nSelectedScreen As Integer
Private Sub lvScreen_ItemSelectionChanged(sender As Object,
e As ListViewItemSelectionChangedEventArgs) Handles lvScreen.ItemSelectionChanged
If e.IsSelected Then nScreenTracer = e.Item.Index
End Sub
Private Sub lvScreen_MouseDown(sender As Object,
e As MouseEventArgs) Handles lvScreen.MouseDown
nScreenTracer = -1
End Sub
Private Sub lvScreen_MouseUp(sender As Object,
e As MouseEventArgs) Handles lvScreen.MouseUp
If nScreenTracer = -1 Then
lvScreen.SelectedIndices.Add(nSelectedScreen)
Else
nSelectedScreen = nScreenTracer
End If
End Sub
End Class
这个解决方案适用于单项选择,但也可以简单地用List(Of Integer)重新设计为多项选择。