Đây là giải pháp hoàn chỉnh thay đổi giải pháp được cung cấp bởi Matthew M. ở trên.
Nó cung cấp cải tiến cũng như một chút chức năng bổ sung.
Cải thiện: - nhấp chuột trái vào điều khiển để tập trung vào điều khiển. - hành vi nhấp chuột phải là nhất quán (chọn một lần)
Chức năng được thêm: - điều khiển có thuộc tính (MultiSelectionLimit) cho phép bạn đặt giới hạn số lượng mục có thể được chọn cùng một lúc.
Sau lần đăng đầu tiên, tôi đã nhận ra một vấn đề nhỏ với mã. Việc xóa nhiều lựa chọn sẽ dẫn đến sự kiện ItemSelectionChanged được gọi nhiều lần. Tôi không thể tìm ra cách nào để tránh điều này với thừa kế hiện tại, vì vậy thay vào đó tôi đã sử dụng một giải pháp mà thuộc tính bool SelectionsBeingCleared sẽ đúng trong khi cho đến khi tất cả các mục đã chọn đã được bỏ chọn. Bằng cách này, một cuộc gọi đơn giản đến thuộc tính đó sẽ làm cho nó có thể tránh được việc cập nhật hiệu ứng cho đến khi tất cả các lựa chọn đã được xóa.
public class ListViewMultiSelect : ListView
{
public const int WM_LBUTTONDOWN = 0x0201;
public const int WM_RBUTTONDOWN = 0x0204;
private bool _selectionsBeingCleared;
/// <summary>
/// Returns a boolean indicating if multiple items are being deselected.
/// </summary>
/// <remarks> This value can be used to avoid updating through events before all deselections have been carried out.</remarks>
public bool SelectionsBeingCleared
{
get
{
return this._selectionsBeingCleared;
}
private set
{
this._selectionsBeingCleared = value;
}
}
private int _multiSelectionLimit;
/// <summary>
/// The limit to how many items that can be selected simultaneously. Set value to zero for unlimited selections.
/// </summary>
public int MultiSelectionLimit
{
get
{
return this._multiSelectionLimit;
}
set
{
this._multiSelectionLimit = Math.Max(value, 0);
}
}
public ListViewMultiSelect()
{
this.ItemSelectionChanged += this.multiSelectionListView_ItemSelectionChanged;
}
public ListViewMultiSelect(int selectionsLimit)
: this()
{
this.MultiSelectionLimit = selectionsLimit;
}
private void multiSelectionListView_ItemSelectionChanged(object sender, ListViewItemSelectionChangedEventArgs e)
{
if (e.IsSelected)
{
if (this.MultiSelectionLimit > 0 && this.SelectedItems.Count > this.MultiSelectionLimit)
{
this._selectionsBeingCleared = true;
List<ListViewItem> itemsToDeselect = this.SelectedItems.Cast<ListViewItem>().Except(new ListViewItem[] { e.Item }).ToList();
foreach (ListViewItem item in itemsToDeselect.Skip(1)) { item.Selected = false; }
this._selectionsBeingCleared = false;
itemsToDeselect[0].Selected = false;
}
}
}
protected override void WndProc(ref Message m)
{
switch (m.Msg)
{
case WM_LBUTTONDOWN:
if (this.SelectedItems.Count == 0 || !this.MultiSelect) { break; }
if (this.MultiSelectionLimit > 0 && this.SelectedItems.Count > this.MultiSelectionLimit) { this.ClearSelections(); }
int x = (m.LParam.ToInt32() & 0xffff);
int y = (m.LParam.ToInt32() >> 16) & 0xffff;
ListViewHitTestInfo hitTest = this.HitTest(x, y);
if (hitTest != null && hitTest.Item != null) { hitTest.Item.Selected = !hitTest.Item.Selected; }
this.Focus();
return;
case WM_RBUTTONDOWN:
if (this.SelectedItems.Count > 0) { this.ClearSelections(); }
break;
}
base.WndProc(ref m);
}
private void ClearSelections()
{
this._selectionsBeingCleared = true;
SelectedListViewItemCollection itemsToDeselect = this.SelectedItems;
foreach (ListViewItem item in itemsToDeselect.Cast<ListViewItem>().Skip(1)) { item.Selected = false; }
this._selectionsBeingCleared = false;
this.SelectedItems.Clear();
}
}
Tôi đã tự mình làm việc theo những dòng này nhưng muốn kiểm tra xem có điều gì dễ dàng hơn không trước khi tôi tiếp tục khắc phục. Cảm ơn câu trả lời của bạn. – Evan
Đảm bảo rằng danh sách các mục đã chọn lưu trữ "nội dung" chứ không phải chỉ mục. Sử dụng chỉ mục dễ dàng hơn nhưng điều đó có nghĩa là bạn cần giữ cho ListBox.ItemsCollection và danh sách của bạn được đồng bộ hóa. Hy vọng rằng đối tượng hoặc chuỗi của bạn là duy nhất đủ để giữ mối quan hệ này! –