C# Winforms列表视图选择图形?

C# Winforms列表视图选择图形?,c#,.net,winforms,listview,gdi+,C#,.net,Winforms,Listview,Gdi+,是否可以覆盖listview detault选择绘制?一个看起来半透明的蓝色覆盖在项目上,就像在资源管理器窗口中一样 我想在所选内容周围画一个轮廓来表示所选内容 有办法吗?示例非常感谢。我的第一个想法是对ListView控件进行子类化,将OwnerDraw设置为true,然后自己执行所有绘图,但是对于这样一个小的更改来说,这似乎有些过分了 然而,在我的网络漫游中,我发现了这一点,这可能会有所帮助,因为它与您的情况非常相似,允许您避免自己绘制所有内容。我的第一个想法是将ListView控件子类化,

是否可以覆盖listview detault选择绘制?一个看起来半透明的蓝色覆盖在项目上,就像在资源管理器窗口中一样

我想在所选内容周围画一个轮廓来表示所选内容


有办法吗?示例非常感谢。

我的第一个想法是对ListView控件进行子类化,将OwnerDraw设置为true,然后自己执行所有绘图,但是对于这样一个小的更改来说,这似乎有些过分了


然而,在我的网络漫游中,我发现了这一点,这可能会有所帮助,因为它与您的情况非常相似,允许您避免自己绘制所有内容。

我的第一个想法是将ListView控件子类化,将OwnerDraw设置为true,然后自己执行所有绘制,但对于这样一个小小的变化来说,这似乎是矫枉过正


然而,在我的网络漫游中,我发现了这一点,这可能会有所帮助,因为它与您的情况非常相似,允许您避免自己绘制所有内容。

这里是一个快速工作的示例,我正在胡乱处理

第一个助手结构和枚举

  [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并覆盖CreateParamsWndProc

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;
        }
这就是结果


这里是一个快速工作的示例,我正在胡乱处理

第一个助手结构和枚举

  [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并覆盖CreateParamsWndProc

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事件,然后在该事件中可以绘制您喜欢的内容

和往常一样,它使这一过程更加容易。如何做到这一点,有严格的文档记录。如果你觉得对你的用户很卑鄙,你可以把这样的东西弄得花枝招展:

但是,如果您想在单元格本身的边界之外绘制一些东西,这些技术都不起作用。因此,如果希望围绕与上一行和后续行重叠的整行绘制选择轮廓,则不能通过所有者绘制来完成此操作。每个单元格都是单独绘制的,并“拥有”屏幕的一部分,清除已经存在的任何内容

要执行您要求的操作,您必须截取自定义绘图的后期阶段(而不是所有者绘图。Michael Dunn为CodeProject自定义绘图)。你可以阅读所需内容

我不想这么说,但最简单的答案是使用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;
    }
}
这会产生如下结果:
.NET ListView比其他答案更直接地支持所有者绘图。您甚至不需要子类化。将OwnerDraw设置为true,侦听DrawSubItem事件,然后在该事件中可以绘制您喜欢的内容

和往常一样,它使这一过程更加容易。如何做到这一点,有严格的文档记录。如果你觉得对你的用户很卑鄙,你可以把这样的东西弄得花枝招展:

但是,如果您想在单元格本身的边界之外绘制一些东西,这些技术都不起作用。因此,如果希望围绕与上一行和后续行重叠的整行绘制选择轮廓,则不能通过所有者绘制来完成此操作。每个单元格都是单独绘制的,并“拥有”屏幕的一部分,清除已经存在的任何内容

要执行您要求的操作,您必须截取自定义绘图的后期阶段(而不是所有者绘图。Michael Dunn为CodeProject自定义绘图)。你可以阅读所需内容

我不想这么说,但最简单的答案是使用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;
    }
}
这会产生如下结果:

谢谢,我将尝试一下。令人惊讶的是,这样一件小事竟然如此。我想Winforms在这些定制方面似乎不是很灵活。谢谢,我会尝试一下。令人惊讶的是,这样一件小事竟然如此。我想Winforms在这些定制方面似乎不是很灵活。谢谢Stan,我回家后会试试的。非常感谢您的代码示例。我刚刚尝试过,但从未调用ProcessDrawItem。我设置了一个断点,但没有命中。如果出现问题,我会使用win7?我不确定它是否会在win7上以同样的方式运行,我不明白为什么不能。我也知道这很愚蠢,但是您是否在类的设计器部分将类型从ListView更改为CustomListView。记住构造函数调用InitializeComponent()在其中创建ListView,如果您从工具栏添加了ListView,则必须在代码中将其更改为CustomListView。谢谢Stan,我回家后会尝试。非常感谢您的代码示例。我刚刚尝试过,但从未调用ProcessDrawItem。我设置了一个断点,但没有命中。如果出现问题,我会使用win7?我不确定它是否会在win7上以同样的方式运行,我不明白为什么不能。我也知道这很愚蠢,但是您是否在类的设计器部分将类型从ListView更改为CustomListView。记住构造函数调用InitializeComponent()在其中创建ListView,如果从工具栏添加ListView,则必须在代码中将其更改为CustomListView。谢谢,您的控件看起来很酷。它是商业性的吗?不是吹毛求疵,只是好奇。它是开源和GPL’ed。如果一家公司不喜欢GPL,他们可以花几百美元(小公司/个人更少)购买商业许可证。此外,它实际上只是一个.NET ListView,但有很多聪明的助手功能