You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在PictureBox中绘制带凹凸圆角衔接的多色进度条

求助:实现带中间凹凸圆角衔接的多色进度条

我用PictureBox制作一款包含4种颜色的进度条,需要实现两端圆角及中间凹凸圆角衔接效果。目前已完成颜色填充和两端圆角,但中间的凹凸圆角衔接还没实现。

效果展示

目标效果

目标效果示例

当前实现效果

当前实现效果

初始代码

Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    Dim pictureBox As New PictureBox()
    pictureBox.Size = New Size(280, 30)
    pictureBox.Location = New Point(10, 80)
    AddHandler pictureBox.Paint, AddressOf Me.PictureBox_Paint
    Me.Controls.Add(pictureBox)
End Sub

Private Sub PictureBox_Paint(sender As Object, e As PaintEventArgs)
    Dim g As Graphics = e.Graphics
    g.SmoothingMode = SmoothingMode.AntiAlias ' Essencial para bordas suaves

    Dim pb As PictureBox = CType(sender, PictureBox)
    
    Dim area As Rectangle = pb.ClientRectangle
   
    Dim colors() As Color = {
        Color.FromArgb(85, 85, 135), 
        Color.FromArgb(217, 83, 79),  
        Color.FromArgb(240, 173, 78), 
        Color.FromArgb(91, 192, 222)  
    }
    Dim proportions() As Double = {0.25, 0.15, 0.35, 0.25}

    Dim totalWidth As Single = area.Width
    Dim totalHeight As Single = area.Height

    Dim cornerRadius As Single = CSng(Math.Min(totalWidth * 0.1, totalHeight / 2.0)) 
    If cornerRadius <= 0 Then cornerRadius = 1 

    Dim currentX As Single = area.X
    Dim numSegments As Integer = colors.Length

    
    For i As Integer = 0 To numSegments - 1
        Dim segmentWidth As Single
        If i = numSegments - 1 Then
            segmentWidth = (area.X + totalWidth) - currentX
        Else
            segmentWidth = CSng(totalWidth * proportions(i))
        End If

        If segmentWidth <= 0 Then Continue For

        Dim segmentRect As New RectangleF(currentX, area.Y, segmentWidth, totalHeight)

        Dim roundLeft As Boolean = (i = 0) 
        Dim roundRight As Boolean = (i = numSegments - 1) 

        Using segmentPath As GraphicsPath = CreateSegmentPath(segmentRect, cornerRadius, roundLeft, roundRight)
            Using segmentBrush As New SolidBrush(colors(i))
                g.FillPath(segmentBrush, segmentPath)
            End Using 
        End Using 

        currentX += segmentWidth
    Next
End Sub

Private Function CreateSegmentPath(rectF As RectangleF, radius As Single, makeLeftRound As Boolean, makeRightRound As Boolean) As GraphicsPath
    Dim path As New GraphicsPath()
    Dim diameter As Single = Math.Max(0, radius * 2) 

    If diameter > rectF.Width Then diameter = rectF.Width
    If diameter > rectF.Height Then diameter = rectF.Height
    radius = diameter / 2.0F 

    Dim Left As Single = rectF.Left
    Dim Top As Single = rectF.Top
    Dim Right As Single = rectF.Right
    Dim Bottom As Single = rectF.Bottom

    If radius <= 0 OrElse rectF.Width <= 0 OrElse rectF.Height <= 0 Then
        path.AddRectangle(rectF)
        Return path
    End If

    If makeLeftRound Then
        path.AddArc(Left, Top, diameter, diameter, 180, 90)
    Else
        ' Começa com uma linha reta no canto
        path.AddLine(Left, Top + radius, Left, Top) 
        path.AddLine(Left, Top, Left + radius, Top)
    End If

    path.AddLine(Left + radius, Top, Right - radius, Top)

    If makeRightRound Then
        path.AddArc(Right - diameter, Top, diameter, diameter, 270, 90)
    Else
        path.AddLine(Right - radius, Top, Right, Top) 
        path.AddLine(Right, Top, Right, Top + radius) 
    End If

    path.AddLine(Right, Top + radius, Right, Bottom - radius)

    If makeRightRound Then
        path.AddArc(Right - diameter, Bottom - diameter, diameter, diameter, 0, 90)
    Else
        path.AddLine(Right, Bottom - radius, Right, Bottom) ' Termina a linha direita
        path.AddLine(Right, Bottom, Right - radius, Bottom) ' Começa a linha inferior
    End If

    path.AddLine(Right - radius, Bottom, Left + radius, Bottom)

    If makeLeftRound Then
        path.AddArc(Left, Bottom - diameter, diameter, diameter, 90, 90)
    Else
        path.AddLine(Left + radius, Bottom, Left, Bottom) 
        path.AddLine(Left, Bottom, Left, Bottom - radius) 
    End If

    path.CloseFigure() 
    Return path
End Function

更新后的代码

Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim pictureBox As New PictureBox()
pictureBox.Size = New Size(280, 30)
pictureBox.Location = New Point(10, 80)
AddHandler pictureBox.Paint, AddressOf Me.PictureBox_Paint
Me.Controls.Add(pictureBox)

End Sub

Private Sub PictureBox_Paint(sender As Object, e As PaintEventArgs)
Dim g As Graphics = e.Graphics
g.SmoothingMode = SmoothingMode.AntiAlias

Dim pb As PictureBox = CType(sender, PictureBox)
Dim area As Rectangle = pb.ClientRectangle
Dim colors As New List(Of Color) From {
    Color.FromArgb(70, 71, 117), ' Produtivo
    Color.FromArgb(210, 50, 45), ' Não Produtivo
    Color.FromArgb(237, 156, 40), ' Atenção
    Color.FromArgb(91, 192, 222) ' Não Definido
}
Dim proportions As New List(Of Double) From {0.25, 0.15, 0.35, 0.25}

Dim totalWidth As Single = area.Width
Dim totalHeight As Single = area.Height
Dim cornerRadius As Single = CSng(Math.Min(totalWidth * 0.1, totalHeight / 2.0))
If cornerRadius <= 0 Then cornerRadius = 1

Dim currentX As Single = area.X
paths.Clear() ' Limpar a lista antes de adicionar novos caminhos

' Criar os caminhos para cada segmento
For i As Integer = 0 To colors.Count - 1
    Dim segmentWidth As Single = CSng(totalWidth * proportions(i))
    If segmentWidth <= 0 Then Continue For

    Dim segmentRect As New RectangleF(currentX, area.Y, segmentWidth, totalHeight)
    Dim roundLeft As Boolean = (i = 0)
    Dim roundRight As Boolean = (i = colors.Count - 1)

    Dim segmentPath As GraphicsPath = CreateSegmentPath(segmentRect, cornerRadius, roundLeft, roundRight)
    paths.Add(segmentPath)

    currentX += segmentWidth
Next

' Pintar os caminhos de trás para frente
For i As Integer = paths.Count - 1 To 0 Step -1
    Using segmentBrush As New SolidBrush(colors(i))
        g.FillPath(segmentBrush, paths(i))
    End Using
Next
End Sub

Private Function CreateSegmentPath(rectF As RectangleF, radius As Single, makeLeftRound As Boolean, makeRightRound As Boolean) As GraphicsPath
Dim path As New GraphicsPath()
Dim diameter As Single = Math.Max(0, radius * 2)

If diameter > rectF.Width Then diameter = rectF.Width
If diameter > rectF.Height Then diameter = rectF.Height
radius = diameter / 2.0F

Dim Left As Single = rectF.Left
Dim Top As Single = rectF.Top
Dim Right As Single = rectF.Right
Dim Bottom As Single = rectF.Bottom

If radius <= 0 OrElse rectF.Width <= 0 OrElse rectF.Height <= 0 Then
    path.AddRectangle(rectF)
    Return path
End If

' --- Construção do Caminho ---
' Canto Superior Esquerdo
If makeLeftRound Then
    path.AddArc(Left, Top, diameter, diameter, 180, 90)
Else
    ' Começa com uma linha reta no canto
    path.AddLine(Left, Top + radius, Left, Top) ' Garante o ponto inicial correto
    path.AddLine(Left, Top, Left + radius, Top)
End If

' Linha Superior (entre os cantos/arcos)
path.AddLine(Left + radius, Top, Right - radius, Top)

' Canto Superior Direito
If makeRightRound Then
    path.AddArc(Right - diameter, Top, diameter, diameter, 270, 90)
Else
    path.AddLine(Right - radius, Top, Right, Top) ' Termina a linha superior
    path.AddLine(Right, Top, Right, Top + radius) ' Começa a linha direita
End If

' Linha Direita (Vertical)
path.AddLine(Right, Top + radius, Right, Bottom - radius)

' Canto Inferior Direito
If makeRightRound Then
    path.AddArc(Right - diameter, Bottom - diameter, diameter, diameter, 0, 90)
Else
    path.AddLine(Right, Bottom - radius, Right, Bottom) ' Termina a linha direita
    path.AddLine(Right, Bottom, Right - radius, Bottom) ' Começa a linha inferior
End If

' Linha Inferior
path.AddLine(Right - radius, Bottom, Left + radius, Bottom)

' Canto Inferior Esquerdo
If makeLeftRound Then
    path.AddArc(Left, Bottom - diameter, diameter, diameter, 90, 90)
Else
    path.AddLine(Left + radius, Bottom, Left, Bottom) ' Termina a linha inferior
    path.AddLine(Left, Bottom, Left, Bottom - radius) ' Começa a linha esquerda (para fechar)
End If

path.CloseFigure() ' Fecha o caminho conectando a última linha ao início
Return path
End Function

恳请各位提供帮助解决该问题。

内容的提问来源于stack exchange,提问作者Luiz

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 11:44:51