I draw image to form, but it is limited to top left corner of form - vb.net

I have created a form and imported two square images saved as PNG files in resources. when I run the code below the black box which is drawn will only go about 200 pixels in the x coordinate and 150 pixels in the Y coordinate from where the image is drawn, after that the background remains white, and it seems I am unable to draw anything and anything I do draw stops around this point.
I have tried redrawing the image in a completely different location on the screen and It will not be visible if it is not within the region to the top left of the form, I have also tried drawing other images, but they also cease to exist when not in the top left of my form.
What I want is for the black box/other images to be drawn across the whole form, and not just in the top left corner, which something is preventing me from doing.
Public Class Form1
Dim gameGraphics As System.Drawing.Graphics = Me.CreateGraphics
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
'Draws black square which I have saved as resource
gameGraphics.DrawImage(My.Resources.black_Background, 0, 80, 1600, 600)
'Draws green square which I have saved as resource
gameGraphics.DrawImage(My.Resources.greenSquare, 2, 82, 40, 40)
End Sub
'makes the form fullscreen
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Me.FormBorderStyle = FormBorderStyle.None
Me.WindowState = FormWindowState.Maximized
End Sub
'closes form if quitbutton is clicked
Private Sub QuitButton_Click(sender As Object, e As EventArgs) Handles QuitButton.Click
Me.Close()
End Sub
End Class
Thanks for your time!

The Graphics Object cannot be stored. It's Erased/Updated constantly. You'll end up with an invalid one. It's really useless and, you could say, a mistake.
You can use a Graphics Object created with Control.CreateGraphics(), but you have to remember that it's not persistent; it will be erased when the Control you have painted it on needs to re-Paint() itself (e.g. you drag something over it, if it's a Form, when it's minimized etc.).
Those Properties, Me.FormBorderStyle = FormBorderStyle.None and Me.WindowState = FormWindowState.Maximized are better set in the designer. There's no reason to set them on a Form.Load() event. Their state is not even subject to a condition. In general, leave the Load event of a Form as lightweight as possible and avoid setting properties that can cause cascading events.
An example:
Define an object to store your images:
(The DrawBitmaps flag is used to let your Form know when to draw those Bitmaps).
Public Class MyBitmap
Public Property Image As Bitmap
Public Property Position As Point
Public Property Size As Size
End Class
Public MyBitmaps As List(Of MyBitmap)
Public DrawBitmaps As Boolean = False
Somewhere (even in Form.Load()), fill the list with you bitmaps:
(Here, the bitmap size is set to original size, but you can set it to whatever dimension you see fit).
MyBitmaps = New List(Of MyBitmap)
MyBitmaps.Add(New MyBitmap With {.Image = My.Resources.black_Background,
.Position = New Point(0, 80),
.Size = New Size(My.Resources.black_Background.Width,
My.Resources.black_Background.Height)})
MyBitmaps.Add(New MyBitmap With {.Image = My.Resources.greenSquare,
.Position = New Point(2, 82),
.Size = New Size(My.Resources.greenSquare.Width,
My.Resources.greenSquare.Height)})
The Paint() event e.Graphics of the Form performs all the painting:
(Note that it will not paint its surface unless the DrawBitmaps flag is set to True => It will not paint those Bitmaps when it's loading/showing. The other condition is a basic fail-safe.
Private Sub Form1_Paint(sender As Object, e As PaintEventArgs) Handles MyBase.Paint
If DrawBitmaps = True AndAlso MyBitmaps.Count > 0 Then
For Each _Item As MyBitmap In MyBitmaps
e.Graphics.DrawImage(_Item.Image, New Rectangle(_Item.Position, _Item.Size))
Next
End If
End Sub
When Button1 is clicked, the Form will draw your list of Bitmaps:
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
DrawBitmaps = True
Me.Invalidate()
End Sub
Somewhere in your code, add a new Bitmap and tell the Form to Invalidate only a region of the size of this new Bitmap:
MyBitmaps.Add(New MyBitmap With {.Image = My.Resources.[AnotherBitmap],
.Position = New Point(50, 50),
.Size = New Size(200, 200)})
Me.Invalidate(New Rectangle(MyBitmaps.Last().Position, MyBitmaps.Last().Size))
Remove a Bitmap from the list and repaint:
MyBitmaps.RemoveAt(0)
Me.Invalidate()

Related

How do I detect the left and right side of my screen?

I'm doing a project for my kitten who died...
I would like to know how I make it flip (look at the right side) when it comes to the left side of the screen, I'll leave an example in the print
Here's some code you can use to start with a single image file and display it in its original form or flipped horizontally depending on the horizontal position of its PictureBox on the screen.
'The Image to display on the left side of the screen.
Private leftImage As Image
'The Image to display on the right side of the screen.
Private rightImage As Image
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
'Assume that the original image is the one to display on the left.
leftImage = Image.FromFile("file path here")
'Create a mirror image to display on the right.
rightImage = leftImage.Clone()
rightImage.RotateFlip(RotateFlipType.RotateNoneFlipX)
End Sub
Private Function IsPictureBoxOnLeftSideOfScreen() As Boolean
Dim screenMiddle = Screen.PrimaryScreen.WorkingArea.Width \ 2
Dim pictureBoxMiddle = PictureBox1.PointToScreen(Point.Empty).X + PictureBox1.Width \ 2
Return screenMiddle > pictureBoxMiddle
End Function
Private Sub SetImage()
PictureBox1.Image = If(IsPictureBoxOnLeftSideOfScreen(), leftImage, rightImage)
End Sub
Private Sub Form1_FormClosed(sender As Object, e As FormClosedEventArgs) Handles Me.FormClosed
rightImage.Dispose()
leftImage.Dispose()
End Sub
Where you call that SetImage method depends on exactly how you're moving the PictureBox, which you never showed us. If you're moving the form then you might do so in the LocationChanged event handler of the form. If you're moving the PictureBox then you might do so in the LocationChanged event handler of the PictureBox.

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

Panel edge detection, stop before going out of bounds

I have a panel inside it's parent panel that I allow to move. I want it to stop moving BEFORE it falls out of the parent panel. What is the best way to accomplish this. Also I add the panels dynamically.
UPDATE:
Here is the code that goes into the "MyPanel" Panel. Only difference between "MyPanel" vs "Panel" is I add a border and the ability to move it. The "CoolMove" was from another person's answer I found online. I add a "MyPanel1" to form and then add another "MyPanel2" to that and allow it to move only if it is on the "MyPanel1". So with that, I want "MyPanel2" to stay completely in bounds of "MyPanel1". I'm struggling to get the right code to accomplish this.
Private allowCoolMove As Boolean = False
Private myCoolPoint As New Point
Public Overridable Sub MyPanel_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Me.MouseDown
'If panel is ontop of Stock panel, then allow manual moving
If Me.Parent.Name.StartsWith("S") Then
allowCoolMove = True
myCoolPoint = New Point(e.X, e.Y)
Me.Cursor = Cursors.SizeAll
Me.BringToFront()
ElseIf Not Me.Parent.Name.Contains("keyR") Then
DoDragDrop(Me, DragDropEffects.Move)
End If
End Sub
Private Sub MyPanel_MouseMove(sender As Object, e As MouseEventArgs) Handles Me.MouseMove
If allowCoolMove = True Then
Me.Location = New Point(Me.Location.X + e.X - myCoolPoint.X, Me.Location.Y + e.Y - myCoolPoint.Y)
End If
End Sub
Private Sub MyPanel_MouseUp(sender As Object, e As MouseEventArgs) Handles Me.MouseUp
allowCoolMove = False
Me.Cursor = Cursors.Default
End Sub
Each control has a ClientRectangle property that returns the dimensions of its client area (which, for a panel, is the interior part). There is also a DisplayRectangle property, which tells you the entire area of the control.
And the Rectangle structure has a Contains method overload that takes another Rectangle structure and tells you whether one rectangle is fully contained within the bounds of another rectangle.
You should be able to put those two facts together, now, to come up with code that will solve your problem. Something like:
Dim rcParentPanelInterior As Rectangle = parentPanel.ClientRectangle
Dim rcChildPanel As Rectangle = childPanel.DisplayRectangle
If rcParentPanelInterior.Contains(rcChildPanel)
' continue to allow moving
Else
' forbid moving
End If

Picturebox's image is nothing even though Clipboard.ContainsImage = True?

I am making a program that constantly sends the key "{PRTSC}" and then sets PictureBox1.BackgroundImage = My.Computer.Clipboard.GetImage.
At first it works fine but after a min or two the picturebox goes blank and no error is given.
My code is:
Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
If Not My.Computer.Clipboard.ContainsImage Then
SendKeys.Send("{PRTSC}")
Else
PictureBox1.BackgroundImage = My.Computer.Clipboard.GetImage
My.Computer.Clipboard.Clear()
End If
End Sub
I have tried:
Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
'SendKeys.Send("{PRTSC}")
'If My.Computer.Clipboard.ContainsImage Then PictureBox1.BackgroundImage = My.Computer.Clipboard.GetImage
Dim bounds As New Rectangle
Dim screenshot As System.Drawing.Bitmap
Dim graph As Graphics
bounds = Screen.PrimaryScreen.Bounds
screenshot = New System.Drawing.Bitmap(bounds.Width, bounds.Height) ', System.Drawing.Imaging.PixelFormat.Format32bppRgb)
graph = Graphics.FromImage(screenshot)
graph.CopyFromScreen(0, 0, 0, 0, bounds.Size, CopyPixelOperation.SourceCopy)
PictureBox1.BackgroundImage = screenshot
graph.Dispose()
'screenshot.Save("d:\\dcap.jpg", Imaging.ImageFormat.Bmp)
End Sub
But attempting to dispose the screenshot yields an instant error. I don't know why.
{PRTSC} grabs the active window, when it has focus, and the screen otherwise.
It's a good idea to disable the timer at the beginning of the tick event, and start it at the end. This prevents re-entry, and, depending on the type of timer (there is the timer control, system.timers.timer, and system.threading.timer, each of which is a little different), you may be required to restart the timer each tick event.
It's normal to assign an image to a picturebox image instead of the backgroundimage. If something in the application is assigning a bitmap to picturebox1.image or blanking picturebox1.image, it will overwrite the screen shot in picturebox1.backgroundimage.