【问题标题】:Winforms ListView Selection Drawing?Winforms ListView 选择绘图?
【发布时间】:2009-08-04 16:20:25
【问题描述】:

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

我想在选择的周围画一个轮廓来表示选择。

有什么办法吗?示例表示赞赏。

【问题讨论】:

    标签: c# .net winforms listview gdi+


    【解决方案1】:

    .NET ListView 比其他答案建议的更直接地支持所有者绘图。你甚至不需要子类。将 OwnerDraw 设置为 true,监听 DrawSubItem 事件,然后在该事件中您可以绘制您喜欢的内容。

    与往常一样,ObjectListView 使此过程更容易。 this page 记录了如何执行此操作。如果你对你的用户感到刻薄,你可以用这样的东西来装饰:

    但是,如果您想在单元格本身的边界之外绘制一些东西,这些技术都不起作用。因此,如果您希望在与前一行和后续行重叠的整行周围绘制一个选择轮廓,则无法通过所有者绘图来做到这一点。每个单元格都是单独绘制的,并“拥有”它所在的屏幕部分,从而清除所有已经存在的内容。

    要执行您要求的操作,您必须拦截自定义绘图的后期绘制阶段(不是所有者绘图。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;
        }
    }
    

    这给出了如下所示的内容:

    【讨论】:

    • 谢谢,您的控件看起来很酷。是商业的吗?不吹毛求疵,只是想知道。
    • 它是开源和 GPL 的。如果公司不喜欢 GPL,他们可以花几百美元购买商业许可证(对于小公司/个人来说更少)。
    • 此外,它实际上只是一个 .NET ListView,但具有许多巧妙的辅助函数,可以消除使用普通版本的痛苦。
    【解决方案2】:

    这是一个简单的工作示例,我正在搞砸。

    第一个辅助结构和枚举。

      [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;
            }
    

    这是结果。

    【讨论】:

    • 谢谢斯坦,我回家后会试试这个。非常感谢您的代码示例。
    • 我刚刚尝试过,但从未调用过 ProcessDrawItem。我放了一个断点,它没有被击中。如果有问题我用win7?
    • 我不确定它在 win7 上的行为是否相同,我不明白为什么不这样做。我也知道这很愚蠢,但是您是否在类的设计器部分将类型从 ListView 更改为 CustomListView。请记住构造函数调用 InitializeComponent() 在其中创建 ListView,如果您从工具栏中添加了一个列表视图,您将不得不在代码中将其更改为 CustomListView。
    【解决方案3】:

    我的第一个想法是继承 ListView 控件,将 OwnerDraw 设置为 true 并自己执行所有绘图,但对于这么小的更改来说,这似乎有点过头了。

    但是,在我的网络漫游中,我发现了这个article,这可能会有所帮助,因为它与您的情况非常相似,并且可以让您避免自己绘制所有内容。

    【讨论】:

    • 谢谢,试一试。令人惊讶的是,对于这样一件小事,它是如何做到的。我猜 Winforms 似乎对这些自定义不太灵活。
    猜你喜欢
    • 1970-01-01
    • 2013-10-27
    • 1970-01-01
    • 1970-01-01
    • 2013-01-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多