65.9K
CodeProject 正在变化。 阅读更多。
Home

为 TreeView 和 ListView 控件生成缺失的 Paint 事件

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.77/5 (24投票s)

2003年7月15日

2分钟阅读

viewsIcon

178618

downloadIcon

1651

关于为TreeView和ListView生成缺失Paint事件的文章

引言

Microsoft .NET 窗体控件,如TreeViewListview,只是对ComCtl中控件的封装。因此,它们通常不会引发Paint事件。我所见到的唯一建议是设置ControlStyles.UserPaint样式,并自己完成所有绘制!

TreeViewWithPaint 控件

为了解决这个问题,使用了基于Bitmap的内部Graphics对象。它会在任何Resize期间重新创建。

//Recreate internal graphics object
protected override void OnResize( System.EventArgs e ) {
    if( internalBitmap == null  ||
        internalBitmap.Width != Width || internalBitmap.Height != Height ) {

        if( Width != 0 && Height != 0 ) {
            DisposeInternal();
            internalBitmap = new Bitmap( Width, Height );
            internalGraphics = Graphics.FromImage( internalBitmap );
        }
    }
}

当控件收到WM_PAINT时,将执行三个步骤

  1. ComCtl 通过 WM_PRINTCLIENT 消息绘制到内部 Graphics 对象中。
    //Draw Internal Graphics
    IntPtr hdc = internalGraphics.GetHdc();
    Message printClientMessage = Message.Create( Handle, 
         WM_PRINTCLIENT, hdc, IntPtr.Zero );  
    DefWndProc( ref printClientMessage );
    internalGraphics.ReleaseHdc( hdc );
  2. 现在使用从内部 Graphics 对象构造的 PaintEventArgs 触发 OnPaint()
    //Add the missing OnPaint() call
    OnPaint( new PaintEventArgs( internalGraphics, Rectangle.FromLTRB( 
        updateRect.left,
        updateRect.top,
        updateRect.right,
        updateRect.bottom ) ) );
  3. 内部 Graphics 对象的 Bitmap 复制到正常的屏幕 Graphics 设备。
    //Draw Screen Graphics
    screenGraphics.DrawImage( internalBitmap, 0, 0 );

此外,过滤掉了 WM_ERASEBKGND 以消除闪烁。

case WM_ERASEBKGND:
    //removes flicker
    return;

此外,添加了 Paint 事件以恢复可浏览的属性。

[
//Re-enable Attributes for the Paint Event
EditorBrowsableAttribute( EditorBrowsableState.Always ),
BrowsableAttribute(true)
]
public new event PaintEventHandler Paint {
    add   { base.Paint += value; }
    remove{ base.Paint -= value; }
}

Using the Code

要使用 TreeViewWithPaint 控件

  1. 只需将控件添加到工具箱即可。
  2. 将其拖放到您的窗体上。
  3. 将一个 Paint 处理程序附加到现在公开的 Paint 事件。

要创建一个 <AnotherComCtl>WithPaint,请按如下方式修改 TreeViewWithPaint

  1. 使用 <AnotherComCtl> 作为基类。
  2. <AnotherComCtl> 类的属性复制到 <AnotherComCtl>WithPaint 类。
  3. 添加一个 <AnotherComCtl>WithPaint.bmp,这是一个用于工具箱图标的 16x16 位图。

TreeViewWithPaint 控件测试平台

我创建了一个包含单个 TreeViewWithPaint 控件的简单 Form。现在可以使用 Paint 事件。

treeViewWithPaint1.Paint += new PaintEventHandler( 
       treeViewWithPaint1_Paint );

这个特定的 Paint 处理程序只是在选定的节点内部绘制一个简单的白色带。

//Add a simple yellow band inside the selected node
private void treeViewWithPaint1_Paint(object sender, PaintEventArgs e) {
    Graphics g = e.Graphics;
    TreeNode node = treeViewWithPaint1.SelectedNode;
    
    if( node != null && node.IsVisible ) {
        using( Pen pen = new Pen( Color.Yellow ) ) {
            g.DrawRectangle( pen, 
                node.Bounds.X + 1,
                node.Bounds.Y + 1,
                node.Bounds.Width  - 3,
                node.Bounds.Height - 3
                );
        }
    }
}

关注点

  • 所需的属性是通过使用 VB(而不是 C#)对象浏览器为 VS 2002(而不是 VS 2003)发现的。
  • XP 有一个未公开的功能,即如果您在设计时添加项目,则会在 TreeView 上显示水平滚动条。

许可证

本文未附加明确的许可证,但可能在文章文本或下载文件本身中包含使用条款。如有疑问,请通过下面的讨论区联系作者。

作者可能使用的许可证列表可以在此处找到。

© . All rights reserved.