我正在制作一个使用MultiSelect = false
控件的应用程序。在某些情况下,我需要阻止用户更改选定的项。我原以为这是一个简单的任务,但几个小时后,我仍在试图弄清楚发生了什么。
因此,为了能够选择“冻结”ListView的选择,我创建了一个自定义类CListView
,它继承自ListView。如果FreezeSelection
设置为true,那么每次用户更改选择时,我都会尝试将其改回来:
public class CListView : ListView
{
public bool FreezeSelection { get; set; } = false;
bool _applyingSelectionUpdates = false;
protected override void OnSelectedIndexChanged(EventArgs e)
{
if (FreezeSelection)
{
if (_applyingSelectionUpdates)
return;
// for simplicity consider that the selected index while the selection is frozen is always 2
int selectedIndex = 2;
_applyingSelectionUpdates = true;
try
{
SelectedIndices.Clear();
if (selectedIndex >= 0)
SelectedIndices.Add(selectedIndex);
}
finally { _applyingSelectionUpdates = false; }
return;
}
base.OnSelectedIndexChanged(e);
}
}
问题是当我将FreezeSelection
设置回false时,用户试图选择一个不同的项。首先,即使MultiSelect
为false,从视觉上看,它似乎选择了两个项。但从程序上看,当用户更改选择时,有时似乎选择了正确的项,有时没有选择任何项。
这个行为显然是一个bug,我怀疑是什么导致了这个bug。当用户点击一个项目时,SelectedIndexChanged
事件被触发了两次。一次是在SelectedIndices
集合被清除之后,第二次是在点击的项目被添加到所选项目的集合之后。我认为这个bug是由于在这两次事件之间改变了所选项目而引起的。但我需要了解更多关于这方面的信息。如果MultiSelect
为真,用户试图用Ctrl键选择项目,我没有问题。
要重现此错误,可以使用以下TestForm
:
public class TestForm : Form
{
CListView listView;
CheckBox checkBox;
public TestForm()
{
listView = new() { Dock = DockStyle.Fill, View = View.Details, FullRowSelect = true, MultiSelect = false };
listView.Columns.Add("col 1");
listView.SelectedIndexChanged += ListView_SelectedIndexChanged;
Controls.Add(listView);
checkBox = new() { Dock = DockStyle.Right, Text = "freeze selection" };
checkBox.CheckedChanged += CheckBox_CheckedChanged;
Controls.Add(checkBox);
listView.Items.Add("item 1");
listView.Items.Add("item 2");
listView.Items.Add("item 3");
listView.Items.Add("item 4");
}
private void CheckBox_CheckedChanged(object? sender, EventArgs e)
{
listView.FreezeSelection = checkBox.Checked;
}
DateTime lastSelChangeTime = DateTime.MinValue;
private void ListView_SelectedIndexChanged(object? sender, EventArgs e)
{
if ((DateTime.Now - lastSelChangeTime).TotalMilliseconds > 200)
Debug.WriteLine(""); // this is just to group together what happens on a single user interaction
var indices = listView.SelectedIndices.Cast<int>().ToArray();
Debug.WriteLine("CListView fired selection changed event! "
+ DateTime.Now.ToString("h:m:s:fff") + " "
+ "{ " + string.Join(", ", indices) + " }");
lastSelChangeTime = DateTime.Now;
}
}
如果运行此表单:
1.选择第三项(索引为2)
1.勾选“冻结选择”
1.单击第四项
1.取消选中“冻结选择”
1.现在尝试更改所选项并观察错误
问题是如何解决这个bug或者如何实现我的最初目标(防止用户选择不同的项目)。
**更新:**为了澄清,我所说的“bug”并不是我在一次选择更改中得到两个事件(我对此没有意见),而是在我“解冻”选定的索引后UI和ListView.SelectedIndices
之间的不一致行为。我将用下面的图片演示这个问题(注意,每个屏幕截图都是在我单击光标所在的位置后拍摄的;每次我得到SelectedIndexChanged
事件时,输出窗口也会显示SelectedIndices
):
我使用的是.NET 6.0。
1条答案
按热度按时间vu8f3i0k1#
正如其他人已经提到的,这里没有bug,如选择项目1,然后选择项目2的顺序所示(它首先通过取消选择项目1来更改选择)。
如果您不想让用户在执行某个任意任务(比如等待保存一个修改过的文档)时选择内容,为什么不在执行该任务时将
ListView.Enabled
设置为false
呢?在下面引用的测试代码中,我为复选框更改时设置了一个一体化的设置,将SelectionIndices
集合设置为'2',就像您的帖子中一样;现在,返回到
freeze selection
未选中的状态并选择某个新项目不会有任何问题。