How to prevent graphics from resetting [duplicate] - vb.net

I need to put some graphics in one section of a TableLayoutPanel.
I'm doing this by creating a PictureBox in one cell of the TLP.
Can't get two things to work:
1) The initial display is blank! Drawing appears only when you resize the form
2) The resize event doesn't fire equally when expanding the size as compared contracting.
Any suggestions to improve the above two problems would be great!
Here is my code. The form has a 2x2 TableLayoutPanel in it, and one cell of the TLP has a PictureBox in it. Both the TLP and the PictureBox are set to Fill Parent:
Imports System.Drawing.Drawing2D
Public Class Form1
Private g As Graphics
Dim n As Integer = 0
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Debug.Print(String.Format("{0}{0}Form1_Load at {1}", vbCrLf, Now()))
Me.SetDesktopLocation(800, 200)
End Sub
Private Sub Form1_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles MyBase.Paint
n += 1
Debug.Print(String.Format("MyBase.Paint: {0}", n))
DisplayMyStuff()
End Sub
Private Sub PictureBox1_Resize(sender As Object, e As EventArgs) Handles Pict ureBox1.Resize
n += 1
Debug.Print(String.Format("PictureBox1.Resize: {0} PictureBoxSize = {1} / {2}", n, PictureBox1.Width, PictureBox1.Height))
If g IsNot Nothing Then
g.Dispose()
End If
g = PictureBox1.CreateGraphics()
End Sub
Private Sub DisplayMyStuff()
Dim rect1 As Rectangle
Dim rect2 As Rectangle
Dim pt1 As New Point(50, 50)
Dim pt2 As New Point(100, 100)
Dim pt3 As New Point(150, 150)
Dim brR As New SolidBrush(Color.Red)
Dim linGradBr As New LinearGradientBrush(pt2, pt3, Color.Yellow, Color.Blue)
Dim pictBoxSize As Size
Dim sz As Size
Dim width, height As Integer
pictBoxSize = New Size(CType(PictureBox1.Size, Point))
width = CInt(pictBoxSize.Width / 2)
height = CInt(pictBoxSize.Height / 2)
sz = New Size(width, height)
n += 1
Debug.Print(String.Format("DisplayMyStuff: {0}, Half-Size = {1} / {2}", n, width, height))
g.Clear(Color.Bisque)
rect1 = New Rectangle(pt1, sz)
rect2 = New Rectangle(pt2, sz)
g.FillRectangle(brR, rect1)
g.FillRectangle(linGradBr, rect2)
brR.Dispose()
linGradBr.Dispose()
End Sub
End Class

Apparently, you are trying to draw to a picturebox (g = PictureBox1.CreateGraphics())
The reason stuff disappears is that when the size changes, or something passes over the window, the controls and form need to be repainted. This happens in the Paint event, so your code needs to do the drawing there. Unlike a PictureBox image, items drawn to a form or control are not persistent on their own, that is done by drawing in the Paint event.
This is essentially your DrawMyStuff procedure relocated to the Picbox's Paint event.
Private Sub PictureBox1_Paint(sender As Object,
e As PaintEventArgs) Handles PictureBox1.Paint
Dim pt1 As New Point(50, 50)
Dim pt2 As New Point(100, 100)
Dim pt3 As New Point(150, 150)
Dim sz As New Size(CInt(PictureBox1.Size.Width / 2),
CInt(PictureBox1.Size.Height / 2))
n += 1
Debug.Print(String.Format("DisplayMyStuff: {0},
Half-Size = {1} / {2}", n, sz.Width, sz.Height))
Dim rect1 As New Rectangle(New Point(50, 50), sz)
Dim rect2 As New Rectangle(New Point(100, 100), sz)
Using linGradBr As New LinearGradientBrush(pt2, pt3, Color.Yellow, Color.Blue)
e.Graphics.Clear(Color.Bisque)
e.Graphics.DrawRectangle(Pens.Black, rect1)
e.Graphics.DrawRectangle(Pens.Black, rect2)
e.Graphics.FillRectangle(Brushes.Red, rect1)
e.Graphics.FillRectangle(linGradBr, rect2)
End Using
End Sub
If you are actually trying to paint on the Form, then Grim's answer is the solution. There you respond to the Form Paint event. In either case, use the Graphics object provided by Windows as an EventArg.
Above, you are using the Graphics object for the PictureBox (via event args) so output is to the PictureBox.
Windows wont know you are drawing something in the Paint event, so you need to tell it that the image needs to be updated at certain times such as when the PictureBox is resized. In the resize event, add:
PictureBox1.Invalidate ' tell windows it needs to be redrawn
' or
PictureBox1.Refresh ' redraw now
Me.Refresh is a bit of overkill because the entire form likely does not need to be repainted.

As Hans Passant says. First get rid of;
Private g As Graphics
and the whole of the PictureBox1_Resize(...)... routine. Then change the following routines to be like so;
Private Sub Form1_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles MyBase.Paint
DisplayMyStuff(e.Graphics)
End Sub
and
Private Sub DisplayMyStuff(pGraphics As Graphics)
Dim pt1 As New Point(50, 50)
Dim pt2 As New Point(100, 100)
Dim pt3 As New Point(150, 150)
Dim pictBoxSize As New Size(CType(PictureBox1.Size, Point))
Dim width As Integer = CInt(pictBoxSize.Width / 2)
Dim height As Integer = CInt(pictBoxSize.Height / 2)
Dim sz As New Size(width, height)
pGraphics.Clear(Color.Bisque)
Dim rect1 As New Rectangle(pt1, sz)
Dim rect2 As New Rectangle(pt2, sz)
Using linGradBr As New LinearGradientBrush(pt2, pt3, Color.Yellow, Color.Blue)
pGraphics.FillRectangle(Brushes.Red, rect1)
pGraphics.FillRectangle(linGradBr, rect2)
End Using
End Sub
.. then test. Please report back to tell me that you've learned something!! Especially.. that you don't need to create a new red brush - all 'standard' colours are built in - and that using the graphics object properly leads to better, smoother displays.

Related

How to draw a right triangle in vb.net WinForm?

I have x ImageButtons in my WinForm each ImageButton has the same height and width and works like a radio-button group, the user can press only one ImageButtton at the time.
I would add a triangle at the center of each ImageButton when it's clicked like this:
I was trying to draw a Triangle by using Graphics but i can't get how to make thar triangle pointing at the Right and anchored to the center of the image button...
Here is the code i've tried to draw the triangle
With e.Graphics
Using gp As New GraphicsPath
gp.AddLine(150, 20, 180, 240)
gp.AddLine(180, 240, 20, 240)
gp.CloseFigure()
.FillPath(Brushes.Red, gp)
.DrawPath(Pens.Red, gp)
End Using
End With
In your shoes I’ll pay attention about comments above as if you do that everything will be easier in a long term prospective.
Saying that in a short term this code gives an idea to start with:
Private Sub Button2_Paint(sender As Object, e As PaintEventArgs) Handles Button2.Paint
Dim w As Single = 30
Dim h As Single = 50
Dim centerY As Single = CSng(Button2.Height / 2)
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias
e.Graphics.CompositingQuality = CompositingQuality.HighQuality
Using gp As New GraphicsPath()
gp.AddLine(0, centerY - h / 2, w, centerY)
gp.AddLine(w, centerY, 0, centerY + h / 2)
e.Graphics.DrawPath(Pens.Red, gp)
e.Graphics.FillPath(Brushes.Red, gp)
End Using
End Sub
And let me add something else which is: your triangle (as your image shows) is a Left triangle not a Right triangle.
Se hai necessità di farlo al click e riportare allo stato iniziale tutti gli altri parti da qui:
Private Event UpdateBtns(caller As Button)
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
RaiseEvent UpdateBtns(Button1)
End Sub
Private Sub Button2_Paint(sender As Object, e As PaintEventArgs) Handles Button2.Paint
AddHandler UpdateBtns, Sub(caller As Button)
If (caller.Name <> CType(sender, Button).Name) Then CType(sender, Button).Refresh()
End Sub
If ActiveControl Is CType(sender, Button) Then
Dim w As Single = 30
Dim h As Single = 50
Dim centerY As Single = CSng(Button2.Height / 2)
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias
e.Graphics.CompositingQuality = CompositingQuality.HighQuality
Using gp As New GraphicsPath()
gp.AddLine(0, centerY - h / 2, w, centerY)
gp.AddLine(w, centerY, 0, centerY + h / 2)
e.Graphics.DrawPath(Pens.Red, gp)
e.Graphics.FillPath(Brushes.Red, gp)
End Using
End If
End Sub

how to draw on picture box without losing the content after minimizing, vb.net?

When I minimize the form, I lose all the contents drawn on the picture box. I want it to be retain on the picture box. Please help me in this regard.
The code can be seen below..
Private Sub ButtonDraw_Click(sender As Object, e As EventArgs) Handles ButtonDraw.Click
Dim g As Graphics = PictureBox1.CreateGraphics
Dim MyPen As New Pen(Color.Red)
MyPen.Width = 2
MyPen.DashStyle = DashStyle.Solid
g.TranslateTransform(PictureBox1.Width / 2, PictureBox1.Height / 2)
g.DrawEllipse(MyPen, New Rectangle(-150, -150, 300, 300))
End Sub
Example code, using e.Graphics in the Paint() event, as suggested by Jimi:
Private Sub PictureBox1_Paint(sender As Object, e As PaintEventArgs) Handles PictureBox1.Paint
Dim g As Graphics = e.Graphics
Using MyPen As New Pen(Color.Red)
MyPen.Width = 2
MyPen.DashStyle = DashStyle.Solid
g.TranslateTransform(PictureBox1.Width / 2, PictureBox1.Height / 2)
g.DrawEllipse(MyPen, New Rectangle(-150, -150, 300, 300))
End Using
End Sub

How to avoid overlapping of opaque rectangles on graphics.drawrectangle()?

After a bunch of fail on Google searches finally I thought for asking for experts here to help me at this problem as this site always helped me.
What I want?
I want to create a Highlighter for my some kind of drawing application. I want this to be similar to the highlighter you can see on the Windows Snipping Tool.
What is my problem?
The problem is that although I can draw the semitransparent or opaque rectangles using the code, gfx.FillRectangle(New SolidBrush(Color.FromArgb(100, Colors.GreenYellow)), x, y, width, height), but if I draw another rectangle overlapping any previous rectangles the colors gets darker and reduces the transparency of the rectangles where they overlapped.
Code:
Public Class Form1
Dim drag As Boolean
Dim mouseX, mouseY As Integer
Dim prev As Point
Dim initi As Point
Private Sub Form1_Shown(sender As Object, e As EventArgs) Handles Me.Shown
Dim grx As Graphics = Panel1.CreateGraphics
grx.DrawString("+", New Font("Arial", 144, FontStyle.Regular), New SolidBrush(Color.FromArgb(100, Color.GreenYellow)), New Point(200, 200))
End Sub
Private Sub Panel1_MouseDown(sender As Object, e As MouseEventArgs) Handles Panel1.MouseDown
drag = True
mouseX = MousePosition.X - Me.Left - 8
mouseY = MousePosition.Y - Me.Top - 34
initi = New Point(mouseX, mouseY)
End Sub
Private Sub Panel1_MouseMove(sender As Object, e As MouseEventArgs) Handles Panel1.MouseMove
If drag Then
mouseX = MousePosition.X - Me.Left - 8
mouseY = MousePosition.Y - Me.Top - 34
End If
End Sub
Private Sub Panel1_MouseUp(sender As Object, e As MouseEventArgs) Handles Panel1.MouseUp
drag = False
prev = New Point(0, 0)
Dim grx As Graphics = Panel1.CreateGraphics
grx.FillRectangle(New SolidBrush(Color.FromArgb(100, Color.GreenYellow)), initi.X, initi.Y, (mouseX - initi.X), (mouseY - initi.Y))
End Sub
End Class
Screenshot of the application (Showing the problem)
Left "+" is the one that I want to draw.
Right "+" is the one that I get when I draw.
I tried this and it works: There are no borders you can't even distinguish the different boxes:
I changed the following:
New SolidBrush(Color.FromArgb(100, Color.GreenYellow)), New Point(200, 200))
Into this:
New SolidBrush(Color.GreenYellow), New Point(200, 200))
For BOTH times: For the declarations and for the Panel1_MouseDown
That is because this sets the Alpha to 1 by defualt making unchangable as well, so over lapping will not change any colours, layers, or visibility.
You should only use FromArgb when you are going to control the alpha of the colour, but in this case you are letting the computer do that for you
Well. I've never really used graphics, but the only thing I could come up with is to create a list of the highlights to be drawn and then each time the mouse_up event fires, create a new bitmap, draw each rectangle pixel by pixel, with semi-transparent pixels to the bitmap and then draw the resulting bitmap to the panel using the panel's paint event handler that fires when you refresh the panel. This seems the built-in alpha blending that .net does automatically.
Add this to your form's variable declarations
Dim highlightsList As New List(Of Rectangle)
Dim bmp1 As Bitmap
alter your form_shown event to
Private Sub Form1_Shown(sender As Object, e As EventArgs) Handles Me.Shown
bmp1 = New Bitmap(Panel1.Width, Panel1.Height)
End Sub
add this sub which handles the addition of the rectangles to the list and the creation of the bitmap
Private Sub addRectangle(gr As Graphics, x As Integer, y As Integer, v1 As Integer, v2 As Integer)
Dim newRectangle As New Rectangle(x, y, v1, v2)
highlightsList.Add(newRectangle)
Using G As Graphics = Graphics.FromImage(bmp1)
G.Clear(Color.White)
End Using
bmp1.MakeTransparent(Color.White)
For Each rect As Rectangle In highlightsList
For i As Integer = rect.X To rect.X + rect.Width - 1
For j As Integer = rect.Y To rect.Y + rect.Height - 1
bmp1.SetPixel(i, j, Color.FromArgb(100, Color.GreenYellow))
Next
Next
Next
Panel1.Refresh()
End Sub
add a handler for the panel's paint event so that when you refresh the panel, the bitmap is drawn onto it
Private Sub Panel1_Paint(sender As Object, e As PaintEventArgs) Handles Panel1.Paint
e.Graphics.DrawImage(bmp1, 0, 0)
End Sub
and change your mouse_up event to use the above sub to do the drawing
Private Sub Panel1_MouseUp(sender As Object, e As MouseEventArgs) Handles Panel1.MouseUp
drag = False
prev = New Point(0, 0)
Dim grx As Graphics = Panel1.CreateGraphics
'grx.Clear(Panel1.BackColor)
addRectangle(grx, initi.X, initi.Y, (mouseX - initi.X), (mouseY - initi.Y))
End Sub
This seems to work, but if you're using the panel for anything other that showing highlights, it may not work as expected.

vb.net redrawing rectangles on picturebox and invalidate method

I am trying to create a rectangle on a picturebox with randome size and location. Every time the user click on a button, the rectangle will be removed and the new one will be displayed.
Private Sub btnDraw_Click(sender As Object, e As EventArgs) Handles btnDraw.Click
RandomBox()
End Sub
Public Sub RandomBox()
Dim r As New Rectangle(CInt(Rnd() * picGene.Width), CInt(Rnd() * picGene.Height), 20 + Rnd() * 50, 10 + Rnd() * 50)
Dim g As System.Drawing.Graphics
g = picGene.CreateGraphics()
g.FillRectangle(Brushes.Blue, r)
End Sub
It always keeps the rectangles drawn previously so I tried to place the picGene.Invalide() before or after the code block. It removes the old rectangle but it does not draw the new rectangle.
Public Sub RandomBox()
picGene.Invalidate()
Dim r As New Rectangle(CInt(Rnd() * picGene.Width), CInt(Rnd() * picGene.Height), 20 + Rnd() * 50, 10 + Rnd() * 50)
Dim g As System.Drawing.Graphics
g = picGene.CreateGraphics()
g.FillRectangle(Brushes.Blue, r)
'picGene.Invalidate()
End Sub
Any idea?
Thanks
Store a Rectangle in memory and just change that one with each click and paint it in the paint event. The Random class is a better choice these days for random generated values.
Private rect As New Rectangle
Private Sub btnDraw_Click(sender As Object, e As EventArgs) Handles btnDraw.Click
Static rnd As New Random
rect = New Rectangle(rnd.Next(picGene.Width), rnd.Next(picGene.Height), 20 + rnd.Next({somevalue}) * 50, 10 + rnd.Next({someValue}) * 50)
pb.Refresh() 'redraw the rectangle
End Sub
Private Sub picGene_Paint(sender As Object, e As PaintEventArgs) Handles picGene.Paint
e.Graphics.DrawRectangle(Pens.Black, rect)
End Sub

VB Tab control in draw.mode

I am trying to make the tabs show horizontally on the right side of my form. I can't use tabcontrol from tool box because of how the text displays.
I am using a code that I found to help me. But after exhausting all of my resources I can't seem to get the code to point to the tabPages collection. I have entries in there but the tabs show up blank.
Public Sub New()
tabControl1 = New TabControl()
Dim tabPage1 As New TabPage()
' Sets the tabs to be drawn by the parent window Form1.
' OwnerDrawFixed allows access to DrawItem.
tabControl1.DrawMode = TabDrawMode.OwnerDrawFixed
tabControl1.Controls.Add(tabPage1)
tabControl1.Location = New Point(25, 25)
tabControl1.Size = New Size(250, 250)
tabPage1.TabIndex = 0
myTabRect = tabControl1.GetTabRect(0)
ClientSize = New Size(300, 300)
Controls.Add(tabControl1)
AddHandler tabControl1.DrawItem, AddressOf OnDrawItem
End Sub!
tab Example
You can set the .Alignment property of the TabControl to Left to use horizontal tabs.
If you don't like that, try a FlowLayoutPanel with a separate TabControl for each tab, e.g.
Public Class Form1
Private Sub Form1_Load(sender As Object, e As System.EventArgs) Handles Me.Load
Dim flp As New FlowLayoutPanel
flp.Dock = DockStyle.Left
flp.AutoSize = True
flp.AutoSizeMode = Windows.Forms.AutoSizeMode.GrowOnly
Me.Controls.Add(flp)
For i As Integer = 0 To 5
Dim tbc As New TabControl
Dim tbp As New TabPage("Tab" & i.ToString)
tbc.TabPages.Add(tbp)
flp.Controls.Add(tbc)
Next i
End Sub
End Class
I ended up compiling this code from different sources to get this working,
Private Sub TabControl1_DrawItem(ByVal sender As Object, ByVal e As System.Windows.Forms.DrawItemEventArgs) Handles TabControl1.DrawItem
Dim g As Graphics
Dim sText As String
Dim iX As Integer
Dim iY As Integer
Dim sizeText As SizeF
Dim ctlTab As TabControl
Dim r As New RectangleF(e.Bounds.X, e.Bounds.Y + 2, e.Bounds.Width, e.Bounds.Height - 2)
ctlTab = CType(sender, TabControl)
g = e.Graphics
sText = ctlTab.TabPages(e.Index).Text
sizeText = g.MeasureString(sText, ctlTab.Font)
iX = e.Bounds.Left + 6
iY = e.Bounds.Top + (e.Bounds.Height - sizeText.Height) / 2
g.DrawString(sText, ctlTab.Font, Brushes.Black, iX, iY)
End Sub
The text doesn't show up in the RAD but it does when I debug/run it.
Many thanks to LUC001 # http://www.dreamincode.net/forums/topic/125792-how-to-make-vertical-tabs/