是否可以覆盖listview默认选择的油漆?看起来半透明的蓝色覆盖在项目上的那个,就像在资源管理器窗口中一样。
我想在选择周围画一个轮廓来表示选择。
有什么办法吗?赞赏的例子。
这是一个快速工作的例子,我正在搞乱。
第一个辅助结构和枚举。
[StructLayout(LayoutKind.Sequential)]
public struct DRAWITEMSTRUCT
{
public int CtlType;
public int CtlID;
public int itemID;
public int itemAction;
public int itemState;
public IntPtr hwndItem;
public IntPtr hDC;
public RECT rcItem;
public IntPtr itemData;
}
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
public int left;
public int top;
public int right;
public int bottom;
public int Width
{
get { return right - left; }
}
public int Height
{
get { return bottom - top; }
}
}
public enum ListViewDefaults
{
LVS_OWNERDRAWFIXED = 0x0400
}
public enum WMDefaults
{
WM_DRAWITEM = 0x002B,
WM_REFLECT = 0x2000
}
现在创建一个自定义ListView和覆盖CreateParams和WndProc
public class CustomListView : ListView
{
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
//add OwnerDraw style...i took this idea from Reflecting against ListView
// bit OR is very important, otherwise you'll get an exception
cp.Style |= (int)ListViewDefaults.LVS_OWNERDRAWFIXED;
return cp;
}
}
protected override void WndProc(ref Message m)
{
base.WndProc(ref m);
//if we are drawing an item then call our custom Draw.
if (m.Msg == (int)(WMDefaults.WM_REFLECT | WMDefaults.WM_DRAWITEM))
ProcessDrawItem(ref m);
}
现在是最重要的部分......绘图。我在画画时非常业余,但这应该让你知道该怎么做。
private void ProcessDrawItem(ref Message m)
{
DRAWITEMSTRUCT dis = (DRAWITEMSTRUCT)Marshal.PtrToStructure(m.LParam, typeof(DRAWITEMSTRUCT));
Graphics g = Graphics.FromHdc(dis.hDC);
ListViewItem i = this.Items[dis.itemID];
Rectangle rcItem = new Rectangle(dis.rcItem.left, dis.rcItem.top, this.ClientSize.Width, dis.rcItem.Height);
//we have our rectangle.
//draw whatever you want
if (dis.itemState == 17)
{
//item is selected
g.FillRectangle(new SolidBrush(Color.Red), rcItem);
g.DrawString(i.Text, new Font("Arial", 8), new SolidBrush(Color.Black), new PointF(rcItem.X, rcItem.Y+1));
}
else
{
//regular item
g.FillRectangle(new SolidBrush(Color.White), rcItem);
g.DrawString(i.Text, new Font("Arial", 8), new SolidBrush(Color.Black), new PointF(rcItem.X, rcItem.Y+1));
}
//we have handled the message
m.Result = (IntPtr)1;
}
这是结果。
.NET ListView支持所有者绘图比其他答案建议更直接。你甚至不需要子类。将OwnerDraw设置为true,侦听DrawSubItem事件,然后在该事件中您可以绘制您喜欢的内容。
与往常一样,ObjectListView使这个过程更容易。有this page记录严格如何做到这一点。如果你对用户有意义,你可以像这样设计:
但是,如果你想在细胞本身的范围之外绘制一些东西,这些技术都不会起作用。因此,如果您希望在整个行周围绘制一个与前一行和后续行重叠的选择轮廓,则无法通过所有者绘制来执行此操作。每个单元格都是单独绘制的,并“拥有”它的部分屏幕,擦除已经存在的任何内容。
要做一些像你要求的事情,你将不得不截取自定义绘制的postpaint阶段(不是所有者绘制.Michael Dunn wrote a great introduction为CodeProject的自定义绘图)。你可以阅读here所需的内容。
我讨厌说,但最简单的答案是使用ObjectListView,创建一个装饰并安装它:
public void InitializeSelectionOverlay()
{
this.olv1.HighlightForegroundColor = Color.Black;
this.olv1.HighlightBackgroundColor = Color.White;
this.olv1.AddDecoration(new SelectedRowDecoration());
}
public class SelectedRowDecoration : IOverlay
{
public void Draw(ObjectListView olv, Graphics g, Rectangle r) {
if (olv.SelectedIndices.Count != 1)
return;
Rectangle rowBounds = olv.GetItem(olv.SelectedIndices[0]).Bounds;
rowBounds.Inflate(0, 2);
GraphicsPath path = this.GetRoundedRect(rowBounds, 15);
g.DrawPath(new Pen(Color.Red, 2.0f), path);
}
private GraphicsPath GetRoundedRect(RectangleF rect, float diameter) {
GraphicsPath path = new GraphicsPath();
RectangleF arc = new RectangleF(rect.X, rect.Y, diameter, diameter);
path.AddArc(arc, 180, 90);
arc.X = rect.Right - diameter;
path.AddArc(arc, 270, 90);
arc.Y = rect.Bottom - diameter;
path.AddArc(arc, 0, 90);
arc.X = rect.Left;
path.AddArc(arc, 90, 90);
path.CloseFigure();
return path;
}
}
我的第一个想法是将ListView控件子类化,将OwnerDraw设置为true并自己执行所有绘图,但这对于这么小的改变来说似乎有些过分。
然而,在我的网络漫游中,我发现这个article,这可能会有所帮助,因为它非常类似于你的情况,并允许你自己避免绘制一切。