Trying to draw on a Picture Box, but nothing appears - vb.net

I'm converting a VB6 application to VB.Net that draws on picture boxes. Naturally I read the fine manual and turn up this example here. I therefore produced a little project with a form containing only a picture box and tried the following:-
Private Sub Picture1_paint(ByVal sender As System.Object, _
ByVal e As System.EventArgs) _
Handles PictureBox1.Paint
Dim mygraphics As Graphics
mygraphics = PictureBox1.CreateGraphics
Dim pen As New Drawing.Pen(System.Drawing.Color.Red, 1)
mygraphics.DrawEllipse(pen, 0, 0, 100, 100)
pen.Dispose
End Sub
just like it says. But on running the application, the box turns up blank. Searching for help turned up a suggestion here that I should use a Frame instead, but the result was the same. I have checked that I'm not drawing in the background colour, and that the function is actually invoked.
What have I overlooked?

Paint handler has invalid type for EventArgs. It should be System.Windows.Forms.PaintEventArgs
Use e.Graphics property to obtain graphics instance.
mygraphics = e.Graphics
Reference Link MSDN - Control.Paint Event

I think e is of PainEventArgs type, with already contains a graphics object in e.Graphics. Use that instead.
Public Class Form1
Private Sub PictureBox1_Paint(ByVal sender As System.Object, _
ByVal e As System.Windows.Forms.PaintEventArgs) Handles PictureBox1.Paint
Dim pen As New Pen(Color.Red, 1)
e.Graphics.DrawEllipse(pen, 0, 0, 100, 100)
pen.Dispose()
End Sub
End Class

Related

Rectangle Panel Graphic relationship

The following code does not work:
Private Sub panelButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles panelButton.Click
Dim myBrush As Brush
myBrush = New SolidBrush(Color.Yellow)
fBitmap = New Bitmap(picturePanel.Width, picturePanel.Height)
Dim gg As Graphics = Graphics.FromImage(fBitmap)
gg.Clear(Color.White)
'<<<<<my attempt<<<<<<
Dim rec As Rectangle
rec = New Rectangle(picturePanel.Location.X, picturePanel.Location.Y, picturePanel.Width, picturePanel.Height)
gg.FillRectangle(myBrush, rec)
'<<<<<<<<<<<<<<<<<<<<<
'gg.FillRectangle(myBrush, gg.ClipBounds) '<<actual answer
gg.Dispose()
picturePanel.Refresh()
End Sub
In the Panel's repaint handler I've got this:
Private Sub picturePanel_Paint(sender As System.Object, e As System.Windows.Forms.PaintEventArgs) Handles picturePanel.Paint
If fBitmap IsNot Nothing Then
e.Graphics.DrawImage(fBitmap, 0, 0)
End If
End Sub
I've included the recommended code (marked as actual answer) but why doesn't the section marked my attempt turn the panel yellow? - can it be tweaked so that it turns the panel yellow?
rec = New Rectangle(picturePanel.Location.X, picturePanel.Location.Y, _
picturePanel.Width, picturePanel.Height)
That's the wrong rectangle. It is relative from the panel's Parent instead of the panel itself. At best you'll see the rectangle at the far right-bottom corner. Or not at all if it is off the bitmap. Draw relative to the bitmap's upper left corner instead. Fix:
rec = New Rectangle(0, 0, fBitmap.Width, fBitmap.Height)
Do note that you'll no longer see any white since you completely overdrew that. It isn't clear what you meant to do. Perhaps more illustrative is to give it a yellow border:
rec = New Rectangle(0, 0, fBitmap.Width-1, fBitmap.Height-1)
gg.DrawRectangle(Pens.Yellow, rec)
Do favor the Using statement instead of the explicit Dispose() call. Also use it on the brush, it should be disposed as well.

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

GDI+ Moving Line Memory Leak

I have tried to do this both with GDI+/Invalidate and by using a Line Shape Control. In both cases the memory spirals out of control. To demonstrate, create a windows form application with a timer object which is set to 100ms and enabled and use the following code:
Public Class Form1
Private Y As Integer
Private intDirection As Integer = 1
Private Sub timTest_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles timTest.Tick
Me.Invalidate()
End Sub
Private Sub Form1_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles Me.Paint
Dim g As Graphics = Me.CreateGraphics
Dim myPen As New Pen(Color.Black)
myPen.Width = 1
g.SmoothingMode = Drawing2D.SmoothingMode.AntiAlias
g.DrawLine(myPen, 10, 10, 200, Y)
Y += intDirection
If Y > Me.Height - 20 Then intDirection = -1
If Y < 0 Then intDirection = 1
g.Dispose()
myPen.Dispose()
End Sub
End Class
So the code above causes a memory leak as the line moves. I think the reason is that there are unmanaged bitmaps created behind the scenes to paint the form which are not being released because on the managed side it is just a pointer.
If I add the following code at the start of the paint function
Dim intAlloc As Integer = Me.Width * Me.Height * 16
GC.AddMemoryPressure(intAlloc)
and at the end of the function I call
GC.RemoveMemoryPressure(intAlloc)
The memory utilization grows a little and shrinks a little but never grows out of control. The AddMemoryPressure and RemoveMemoryPressure seems to alert the GC that it needs to run. Is there a better way to do this or is this correct? The code above is just a simplification for example purposes to get to the root of a problem I have in a larger component with several moving lines. Also is this the best way to calculate the proper value to place in AddMemoryPressure?

add images to listbox (visual basic)

I have sets of web hosted images that I need my user to be able to select 1 from each. I thought a listbox would work for this, but I can't figure out add an image to one. Is this possible? better way of doing this? I am using the latest free vb.
Use the Listview control instead, it provides better functionality, and doesn't suffer from an annoying resize bug. The listbox is carried over from VB6 days. The listview supports column headers, groupings and a bit more.
Add a Imagelist control to your form, to store the images; set it's ColorDepth property to 32-bit, and set the Listview's LargeImagelist property to the imagelist control you just added (this can all be done in code as well).
Add images to the Imagelist via this code:
ImageList1.Images.Add("imagekey", Image.FromStream(yourimagestream))
Add items to the Listview via this code:
ListView1.Items.Add("list item title", "imagekey")
The "imagekey" is a way to tell the Listview which image to use. You can also use indexes for icons, but specifying an index that doesn't exist will give an index out of range exception, whereas a key that doesn't exist, will just use no image instead.
Oh you also want to set the Listview Multiselect property to False (if you only want them to select one at a time), and access the SelectedIndexChanged() and ItemActivate() events for when the user clicks / double-clicks on items respectively.
Set ListBox1.DrawMode to DrawMode.OwnerDrawFixed or DrawMode.OwnerDrawVariable, and add a handler for drawing the images.
Private Sub listBox1_DrawItem(ByVal sender As System.Object, ByVal e As System.Windows.Forms.DrawItemEventArgs) Handles ListBox1.DrawItem
Dim img As Image
img = sender.items(e.Index)
e.Graphics.DrawImage(img, targetsize)
End Sub
You can add the images to the listbox items collection.
Dim img As Image
img = Image.FromFile("c:\tmp.jpg") ' or whatever
ListBox1.Items.Add(img)
...
Yes, this is possible:
Dim imgList As New ImageList
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
ListView1.View = View.Details
ListView1.Width = 500
ListView1.Columns.Add("Gender", 75, HorizontalAlignment.Left)
ListView1.Columns.Add("Name", 100, HorizontalAlignment.Left)
ListView1.Columns.Add("Notes", 350, HorizontalAlignment.Left)
ListView1.AllowColumnReorder = True
ListView1.Columns(0).DisplayIndex = 1
imgList.Images.Add("Male", Image.FromFile("C:\Users\Joe\Pictures\Male-Symbol.jpg"))
imgList.Images.Add("Female", Image.FromFile("C:\Users\Joe\Pictures\Female-Symbol.jpg"))
ListView1.SmallImageList = imgList
End Sub
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim index As Integer
Select Case True
Case RadioButton1.Checked
index = 0
Case RadioButton2.Checked
index = 1
End Select
Dim lvi As New ListViewItem
lvi.ImageIndex = index
lvi.SubItems.Add(TextBox1.Text)
ListView1.Items.Add(lvi)
End Sub
To resize the image use:
imgList.ImageSize = New Size(100, 14)

How to get image from vb6 MSFlexGrid OLEDragDrop event

I have a VB project that is converted from VB6 to VB.NET.
In this, I have a MSFlexGrid that is used as an interop compatibiliy. That means it is somewhat converted to .NET, but internally, many of the mechanisms are still from VB6/COM.
I need to drag an image from a PictureBox (which is .NET) and drop it on the flexgrid.
This is what I do to initialize the drag:
Private Sub picStartSymbol_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles picStartSymbol.MouseDown
picStartSymbol.DoDragDrop(picStartSymbol.Image, DragDropEffects.Copy)
End Sub
And this is where I catch the drop in the FlexGrid:
Private Sub flxConstructionPoints_OLEDragDrop(ByVal sender As Object, ByVal e As AxMSFlexGridLib.DMSFlexGridEvents_OLEDragDropEvent) Handles flxConstructionPoints.OLEDragDrop
Dim image As Image
Dim oleImage As Object
oleImage = e.data.GetData(2) ''This gets an object of type 2 (bitmap)
''How to convert oleImage to a .NET Image?
End Sub
I don't have VB6 anymore so I can't test this but try adding a reference to Microsoft.VisualBasic.Compatibility and then call:
Dim image as Image = Microsoft.VisualBasic.Compatibility.VB6.IPictureToImage(oleImage)
or
Dim image as Image = Microsoft.VisualBasic.Compatibility.VB6.IPictureDispToImage(oleImage)