VB.net Graphics not drawing to screen - vb.net

I am creating a hangman game and i cant get my dashes to draw on the screen.
Public Class Form1
Public lines() As String= IO.File.ReadAllLines("C:\Users\axfonath14\Desktop\words_alpha.txt")
Dim thisArray As String() = lines.ToArray
Public word As String = thisArray(CInt(Int((thisArray.Length * Rnd()) + 1)))
Public g As Graphics = Me.CreateGraphics()
Public dash As Image = My.Resources.dash
Public x As Integer = 1
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Label1.Text = word
For Each c As Char In word
x += 1
g.DrawImage(dash, 125 + (100 * x), 100)
Next
Label2.Text = x
End Sub
End Class
when i run this code it runs through the loop and changes the label to the amount of letters but it just wont draw anything.

Never, EVER call CreateGraphics. Lazy people do so without explanation in examples and tutorials but it is never appropriate in a real application. ALWAYS do your drawing in the Paint event handler of the control you want to draw on. In your case, get rid of that g field altogether, move your drawing code to the Paint event handler of the form and use the Graphics object that it provides:
Private Sub Form1_Paint(sender As Object, e As PaintEventArgs) Handles Me.Paint
For Each c As Char In word
x += 1
e.Graphics.DrawImage(dash, 125 + (100 * x), 100)
Next
End Sub
That way, your drawing gets restored each time the form gets erased and repainted, which can be often and will be after the Load event handler completes.
If you want to change what gets drawn then you need to store the appropriate data in fields that can then be accessed from the Paint event handler. To force a repaint, call Invalidate on the form or control whose Paint event you're handling.

You're grabbing the graphics context before the form has initialized. Move your initialization code to the actual constructor, after the InitializeComponent() method is called.

Related

Graphics causing solid white button

I'm coding Conway's game of life. My grid is entirely in a picture box but when I load the form the back button in the upper right is completely white. Refreshing the form fixes the button but makes it incredibly laggy. Every other button shows up fine, just the back button is broken. How can I fix this?
Option Strict On
Public Class frmGame
' Declaring public variables
Public bmp As Bitmap
Public G As Graphics
Public WithEvents speed As Timer
Public grid(50, 40) As Boolean
Public input(50, 40) As Boolean
Public intGens As Integer = 0
Public change As Boolean = False
Public P As New Pen(Color.Black)
Private Sub picGrid_Paint(sender As Object, e As PaintEventArgs) Handles picGrid.Paint
' Loads bitmap, graphics, etc and prepares to begin simulation
' Creates graphics device
bmp = New Bitmap(600, 480)[enter image description here][1]
picGrid.Image = bmp
G = Graphics.FromImage(bmp)
' Defining variables for grid
Dim x As Integer = 0
Dim y As Integer = 0
' Draws grid
For y = 0 To 480
For x = 0 To 600
G.DrawRectangle(pen:=P, x:=x, y:=y, width:=12, height:=12)
x += 12
Next
x = 0
y += 12
Next
End Sub
Private Sub btnBack_Click(sender As Object, e As EventArgs) Handles btnBack.Click
' Hides rules form, shows main menu form
frmMainMenu.Show()
Me.Hide()
End Sub
Private Sub frmGame_FormClosed(ByVal sender As Object, ByVal e As System.Windows.Forms.FormClosedEventArgs) Handles Me.FormClosed
' Frees memory when form is closed
G.Dispose()
bmp.Dispose()
speed.Dispose()
End Sub
End Class
I'm not exactly sure do I understand your problem, nor I can recreate it with code provided.
But I can recommend some thing's that can help you.
First of all do not use Control.Refresh() while working with drawing, use instead Control.Invalidate() & Control.Update(), that may fix lag issue.
Do you relay need 2 forms (that i understand is the root of problem)?
Instead of hiding form, hide PictureBox (.Visible = False) and show other controls you need at that moment.

How do I clear a drawn line in vb.net?

I am writing a simple test program that draws an axis/crosshair in a form. I have two text boxes, where I put in the x-center and y-center and draw the crosshair based on that. I want to be able to put in new coordinates, and move the crosshair to the new position, but when I do, the old drawing stays there. I want to erase the old drawing and then draw the new one.
My code is below:
Public Class Form1
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim mypen As Pen
mypen = New Pen(Drawing.Color.Red, 1)
Dim mygraphics As Graphics = Me.CreateGraphics
Dim x_center = Integer.Parse(xPos.Text)
Dim y_center = Integer.Parse(yPos.Text)
mygraphics.DrawLine(mypen, x_center - 50, x_center, x_center + 50, x_center)
mygraphics.DrawLine(mypen, y_center, y_center - 50, y_center, y_center + 50)
End Sub
End Class
The Drawing on a Control surface is usually handled through the Control's Paint() event, using its PaintEventArgs class object.
To raise the Paint() event of a Control, call its Invalidate() method.
(Note that the Invalidate() method has a number of overloads, some of which allows to re-paint only a defined region of the surface.)
If a Graphics object is created elsewhere (as you're doing now), the drawings performed with this object will persist or will be erased when you don't want to (e.g. if a Control needs to repaint itself - and this happens quite often - the drawings will be erased).
Also, the Graphics object can't be stored. It will become an invalid object as soon as a Control has repainted its surface.
You could re-design you code in this way.
Create a shared Pen (you can redefined it at any moment if you need to, using its properties) so you don't have to create a new one every time you need to draw something.
Use a shared Point field to store the current center of the drawing.
Move the Graphics.DrawLine() to the Paint event of your Form.
Remember to Dispose() the Pen object when the Form closes (you can use it's Dispose() pre-defined method).
Public Class Form1
Private mypen As Pen = New Pen(Color.Red, 1)
Private Position As Point = New Point(-1, -1)
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
If (Integer.TryParse(xPos.Text, Position.X) = True) AndAlso
(Integer.TryParse(yPos.Text, Position.Y) = True) Then
Me.Invalidate()
End If
End Sub
Private Sub Form1_Paint(sender As Object, e As PaintEventArgs) Handles MyBase.Paint
If Position.X > -1 Then
e.Graphics.DrawLine(mypen, Position.X - 50, Position.Y, Position.X + 50, Position.Y)
e.Graphics.DrawLine(mypen, Position.X, Position.Y - 50, Position.X, Position.Y + 50)
End If
End Sub
End Class
This is, however, not that much efficient, because you need to invalidate the entire Form.
For a full implementation, take a look a this Class (PasteBin - CrossHair).

Is there a way to use timer ticks for multiple procedures?

I am currently coding a small animation. The animation starts off with a small circle moving across the screen, then the user click buttons and other small images move across the screen (I feel like describing the contents of the images, the purpose of the program, etc. would be tangential and irrelevant).
The way I am currently coding the animation is like this:
Private Sub Timer_Tick(sender As Object, e As EventArgs) Handles Timer.Tick
Circle1.Left -= 10
If Counter = 16 Then
Timer.Enabled = False
Counter = 0
End If
Counter += 1
End Sub
However, the problem is that I need to use the timer to help animate the movement of multiple images. Other than creating multiple timers, is there a way of using the ticking of the timer in multiple subroutines?
You can use a list to hold all the controls you want to animate. Iterate the list in the timer event and modify the position of the controls. Here's an example how to do it.
Public Class Form1
' this list holds all controls to animate
Private controlsToAnimate As New List(Of Control)
Private random As New Random
Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
' this list holds all controls to remove
Dim controlsToRemove As New List(Of Control)
For i = 0 To controlsToAnimate.Count - 1
Dim control = controlsToAnimate(i)
If control.Left < 0 Then
' this control has reached the left edge, so put it in the list to remove
controlsToRemove.Add(control)
Else
' move the control to left
control.Left -= 10
End If
Next
' remove all controls that have reached the left edge
For Each control In controlsToRemove
controlsToAnimate.Remove(control)
control.Dispose()
Next
' for debug only, display the number of controls to animate
Me.Text = controlsToAnimate.Count()
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
' create a new picturebox
Dim newControl As New PictureBox
With newControl
' load an image for the picturebox
.Image = Image.FromFile("D:\Pictures\abc.jpg")
' size of the picturebox
.Size = New Size(100, 100)
' picturebox appears at the right edge of the form,
' the vertical position is randomize
.Location = New Point(Me.Width - newControl.Width, random.Next(Me.Height - newControl.Height))
' stretch the image to fit the picturebox
.SizeMode = PictureBoxSizeMode.StretchImage
End With
' add the newly created control to list of controls to animate
controlsToAnimate.Add(newControl)
' add the newly created control to the form
Me.Controls.Add(newControl)
End Sub
End Class

How to use the Paint event more than once on a form?

Okay, so I am trying to make a program that each time you click (doesn't matter where) a random colored, and sized circle appears where you happened to click. however, the only way I can add a shape is via Paint event. here is the code I have now:
Private Sub Form1_Paint(ByVal Sender As System.Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles MyBase.Paint
Using Brush1 As New SolidBrush(Color.Orange)
e.Graphics.FillEllipse(Brush1, MousePosition.X, MousePosition.Y, 100, 100)
End Using
End Sub
I need to know a line of code that I can use in a mouse click event, that will re-run this sub. I know how to change the size, and make it random, I just don't know how to run this sub multiple times, more precisely; run this sub once after each mouse click. If someone can help, I would appreciate it!
Just as Plutonix explained, a refresh is handled by calling the Invalidate method.
The thing you need to remember is that whatever is painted on a surface is not persistent, so you need to redraw the whole screen every time. There are, of course, many ways in which this can be optimized for performance purposes, as this process can be extremely CPU intensive; specially, since GDI+ is not hardware accelerated.
So, what you need to do is:
Record every click (x, y position) and store it
Since the radius of each circle is random, determine the radius when the user clicks the form, then store it along with the x, y position of the click
Then, have the Paint event re-draw each stored sequence of clicks (with their respective radii) and re-draw each circle over and over.
Here's an implementation that will do the trick. Just paste this code inside any Form's class to test it:
Private Class Circle
Public ReadOnly Property Center As Point
Public ReadOnly Property Radius As Integer
Public Sub New(center As Point, radius As Integer)
Me.Center = center
Me.Radius = radius
End Sub
End Class
Private circles As New List(Of Circle)
Private radiusRandomizer As New Random()
Private Sub FormLoad(sender As Object, e As EventArgs) Handles MyBase.Load
Me.SetStyle(ControlStyles.AllPaintingInWmPaint, True) ' Not really necessary in this app...
Me.SetStyle(ControlStyles.OptimizedDoubleBuffer, True)
Me.SetStyle(ControlStyles.ResizeRedraw, True)
Me.SetStyle(ControlStyles.UserPaint, True)
End Sub
Private Sub FormMouseClick(sender As Object, e As MouseEventArgs) Handles Me.MouseClick
circles.Add(New Circle(New Point(e.X, e.Y), radiusRandomizer.Next(10, 100)))
Me.Invalidate()
End Sub
Private Sub FormPaint(sender As Object, e As PaintEventArgs) Handles Me.Paint
Dim g As Graphics = e.Graphics
g.Clear(Color.Black)
Using p As New Pen(Color.White)
For Each c In circles
g.DrawEllipse(p, c.Center.X - c.Radius \ 2, c.Center.Y - c.Radius \ 2, c.Radius, c.Radius)
Next
End Using
End Sub
Here's what you'll get after a few clicks on the form

Printing a Graphic Object VB.NET

I have a module that generates fill out a System.Drawing.Graphics object.
I then try to print it with a event on my main form but the print preview comes out blank.
This is my print page
Private Sub MyPrintDocument_PrintPage(ByVal sender As System.Object, ByVal e As System.Drawing.Printing.PrintPageEventArgs) Handles MyPrintDocument.PrintPage
Dim MyGraphic As Graphics
MyPrintDocument.PrinterSettings.DefaultPageSettings.Margins.Top = 200
MyPrintDocument.PrinterSettings.DefaultPageSettings.Margins.Left = 100
MyPrintDocument.PrinterSettings.DefaultPageSettings.Margins.Right = 100
MyPrintDocument.PrinterSettings.DefaultPageSettings.Margins.Bottom = 75
MyGraphic = MyGrpahicPage
End Sub
MyGrpahicPage is the public graphics object my module fill out.
I think the problem is that you have to print to the Graphics object provided by the event argument, not another one that you may have hanging around. In other words, you need to draw on e.Graphics. The help page for PrintPageEventArgs.Graphics shows how this is supposed to work.
I found the way.
1. step: you must create thy MyGraphics in your form:
... Form declaration ...
Private GrBitmap As Bitmap
Private GrGraphics As Graphics
Dimm Withevents pd as new PrintDocument 'The withevents is important!
...
2. step: Anywhere (ig, in the formload sub, or in a buttonclick sub) :
GrBitmap = New Bitmap(Width, Height)
GrGraphics = Graphics.FromImage(GrBitmap)
...
(the Width and the Height values you must calculate by the content of the graphics)
3. step:
Complete the GrGraphics with any .DrawString, .DrawLine, etc. methods
4. step:
Create a sub of Printdocument:
Private Sub pd_PrintPage(sender As Object, ev As PrintPageEventArgs) Handles pd.PrintPage
ev.Graphics.DrawImage(Me.GrBitmap, New Point(0, 0))
End Sub