Hello,
I am using basic graphics to produce the "vanilla" picture box on the left. I was wondering what I can do to get something nicer like the image on the right.
The idea is to fill the "bucket" with a certain color based on a given percentage (in this case 20%).
My code for the vanilla is as follows:
(Note: the code is using an array for the percentages and an array for the colors, but for this application, there will only ever be one of each - i.e. "fill the bucket with 20% of blue".)
Code:
Public Sub FillRiskBucket(pobjPic As PictureBox,
percentages As Integer(),
colorsToUse As Color(),
pstrText As String,
Optional pblnHideIfZero As Boolean = True,
Optional pintFontSize As Integer = 10)
pobjPic.CreateGraphics.Clear(Color.Azure)
' Create a Graphics object to draw on the picturebox
Dim G As Graphics = pobjPic.CreateGraphics()
' Calculate the number of pixels per 1 percent
Dim pixelsPerPercent As Single = pobjPic.Height / 100.0F
' Keep track of the height at which to start drawing (starting from the bottom going up)
Dim drawHeight As Integer = pobjPic.Height
' Loop through all percentages and draw a rectangle for each
For i As Integer = 0 To percentages.Length - 1
' Create a brush with the current color
Dim brush As New SolidBrush(colorsToUse(i))
' Update the height at which the next rectangle is drawn.
drawHeight -= CInt(pixelsPerPercent * percentages(i))
' Draw a filled rectangle
G.FillRectangle(brush, 0, drawHeight, pobjPic.Width, pixelsPerPercent * percentages(i))
Next
Dim objMyFont As Font = New Font("Tahoma", pintFontSize, FontStyle.Bold)
G.DrawString(pstrText,
objMyFont, Brushes.Black,
New PointF(pobjPic.Width / 2 - (G.MeasureString(pstrText, objMyFont).Width / 2.0F),
pobjPic.Height / 2 - (G.MeasureString(pstrText, objMyFont).Height / 2.0F)))
If pblnHideIfZero Then
pobjPic.Visible = (percentages(0) > 0)
Else
pobjPic.Visible = True
End If
End Sub
do the following: take your favorite paint program and make two images same size where the right bar is in exactly the same position. then in one you empty the bar by copying the empty area over the blue, in the secon image you fill it up by copying the blue. then store them as 0.png and 100.png then you can do this:
Code:
Private imgFull As Image
Private imgEmpty As Image
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
VScrollBar1.LargeChange = 1
imgFull = Image.FromFile(IO.Path.Combine(Application.StartupPath, "100.png"))
imgEmpty = Image.FromFile(IO.Path.Combine(Application.StartupPath, "0.png"))
End Sub
Private Sub VScrollBar1_ValueChanged(sender As Object, e As EventArgs) Handles VScrollBar1.ValueChanged
PictureBox1.Invalidate()
End Sub
Private Sub PictureBox1_Paint(sender As Object, e As PaintEventArgs) Handles PictureBox1.Paint
Dim f As Double = VScrollBar1.Value / VScrollBar1.Maximum
Dim cpDestBottom As Int32 = CInt(PictureBox1.Height * f)
Dim cpDestTop As Int32 = PictureBox1.Height - cpDestBottom
Dim cpSourceBottom As Int32 = CInt(imgEmpty.Height * f)
Dim cpSourceTop As Int32 = imgEmpty.Height - cpSourceBottom
e.Graphics.DrawImage(imgEmpty, New Rectangle(0, 0, PictureBox1.Width, cpDestTop),
New Rectangle(0, 0, imgEmpty.Width, cpSourceTop),
GraphicsUnit.Pixel)
e.Graphics.DrawImage(imgFull, New Rectangle(0, cpDestTop, PictureBox1.Width, cpDestBottom),
New Rectangle(0, cpSourceTop, imgFull.Width, cpSourceBottom),
GraphicsUnit.Pixel)
End Sub
even better: take the image you want, make the middle part alpha transparent with some white and black semi transparent area for the reflections and the shadow. this will be your "overlay" then you first draw a rectangle in the desired color, then paint the overlay on top:
Code:
Public Class Form1
Private imgOverlay As Image
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
imgOverlay = Image.FromFile(IO.Path.Combine(Application.StartupPath, "Overlay.png"))
VScrollBar1.LargeChange = 1
VScrollBar1.Value = 50
VScrollBar2.LargeChange = 1
VScrollBar2.Value = 30
VScrollBar3.LargeChange = 1
VScrollBar3.Value = 40
End Sub
Private Sub PictureBox1_Paint(sender As Object, e As PaintEventArgs) Handles PictureBox1.Paint
DrawBar(e.Graphics, 0, 1 - (VScrollBar1.Value / VScrollBar1.Maximum), Color.SkyBlue)
DrawBar(e.Graphics, 1, 1 - (VScrollBar2.Value / VScrollBar2.Maximum), Color.LightGreen)
DrawBar(e.Graphics, 2, 1 - (VScrollBar3.Value / VScrollBar3.Maximum), Color.LightPink)
End Sub
Private Sub DrawBar(g As Graphics, BarIndex As Int32, BarValue As Double, BarColor As Color)
Const BAR_COUNT As Int32 = 3
Const MARGIN_TOPBOTTOM As Int32 = 9 'count of pixels from top/bottom of image to where the graph starts
Dim fBarWidth As Double = PictureBox1.Width / (BAR_COUNT * 2 + 1)
Dim rectImage As New Rectangle(CInt(fBarWidth * (2 * BarIndex + 1)), 0, CInt(fBarWidth), PictureBox1.Height)
Dim cpMarginHeight As Double = MARGIN_TOPBOTTOM * (rectImage.Height / imgOverlay.Height) 'rescale margin
Dim cpPeakHeight As Int32 = CInt(BarValue * (rectImage.Height - 2 * cpMarginHeight))
Dim rectBar As New Rectangle(rectImage.X, CInt(rectImage.Bottom - cpMarginHeight - cpPeakHeight), rectImage.Width, cpPeakHeight)
g.FillRectangle(New SolidBrush(BarColor), rectBar)
g.DrawImage(imgOverlay, rectImage,
New Rectangle(0, 0, imgOverlay.Width, imgOverlay.Height),
GraphicsUnit.Pixel)
Dim font As New Font("Arial", 15, FontStyle.Bold)
Dim s As String = String.Format("{0:0.00%}", BarValue)
Dim szString As SizeF = g.MeasureString(s, font)
g.DrawString(s, font, Brushes.Black, rectBar.X + (rectBar.Width - szString.Width) / 2, CInt(rectBar.Bottom - rectBar.Height / 2 - szString.Height / 2))
End Sub
Private Sub VScrollBar1_Scroll(sender As Object, e As ScrollEventArgs) Handles VScrollBar1.Scroll, VScrollBar2.Scroll, VScrollBar3.Scroll
PictureBox1.Invalidate()
End Sub
Private Sub PictureBox1_Resize(sender As Object, e As EventArgs) Handles PictureBox1.Resize
PictureBox1.Invalidate()
End Sub
End Class
Last edited by digitalShaman; Mar 19th, 2017 at 03:05 AM.
Hey, digitalShaman - thanks very much for your response. What you did in the last post looks great - would you be willing to zip up that project with the associated graphics files so that I can review what you did? Meanwhile, I was able to accomplish what I needed to do using somewhat of a "brute force" method, although effective:
I was able to compose the desired "bucket" with a set of 4 picture boxes: one each for the top and bottom borders, then one for the main bucket ("empty"), and the last for the "fill-color". (Being as graphics are not my strong suit, I had some assistance in creating these gradients so that the empty background gradient and fill-color gradients were complementary, and also help on the border graphics to get the rounded corners using transparency.)
My basic process was to resize (based on given percentage) and reposition the fill-color picture box over the main picture box.
I should also mention that I positioned a textbox over the picture boxes to show the percentage, as this was acceptable and called for by the application's requirements - however, if I had to draw the text over the resulting image, I would have had to do something like what you suggested.
Last edited by BruceG; Mar 19th, 2017 at 01:29 PM.