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

带渐变色和额外图像的自定义按钮控件 (VB.NET)

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.91/5 (122投票s)

2008年6月3日

CPOL

7分钟阅读

viewsIcon

891689

downloadIcon

47341

这是一个易于使用的自定义按钮控件,但具有许多视觉设计选项。

引言

CButton 是一个用 VB.NET 编写的简单自定义按钮控件。抱歉,我刚开始时没有意识到“C”前缀是特定于语言的。它只是 Custom Button 的缩写。我不会再让这种事情发生。这是普通 Microsoft 按钮的一个很好的替代方案。


对 3.4 及更早版本的现有用户的重要提示。

从 3.5 版开始,不再需要 DesignerRectTracker 类,并且需要对使用旧控件并即将使用新控件的现有项目进行一些设计器清理。(有关更多详细信息,请参阅下文)


以下是主要功能列表

  1. 更改按钮的形状
  2. 调整圆角的圆角矩形
  3. 纯色和多色填充
  4. 文本阴影和边距
  5. 可调的鼠标悬停和点击颜色更改,并带有模拟点击移动
  6. 普通按钮图像和/或附加的侧边图像

我在 1.2 版中通过添加 UITypeEditorsControlDesigners 来改进了设计时编辑。请参阅 UITypeEditorsDemo[^] 以获得详细解释。这使我能够将某些属性合并到一个属性中,或改进其他属性。

例如

  • ButtonColorAButtonColorBButtonColorC 被合并到 ColorFillBlend 中,允许无限的颜色混合,而不是仅限于三种颜色
  • ButtonColorCenterPointButtonColorCenterPtOffset 被合并到 FocalPoints
  • CornerRadius 成为一个可扩展属性 Corners,允许单独调整每个角

背景

这是另一个需要(好吧,老实说,一个普通按钮也可以,但我想要)一个更好看、视觉效果更强的按钮的例子。已经有很多很棒的按钮控件了,但没有我想要的全部功能。基本上,我创建了我想要的功能,然后重写了 OnPaint 来按照我想要的方式绘制它。

控件属性

以下是主要属性列表

  • 形状

    按钮是什么形状(矩形、椭圆形、三角形)

  • ColorFillBlend

    渐变填充中使用的颜色

  • BorderColor, BorderShow

    以不同颜色显示边框

  • FillType

    使用哪种颜色渐变类型(纯色、线性或路径)

  • FocalPoints

    路径渐变类型填充的CenterPointFocusScales

  • 角点

    每个圆角弧的半径

  • DimFactorHover, DimFactorClick

    鼠标悬停和点击时调整按钮颜色的数字

  • TextShadow, TextShadowShow

    显示或不显示文本阴影以及阴影的颜色

  • TextMargin

    将文本推离按钮内边缘

  • TextSmoothingMode

    设置 Graphics 对象以获得更美观文本的TextRenderingHint

  • SideImage

    添加一个可以放置在按钮表面之外的额外图像

  • 填充

    ButtonArea 的边缘偏移到控件边缘

Using the Code

一旦您按照期望的方式设计了 CButton,就没有什么复杂的代码了。只需像普通按钮一样使用它。

由于 Click 事件会在您点击控件的任何位置触发,因此我添加了两个新 Events;1.1 版中的 ClickButtonArea 事件仅在鼠标点击控件的按钮部分时触发,2.0 版中的 SideImageClicked 事件。

    'Add a new Click event for only when the ButtonArea or SideImage is Clicked
    Public Event ClickButtonArea(ByVal Sender As Object, _
        ByVal e As System.Windows.Forms.MouseEventArgs)
    Public Event SideImageClicked(ByVal Sender As Object, _
        ByVal e As System.Windows.Forms.MouseEventArgs)


Private Sub CButton_MouseUp(ByVal sender As Object, _
    ByVal e As System.Windows.Forms.MouseEventArgs) Handles Me.MouseUp
    If MouseDrawState = eMouseDrawState.Down _
        Then RaiseEvent ClickButtonArea(Me, New EventArgs)
    MouseDrawState = eMouseDrawState.Up
    Me.Invalidate(ButtonArea)
End Sub

关注点

控件只是一个将各个部分按正确位置分层处理的过程。有四个主要区域需要跟踪。控件区域、按钮区域、文本区域和图像区域。按钮区域通过控件内边距值从控件区域中减去。文本区域通过文本边距值和图像区域从按钮区域中减去。图像区域基于图像大小。项目根据其布局选项放置到这些区域中。

Protected Overrides Sub _
		OnPaint(ByVal e As System.Windows.Forms.PaintEventArgs)
		e.Graphics.SmoothingMode = SmoothingMode.AntiAlias
		e.Graphics.TextRenderingHint = _TextSmoothingMode
		'Gray the Text and Border Colors if Disabled
		Dim bColor, tColor, tsColor As Color
		If Enabled Then
			bColor = _BorderColor
			tColor = ForeColor
			tsColor = _TextShadow
		Else
			bColor = GrayTheColor(_BorderColor)
			tColor = GrayTheColor(ForeColor)
			tsColor = GrayTheColor(_TextShadow)
		End If

		Using MyPen As New Pen(bColor)
			MyPen.Alignment = PenAlignment.Inset

			'Shrink the Area so the Border draws correctly, _
			'then trim off the Padding to get the button surface area
			ButtonArea = AdjustRect(New RectangleF(0, 0, _
				Size.Width - 1, Size.Height - 1), Padding)

			'Create the ButtonArea Path
			Using gp As GraphicsPath = GetPath()

				If BackgroundImage Is Nothing Then

					'Color the ButtonArea with the right Brush
					Select Case FillType
						Case eFillType.Solid
							Using br As Brush = New SolidBrush(GetFill)
								e.Graphics.FillPath(br, gp)
							End Using

						Case eFillType.GradientPath
							Using br As PathGradientBrush = _
                                                                          New PathGradientBrush(gp)
								Dim cb As New ColorBlend
								cb.Colors = GetFillBlend()
								cb.Positions = ColorFillBlend.iPoint

								br.FocusScales = _
                                                                     FocalPoints.FocusScales
								br.CenterPoint = New PointF( _
									Padding.Left + _
                                                                        ButtonArea.Width * _
                                                                        FocalPoints.CenterPoint.X, _
									Padding.Top + _
                                                                        ButtonArea.Height * _
                                                                        FocalPoints.CenterPoint.Y)
								br.InterpolationColors = cb

								e.Graphics.FillPath(br, gp)
							End Using

						Case eFillType.GradientLinear
							Using br As LinearGradientBrush = _
                                                                New LinearGradientBrush( _
							  ButtonArea, Color.White, Color.White, _
                                                                FillTypeLinear)
								Dim cb As New ColorBlend
								cb.Colors = GetFillBlend()
								cb.Positions = ColorFillBlend.iPoint
								'MsgBox(cb.Colors.Length & _
                                                                cb.Positions.Length)
								br.InterpolationColors = cb

								e.Graphics.FillPath(br, gp)
							End Using

					End Select
				End If

				If MyBase.Focused AndAlso ShowFocus = eFocus.Dot Then
					Using focusPen As Pen = New Pen(Brushes.Black, 1) _
                                             With {.DashStyle = DashStyle.Dot}
						e.Graphics.DrawPath(focusPen, GetPath(-1, -1))
					End Using
				End If

				If BorderShow Then
					e.Graphics.DrawPath(MyPen, gp)
				End If

			End Using

			Dim ipt As PointF = ImageLocation(GetStringFormat(SideImageAlign), _
				Size, SideImageSize)

			rectSideImage = New Rectangle(CInt(ipt.X), CInt(ipt.Y), _
				SideImageSize.Width, SideImageSize.Height)

			'Put the SideImage behind the Text
			If SideImageBehindText AndAlso SideImage IsNot Nothing Then
				If Enabled Then
					e.Graphics.SmoothingMode = SmoothingMode.None
					e.Graphics.DrawImage(SideImage, ipt.X, ipt.Y, _
						SideImageSize.Width, SideImageSize.Height)
				Else
					ControlPaint.DrawImageDisabled(e.Graphics, _
					New Bitmap(SideImage, SideImageSize.Width, _
					SideImageSize.Height), _
					CInt(ipt.X), CInt(ipt.Y), BackColor)
				End If
			End If

			'Layout the Text and Image on the button surface
			SetImageAndText(e.Graphics)

			If Not Image Is Nothing Then
				If Enabled Then
					e.Graphics.DrawImage(Image, Imagept.X, Imagept.Y, _
						ImageSize.Width, ImageSize.Height)
				Else
					ControlPaint.DrawImageDisabled(e.Graphics, _
						New Bitmap(Image, ImageSize.Width, ImageSize.Height), _
						CInt(Imagept.X), CInt(Imagept.Y), BackColor)
				End If
			End If

			'Draw the Text and Shadow
			If TextShadowShow Then
				TextArea.Offset(1, 1)
				e.Graphics.DrawString(Text, Font, _
					New SolidBrush(tsColor), TextArea, GetStringFormat(TextAlign))
				TextArea.Offset(-1, -1)
			End If
			e.Graphics.DrawString(Text, Font, _
				New SolidBrush(tColor), TextArea, GetStringFormat(TextAlign))

			'Put the SideImage in front of the Text
			If Not SideImageBehindText AndAlso Not SideImage Is Nothing Then
				If Enabled Then
					e.Graphics.SmoothingMode = SmoothingMode.None
					e.Graphics.DrawImage(SideImage, ipt.X, ipt.Y, _
						SideImageSize.Width, SideImageSize.Height)
				Else
					ControlPaint.DrawImageDisabled(e.Graphics, _
					New Bitmap(SideImage, _
					SideImageSize.Width, SideImageSize.Height), _
					CInt(ipt.X), CInt(ipt.Y), BackColor)
				End If
			End If

		End Using
    End Sub

为了在控件禁用时将其颜色变灰,我使用了两种方法

要使单个颜色变灰,我使用了这个 Function

Function GrayTheColor(ByVal GrayColor As Color) As Color
   Dim gray As Integer = _
    CInt(GrayColor.R * 0.3 + GrayColor.G * 0.59 + GrayColor.B * 0.11)
   Return Color.FromArgb(GrayColor.A, gray, gray, gray)
End Function

使图像变灰,我最初使用了这个 Function

Private Function EnableDisableImage(ByVal img As Image) As Bitmap
    If Me.Enabled Then Return img
    Dim bm As Bitmap = New Bitmap(img.Width, img.Height)
    Dim g As Graphics = Graphics.FromImage(bm)
    Dim cm As ColorMatrix = New ColorMatrix(New Single()() _
         {New Single() {0.5, 0.5, 0.5, 0, 0}, _
        New Single() {0.5, 0.5, 0.5, 0, 0}, _
        New Single() {0.5, 0.5, 0.5, 0, 0}, _
        New Single() {0, 0, 0, 1, 0}, _
        New Single() {0, 0, 0, 0, 1}})

    Dim ia As ImageAttributes = New ImageAttributes()
    ia.SetColorMatrix(cm)
    g.DrawImage(img, New Rectangle(0, 0, img.Width, img.Height), 0, 0, _
        img.Width, img.Height, GraphicsUnit.Pixel, ia)
    g.Dispose()
    Return bm

End Function

在 2.0 版中,我切换到了 ControlPaint 函数,因为我认为图像效果更好。

ControlPaint.DrawImageDisabled(e.Graphics, Me.Image, _
    CInt(Imagept.X), CInt(Imagept.Y), Me.BackColor)

圆角、内边距和焦点

3.5 版新增功能,在 ControlDesigner 中添加了 Adorners 和 Glyphs。有关 WinForms 的 Adorners Glyphs Behavior 和 ControlDesigner 的说明,请访问此链接。

圆角、按钮区域(内边距)、文本边距和焦点点都可以在 PropertyGrid 中编辑。这可能有些繁琐且不直观。通过使用 Adorners 和 Glyphs,这些属性可以直接在控件上用鼠标编辑。

ChooseAdorner

选择控件将在控件的右上角附近显示三个按钮。

Corner Adorner

点击 (C)orner 按钮显示五个圆角符号。前后移动四个圆角符号以单独调整每个圆角。移动中心符号以将所有四个圆角调整到相同值。

FocalPoints Adorner

点击 (F)ocalPoints 按钮获取类似于旧版本的 Center 和 FocalScale 符号。仅当 FillTypeGradientPath 时才有效。控件上会出现一个小的方形和圆形选择器。拖动圆圈移动 CenterPoint,拖动正方形移动 FocusScales。按住 Shift 键并单击符号以将 Center 重置为按钮的中间或将 FocusScale 重置为 0

Padding Margin Adorner

点击 (P)adding 来调整 ButtonArea (Padding Property) 和 TextMargin Property。虚线将标出边距的位置。拖动两侧进行调整。如果两个边距直接重叠,则 Padding 会首先被选择。按住 Shift 键以强制选择 TextMargin

.

3.5 版及更高版本的重要升级说明

此控件已存在很长时间,根据下载和评论,已有相当多的人在使用它。Adorner 升级使 DesignerRectTracker 类不再必要。如果您在项目中使用 3.5 版,而之前版本中有一个旧的 CButton,您将在任何包含旧 CButton 的 Form 中遇到大量错误。

这非常繁琐,但您需要删除所有有错误的代码行,以消除 Form 的 Designer 中对旧跟踪器的所有引用。

如果有人知道更轻松或批量清理这些的方法,我非常乐意知道。

到目前为止,我找到的最快方法是打开一个窗体设计器窗口,搜索 (Ctrl-F) DesignerRectTracker。然后按 F3 查找,按 Ctrl-L 删除代码行。在我掌握了节奏之后,它进行得相当快。

ColorFillBlend 的 TypeConverter

2.0 版新增功能,ColorFillBlendTypeConverter 可以将混合的字符串表示形式转换为 cBlendItems,以便进行更精确的调整,并轻松地将混合从一个 cButton 复制到另一个。

历史

  • 版本 1.0 - 2008 年 5 月
  • 版本 1.1 - 2008 年 7 月
    • 更新了“可点击区域”并添加了 ClickButtonArea 事件
  • 版本 1.2 - 2008 年 9 月
    • 更新了属性以使用 UITypeEditorsControlDesigner
    • 添加了三角形按钮
  • 版本 1.3 - 2008 年 9 月
    • 按钮区域点击修复。鼠标移动时按钮未点击
  • 版本 1.4 - 2008 年 10 月
    • 添加了启用/禁用功能
  • 版本 1.5 - 2008 年 12 月
    • 修复了 FocalPointsCorners 属性,以便在子属性在 PropertyGrid 中更改时立即刷新控件
    • 删除了 FocalPoints 的模态 UITypeEditor,并增加了在设计图面上直接调整点
  • 版本 1.6 - 2009 年 1 月
    • 根据 NfErNo 的建议,添加了助记符键。
  • 版本 1.7 - 2009 年 4 月
    • 添加了 SendMessage 以实现穿透非按钮区域的点击
  • 版本 1.8 - 2009 年 11 月
    • 根据 getholdofphil 的建议添加了键盘事件
    • 修复了焦点问题
  • 版本 1.9 - 2010 年 7 月
    • 将禁用图像例程切换为 ControlPaint 方法
    • 添加了 TextSmoothingMode 属性
    • 添加了 SideImageClicked 事件
    • ClickButtonArea EventArgs 更改为 MouseEventArgs 以便更轻松地传递鼠标按钮
  • 版本 2.0 - 2011 年 12 月
    • 添加了 IButtonControl 接口以支持 PerformClickDialogResult
    • cBlendItem 类型添加了 BlendItemsConverter
  • 版本 2.0 - 2013 年 1 月
    • 添加了 Visual Studio 2012 版本
  • 版本 3.4 - 2015 年 3 月
    • 添加了 ShowFocus 属性
  • 版本 3.5 - 2015 年 4 月
    • 移除了 ControlDesigner Adorner Override,并添加了真正的 Adorners 和 Glyphs(在现有项目中替换 3.4 或更早版本之前,请参阅上面的重要提示。)
    • 添加了 ButtonAreaUpButtonAreaDown
  • 版本 3.6 2015 年 5 月
    • 修复了 SideImageMouseUp/Down 后中断点击的问题
© . All rights reserved.