我正在制作一個使用ListView帶有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是假的,在視覺上它也會顯示為選擇了兩個專案。但是以編程方式,當用戶更改選擇時,似乎有時選擇了正確的專案,有時沒有選擇專案。
這種行為顯然是一個錯誤,我懷疑是什么導致了這個錯誤。當用戶點擊一個專案時,該事件SelectedIndexChanged被觸發兩次。一次在SelectedIndices清除集合后,第二次在單擊的專案添加到選定專案的集合后。我認為該錯誤是由更改這兩個事件之間的選定專案引起的,但我需要對此了解更多。如果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;
}
}
如果您運行此表單:
- 選擇第三項(索引為 2)
- 檢查“凍結選擇”
- 點擊第四項
- 取消選中“凍結選擇”
- 現在嘗試更改所選專案并觀察錯誤
問題是如何解決這個錯誤或如何實作我的初始目標(防止用戶選擇不同的專案)。
更新:
澄清一下,我所說的“錯誤”并不是我為一次選擇更改獲得兩個事件(我對此很好),這是 UI 之間的不一致行為,并且ListView.SelectedIndices在我“解凍”之后選定的索引。我將用下圖演示問題(請注意,每個螢屏截圖都是在我單擊游標所在位置后拍攝的;輸出視窗也會顯示SelectedIndices每次我收到SelectedIndexChanged事件時的情況):

我使用.NET 6.0。
uj5u.com熱心網友回復:
正如其他人所提到的,這里沒有錯誤,如選擇專案 1,然后選擇專案 2 的順序所示(首先通過取消選擇專案 1 來更改選擇。

如果您不希望用戶在某些任意任務期間選擇事物(例如等待保存修改的檔案),為什么不在 您執行作業時設定ListView.Enabled為?false在下面參考的測驗代碼中,當復選框更改將集合設定SelectionIndices為“2”時,我做了一個一體化操作,如您的帖子中所示;

現在回到freeze selection未選中狀態并選擇一些新專案沒有問題。

public TestForm()
{
InitializeComponent();
listView.MultiSelect = false;
listView.Columns.Add("col 1");
for (int i = 1; i <= 4; i ) listView.Items.Add($"Item {i}");
listView.SelectedIndexChanged = (sender, e) =>
{
richTextBox.AppendLine(
$"{DateTime.Now} : [{string.Join(", ", listView.SelectedIndices.Cast<int>())}]" );
var sel =
listView
.SelectedItems
.Cast<ListViewItem>();
if (sel.Any())
{
foreach (var item in sel)
{
richTextBox.AppendLine(item);
richTextBox.AppendLine();
}
}
else richTextBox.AppendLine("No selections", Color.Salmon);
};
checkBox.CheckedChanged = (sender, e) =>
{
listView.Enabled = !checkBox.Checked;
if (checkBox.Checked) doWork();
};
void doWork()
{
listView.SelectedIndices.Clear();
listView.SelectedIndices.Add(2);
}
}
將此擴展用于 RichTextBox
static class Extensions
{
public static void AppendLine(this RichTextBox richTextBox) =>
richTextBox.AppendText($"{Environment.NewLine}");
public static void AppendLine(this RichTextBox richTextBox, object text) =>
richTextBox.AppendText($"{text}{Environment.NewLine}");
public static void AppendLine(this RichTextBox richTextBox, object text, Color color)
{
var colorB4 = richTextBox.SelectionColor;
richTextBox.SelectionColor = color;
richTextBox.AppendText($"{text}{Environment.NewLine}");
richTextBox.SelectionColor = colorB4;
}
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/506798.html
