@Dimitry - 感谢 C# 信息。这个问题确实帮助我开始将自定义图形放到自定义控件上。但是,我使用的是 Visual Basic .NET,所以这里有更多细节和 VB 视角。图形放置在表面上的 Zorder 也很关键。这是因为无法改变 .GrahicsPath 和 .FillRectangle 等 Drawing2D 对象的 z 顺序;只能更改控件的 Zorder。要隐藏现有控件的一部分并在其上绘制箭头,步骤如下。整个 Method 应该放在 Control 的 Paint Event 中;然后需要注意确保在重绘控件时触发绘制事件(这是另一个故事!):
- 创建一个附加到控件的 Graphics 对象
- 刷新控件
- 填充一个矩形以隐藏控件的部分现有信息。
- 创建并设置所需的形状作为剪贴蒙版(在下面的代码示例中,箭头)
- 用所需的颜色/图像或所需的任何内容填充形状。
在下面的示例中,在基于 ListView 的自定义控件中应用,一个箭头被创建并放置在标题的左侧。它会遮盖不需要的 CheckBox,将所需的复选框留在视图中,如下所示:
自您发布问题和上一个答案以来已经过去了几年。也许从那时起添加了现在在 .NET 帮助中的好信息。找到它是here,并提供更多详细信息和链接。
Protected Overrides Sub OnPaint(ByVal e As System.Windows.Forms.PaintEventArgs) 'This is traps the default OnPaint for a Component, so it starts by calling the Base's Paint Event
MyBase.OnPaint(e)
'Add your custom paint code here - in this example, a Green Arrow
AddArrowOverHeaderCheckBox()
End Sub
Private Sub AddArrowOverHeaderCheckBox()
'Adds an Arrow that is placed over the Checkbox of the 0th row, of a ListView, (over a Row that is used as a Header in this Custom Control).
'Create a Rectangle into which to put the arrow
Dim G As Graphics = Me.CreateGraphics 'Attach a Graphics Object to the Base
Dim lvsi As ListViewItem.ListViewSubItem = Me.Items(0).SubItems(0) 'Get the first row; it is being used as a header in this case
Dim CheckBoxWidth As Integer = Me.Bounds.X 'Calculate the size of the Control's CheckBox
Dim CheckBoxHeight As Integer = lvsi.Bounds.Height
Dim InflateBy As Integer = 1 'Optional amount to resize the area in which the Arrow is to be placed
Me.Refresh() 'Refresh the ListViewTab so that it does not overpaint the arrow that is about to be created at the End Sub
Dim I As Integer = 0
I = (CheckBoxHeight + 1) Mod 2
Dim R As Rectangle = New Rectangle(lvsi.Bounds.X + Me.Margin.Left, lvsi.Bounds.Top, CheckBoxWidth, CheckBoxHeight + I) 'Make sure the height is an Odd number, to make a tidy arrow
'myBrush.Color = Color.Red
R.Inflate(InflateBy * 2, InflateBy) 'Create a covering rectangle that is larger than the CheckBox on the Left of the CheckView Item in both dimensions
Dim myBrush As New System.Drawing.SolidBrush(Me.BackColor)
G.FillRectangle(myBrush, R) 'Draw a filled Rectangle over the CheckBox in the Background colour for the ListViewTab to obscure the CheckBox
Dim RW = R.Width
Dim RH = R.Height
I = CSng(RH) * 0.5 \ 2 'Set a proportion of the arrow's width for its shaft, here 50%
If RH Mod 2 <> 0 Then RH += 1 'Ensure that the Height is an Odd number to make the Arrow axially symmetric
' 'Create a polygon in an Arrow shape that fits inside the Rectangle
Dim polyArrow As Point() = {New Point(R.X + 0, I + R.Y), New Point(R.X + RW * 0.4, I + R.Y), New Point(R.X + RW * 0.4, 0 + R.Y), New Point(R.X + RW, RH * 0.5 + R.Y),
New Point(R.X + RW * 0.4, RH + R.Y), New Point(R.X + RW * 0.4, RH - I + R.Y), New Point(R.X + 0, RH - I + R.Y)}
Dim path As New Drawing2D.GraphicsPath() 'Create a Path shaped like an Arrow & sized proportionately to the size of the required Rectangle
path.AddPolygon(polyArrow) 'Turn the Path into a polygon
Dim [reg] As New [Region](path) 'Define a Graphics Region of the shape of the Arrow
G.SetClip([reg], combineMode:=Drawing2D.CombineMode.Replace) 'Redefine the area; clipping will now prevent any drawing outside the area of the Arrow
myBrush.Color = Color.Green
G.FillRectangle(myBrush, R) 'Draw a filled Green Rectangle, clipped to the Path shape
'Dim pen As Pen = Pens.Black 'Optionally: Draw a fine boundary in black
'G.DrawPath(pen, path) 'Optionally: Draw a fine boundary in black
G.SetClip(Me.ClientRectangle) 'Remove the Clip from the Graphics area - in case G is re-used
myBrush.Dispose() 'Tidy up
G.Dispose()
End Sub