在寻找解决方法后,我意识到一旦选择一个项目就无法防止闪烁。我尝试使用一些ListView
消息但失败了。如果您想对此进行更多研究,我认为您应该注意一下LVM_SETITEMSTATE
,也许还有其他一些消息。毕竟,我想到了这个想法,我们要阻止用户选择一个项目。所以要伪造一个选定的项目,我们必须像这样进行一些自定义绘图和伪造:
public class CustomListView : ListView
{
public CustomListView(){
SelectedIndices = new List<int>();
OwnerDraw = true;
DoubleBuffered = true;
}
public new List<int> SelectedIndices {get;set;}
public int SelectedIndex { get; set; }
protected override void WndProc(ref Message m)
{
if (m.Msg == 0x1000 + 43) return;//LVM_SETITEMSTATE
else if (m.Msg == 0x201 || m.Msg == 0x202)//WM_LBUTTONDOWN and WM_LBUTTONUP
{
int x = m.LParam.ToInt32() & 0x00ff;
int y = m.LParam.ToInt32() >> 16;
ListViewItem item = GetItemAt(x, y);
if (item != null)
{
if (ModifierKeys == Keys.Control)
{
if (!SelectedIndices.Contains(item.Index)) SelectedIndices.Add(item.Index);
}
else if (ModifierKeys == Keys.Shift)
{
for (int i = Math.Min(SelectedIndex, item.Index); i <= Math.Max(SelectedIndex, item.Index); i++)
{
if (!SelectedIndices.Contains(i)) SelectedIndices.Add(i);
}
}
else
{
SelectedIndices.Clear();
SelectedIndices.Add(item.Index);
}
SelectedIndex = item.Index;
return;
}
}
else if (m.Msg == 0x100)//WM_KEYDOWN
{
Keys key = ((Keys)m.WParam.ToInt32() & Keys.KeyCode);
if (key == Keys.Down || key == Keys.Right)
{
SelectedIndex++;
SelectedIndices.Clear();
SelectedIndices.Add(SelectedIndex);
}
else if (key == Keys.Up || key == Keys.Left)
{
SelectedIndex--;
SelectedIndices.Clear();
SelectedIndices.Add(SelectedIndex);
}
if (SelectedIndex == VirtualListSize) SelectedIndex = VirtualListSize - 1;
if (SelectedIndex < 0) SelectedIndex = 0;
return;
}
base.WndProc(ref m);
}
protected override void OnDrawColumnHeader(DrawListViewColumnHeaderEventArgs e)
{
e.DrawDefault = true;
base.OnDrawColumnHeader(e);
}
protected override void OnDrawItem(DrawListViewItemEventArgs e)
{
i = 0;
base.OnDrawItem(e);
}
int i;
protected override void OnDrawSubItem(DrawListViewSubItemEventArgs e)
{
if (!SelectedIndices.Contains(e.ItemIndex)) e.DrawDefault = true;
else
{
bool isItem = i == 0;
Rectangle iBound = FullRowSelect ? e.Bounds : isItem ? e.Item.GetBounds(ItemBoundsPortion.ItemOnly) : e.SubItem.Bounds;
Color iColor = FullRowSelect || isItem ? SystemColors.HighlightText : e.SubItem.ForeColor;
Rectangle focusBound = FullRowSelect ? e.Item.GetBounds(ItemBoundsPortion.Entire) : iBound;
if(FullRowSelect || isItem) e.Graphics.FillRectangle(SystemBrushes.Highlight, iBound);
TextRenderer.DrawText(e.Graphics, isItem ? e.Item.Text : e.SubItem.Text,
isItem ? e.Item.Font : e.SubItem.Font, iBound, iColor,
TextFormatFlags.LeftAndRightPadding | TextFormatFlags.VerticalCenter);
if(FullRowSelect || isItem)
ControlPaint.DrawFocusRectangle(e.Graphics, focusBound);
}
i++;
base.OnDrawSubItem(e);
}
}
注意:上面的代码将禁用MouseDown
, MouseUp
(用于左键)和KeyDown
事件(用于箭头键),如果您想在 之外处理这些事件CustomListView
,您可能需要自己引发这些事件。(默认情况下,这些事件由 in 或 after 的某些代码引发base.WndProc
)。
还有一种情况是用户可以通过 选择项目holding mouse down and drag to select
。要禁用此功能,我认为我们必须捕获消息WM_NCHITTEST
,但我们必须在正确的条件下捕获和过滤它。我试过处理这个但没有运气。我希望你能做到。这只是一个演示。然而,正如我所说,我们似乎无法走另一条路。BUG
我认为你的问题是某种ListView
控制。
更新
事实上,我之前想过,Focused
但那Selected
是我尝试访问SelectedItem
with的时候ListView.SelectedItems
(这是错误的)。所以我没有尝试这种方法。但是在发现我们可以通过and访问虚拟模式下SelectedItem
的 a之后,我认为这个解决方案是最有效和最简单的解决方案:ListView
ListView.SelectedIndices
ListView.Items
int selected = -1;
bool suppressSelectedIndexChanged;
private void timer1_Tick(object sender, EventArgs e)
{
listView1.SuspendLayout();
if (selected > -1){
ListViewItem item = listView1.Items[selected];
Rectangle rect = listView1.GetItemRect(item.Index);
suppressSelectedIndexChanged = true;
item.Selected = item.Focused = !(rect.Top <= 2 || rect.Bottom >= listView1.ClientSize.Height-2);
suppressSelectedIndexChanged = false;
}
listView1.VirtualListSize++;
listView1.ResumeLayout(true);
}
private void listView1_SelectedIndexChanged(object sender, EventArgs e){
if (suppressSelectedIndexChanged) return;
selected = listView1.SelectedIndices.Count > 0 ? listView1.SelectedIndices[0] : -1;
}
注意:代码只是一个示例,用户只选择了一项,您可以添加更多代码来处理用户选择多于一项的情况。