Cant use my object before dispose - vb.net

Just like the title said, i cannot use my object if i dipose it, i mean before i dispose it, i cannot use it. here is my code
Public Class Form1
Dim hasil(3) As Bitmap
Dim OneClickInsertGbr As New ArrayList
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
hasil(0) = New Bitmap("C:\Users\Robokidz\Google Drive\Tugas Akhir\Data Training\Foto Uang\1K\scan0001.jpg") 'This is my image
hasil(1) = New Bitmap("C:\Users\Robokidz\Google Drive\Tugas Akhir\Data Training\Foto Uang\1K\scan0001.jpg") 'This is my image
hasil(2) = New Bitmap("C:\Users\Robokidz\Google Drive\Tugas Akhir\Data Training\Foto Uang\1K\scan0002.jpg") 'This is my image
PictureBox1.Image = hasil(0)
hasil(0).Dispose()
hasil(1).Dispose()
hasil(2).Dispose()
End Sub
End Class
after i run, it generate error
Parameter is not valid.
after that check and see what is the reason behind the error, i know the problem is dispose. Because after i delete all of that dispose, it work fine, but the other problem rise.
Out of memory
i know this error, because i use too many memory.
my question is.
How to use an object and dispose it without getting that error?

The idea is that you dispose an object when you are finished using it. If you want the user to see an Image displayed in a PictureBox then you obviously haven't finished with that Image, so you shouldn't be disposing it. If you are later going to replace that Image in the PictureBox with another one, THEN you should dispose the Image that you are no longer going to display because you are now finished with it.
Here's an example of the sort of thing you need to do:
Private Sub SetPictureBox1Image(filePath As String)
If PictureBox1.Image IsNot Nothing Then
'Dispose an existing Image first.
PictureBox1.Image.Dispose()
End If
'Display the new Image.
PictureBox1.Image = Image.FromFile(filePath)
End Sub

Related

PictureBox stops adding images

I am using a worker thread to load .png images (from a path string) into a global PictureBox2 object, and then exit the _RunWorkerCompleted to use the PictureBox2's width and height for additional work in the method called processpic2. Everything works fine until about the 5th or 6th images were added to the PB. A this point, in the processpic2 method, an exception is thrown since the image property of the PictureBox2 evaluates to nothing.
Why would a PB stop taking images after a while?
Public Class Form1
Public WithEvents BackgroundWorker1 As New System.ComponentModel.BackgroundWorker
Private Sub BackGroundworker1_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
Dim args As ArgumentType = e.Argument
PictureBox2.Image = Nothing
PictureBox2.Invalidate()
Dim img As Image
Using str As Stream = File.OpenRead(args._pathstr)
img = Image.FromStream(str)
End Using
PictureBox2.Image = img
e.Result = "done"
End Sub
Private Sub BackgroundWorker1_RunWorkerCompleted(ByVal sender As System.Object, ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs) _Handles BackgroundWorker1.RunWorkerCompleted
'Called when the BackgroundWorker is completed.
processpic2()
End Sub
Sub btnLoadPicture(pathstr)
Dim args As ArgumentType = New ArgumentType()
args._pathstr = pathstr
BackgroundWorker1.RunWorkerAsync(args)
End Sub
Sub processpic2()
If PictureBox2.Image Is Nothing Then MsgBox("Image is Nothing")
End Sub
The whole point of a BackgroundWorker is to do background work. Making changes to the UI is the exact opposite of background work. It is foreground work. If your task is to clear the current contents of a PictureBox, load an image from a file and then display that image, only the middle step is background work so only the middle step should be done in the DoWork event handler. The first step should be done before you call RunWorkerAsync and the last step should be done in the RunWorkerCompleted event handler.
Having said all that, why use a BackgroundWorker at all in this case? Why not simply call the LoadAsync method of the PictureBox itself?
SOLUTION - thanks to the suggestions received, and what I found on MSDN regarding the LoadAsync method of PictureBox the following code solved the issue:
PictureBox2.Image = Nothing
PictureBox2.WaitOnLoad = False
' Load the image asynchronously.
PictureBox2.LoadAsync(pathstr)

VB.NET How can I check if an image from my resources is loaded in a PictureBox?

I am trying to make a PictureBox change the image when pressed, and if pressed again it will change to the original image. How can I do that? Here is my code.
Private Sub PictureBox1_Click(sender As Object, e As EventArgs) Handles PictureBox1.Click
If (PictureBox1.Image = WindowsApplication1.My.Resources.Resources.asd) Then
PictureBox1.Image = WindowsApplication1.My.Resources.Resources._stop()
Else
PictureBox1.Image = WindowsApplication1.My.Resources.Resources.asd()
End If
End Sub
When I run it, it gives the following error:
Operator '=' is not defined for types "Image" and "Bitmap".
Well, it is the good kind of problem to have. There is a massive bear trap hidden in the My.Resources property getters, every time you use it you get a new bitmap object. That has many consequences, bitmaps are very expensive objects and calling their Dispose() method is very important to prevent your program from running out of memory. And comparing will always fail since it is new object. The difference between Image and Bitmap is just a minor problem.
It is crucial to use the bitmap object just once. Like this:
Private asd As Image = My.Resources.asd
Private _stop As Image = My.Resources._stop
Now you can correctly write this code since you are comparing objects for reference identity:
Private Sub PictureBox1_Click(sender As Object, e As EventArgs) Handles PictureBox1.Click
If PictureBox1.Image = asd Then
PictureBox1.Image = _stop
Else
PictureBox1.Image = asd
End If
End Sub
And like a good programmer you dispose the image objects when you no longer use them:
Private Sub Form1_FormClosed(sender As Object, e As FormClosedEventArgs) Handles MyBase.FormClosed
asd.Dispose()
_stop.Dispose()
End Sub
Also fix the code that first assigns the PictureBox1.Image property, we can't see it.
You can use the PictureBox's .Tag property to store information. For this, I will store the resource name.
If you have an array of the resource names to be used, you can get the next one (using Mod to wrap around from the last one to the first (zeroth - array indices start at zero in VB.NET) entry).
Private Sub PictureBox1_Click(sender As Object, e As EventArgs) Handles PictureBox1.Click
Dim imageResourceNames = {"Image1", "Image2"}
Dim pb = DirectCast(sender, PictureBox)
Dim tag = CStr(pb.Tag)
pb.Image?.Dispose()
Dim nextImage = imageResourceNames((Array.IndexOf(imageResourceNames, tag) + 1) Mod imageResourceNames.Length)
pb.Image = DirectCast(My.Resources.ResourceManager.GetObject(nextImage), Image)
pb.Tag = nextImage
End Sub
Please change "Image1" and "Image2" as appropriate.
Array.IndexOf will return -1 if the item searched for is not in the array, but we are adding 1 to it so it will get the first item of the array (at index 0) if the .Tag has not been set.
If you had a third image, you would simply add its name into the array.
The line PictureBox1.Image?.Dispose() disposes of the resources used by the image - the ? makes it only do that if PictureBox1.Image is not Nothing.
When you first set the image of the PictureBox, remember to set its .Tag property appropriately so that it behaves as intended.
I used Dim pb = DirectCast(sender, PictureBox) so that you can simply copy-and-paste the code for a different PictureBox and there will be very little to change in the code - otherwise you would have to update the references to PictureBox1 all through it, which can be error-prone. Of course, at that point you would start thinking about refactoring it so that you are not repeating code (the "Don't repeat yourself", or DRY, principle).

ListView displays incorrect images for each item

I have a listview that lists all items from a directory like so:
But as you can see in this image:
They are not in order, because one image text has been skipped entirely, however the image itself is still in order.
But, when it gets further down the list, starting from about half way they start becoming completely mixed up like in this example:
Image 3
When clicking an image it shows the preview of the correct image on the right side.
image 4
This is the code I'm using to load in all the images:
Dim imgList As New ImageList
Dim imgSize As New Size
Dim count As Integer = 0
Dim imgFilename As String
Private Sub BackgroundWorker1_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
'Dim imlTemp As New ImageList
Dim dirFiles() As String = IO.Directory.GetFiles(My.Settings.GalleryLocation)
'Dim item As New ListViewItem
For Each dirFile As String In dirFiles
imgFileName = IO.Path.GetFileName(dirFile)
Dim img As New System.Drawing.Bitmap(dirFile)
Dim imgImage As Image = Image.FromFile(dirFile)
'Dim imgHeight As Integer
'imgHeight = imgImage.Height
imgSize.Width = 120
imgSize.Height = 174
Threading.Thread.Sleep(10)
BackgroundWorker1.ReportProgress(100 / ((dirFiles.Count + 1) - count), img)
Next
End Sub
Private Sub BackgroundWorker1_ProgressChanged(ByVal sender As Object, ByVal e As System.ComponentModel.ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged
ListView1.SmallImageList = imgList
ListView1.LargeImageList = imgList
imgList.ImageSize = imgSize
imgList.ColorDepth = ColorDepth.Depth32Bit
ListView1.Items.Add(imgFilename, count)
imgList.Images.Add(e.UserState.Clone)
count += 1
'ListView1.EnsureVisible(ListView1.Items.Count - 1)
End Sub
Before I added in the backgroundworker it had tremendous loading times for a large amount of images, so I thought I'd implement the backgroundworker to allow async work to be done. However, something is going completely wrong at the start of the task and multiple times part way through the list, where it completely messes up as shown in Image 3.
Does anybody have any idea what is going wrong, or any alternative solutions to what I'm aiming to do?
Since ReportProgress() is not blocking (as far as I know), there is a chance that the files are iterated though faster than the UI can update.
To keep this synchronized you should make a custom class holding everything you want to update and pass that to the ReportProgress() method (even making some steps automated).
For example:
Public Class GalleryImage
Public Property FullPath As String
Public Property FileName As String
Public Property [Image] As Image
Public Sub New(ByVal File As String)
Me.Image = Image.FromFile(File) 'Get the image.
Me.FullPath = File 'Save the full path of the image.
Me.FileName = Path.GetFileName(File) 'Get the file name.
End Sub
End Class
(Also, if you want the file name but not the extension you can use Path.GetFileNameWithoutExtension())
Now to your code. First of all the counter variable isn't really necessary as it can be replaced by either ListView1.Items.Count or imgList.Images.Count.
Secondly, you shouldn't constantly keep setting the imgSize variable nor the ListView's Small-/LargeImageList properties. Doing that is completely unnecessary and will slow things down. For the ListView just set the image list once, and for the imgSize variable you can do like this:
Dim ReadOnly imgSize As New Size(120, 174)
Making the variable ReadOnly does what it sounds like; you can read from it, but not modify it.
Now to fix these other things we'll start in the BackgroundWorker's For Each loop:
For Each dirFile As String In dirFiles
Threading.Thread.Sleep(10) 'You don't need this Sleep if you don't want to.
'Report the progress, declare an in-line version of our class and send it with.
BackgroundWorker1.ReportProgress(100 / ((dirFiles.Count + 1) - ListView1.Items.Count), New GalleryImage(dirFile)) 'A new GalleryImage is created. The property setting is handled by the class itself.
Next
As you see we have now narrowed the code down rather much.
Now we are going to handle the ReportProgess() event:
Private Sub BackgroundWorker1_ProgressChanged(ByVal sender As Object, ByVal e As System.ComponentModel.ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged
Dim img As GalleryImage = e.UserState 'Retrieve our class, no need to clone it.
imgList.Images.Add(img.Image) 'Add the image first (I don't know for sure, but if you add the image after, wouldn't it require to redraw the ListView an extra time?).
ListView1.Items.Add(img.FileName, imgList.Images.Count - 1) 'Add the new item with the values from our class.
End Sub
And lastly, the initialization and initial setting of stuff should be done in for example the Form Load event:
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
'In here we do all the standard settings and initializations.
ListView1.SmallImageList = imgList
ListView1.LargeImageList = imgList
imgList.ImageSize = imgSize
imgList.ColorDepth = ColorDepth.Depth32Bit
End Sub
And by that I think I should have covered it all.
Hope this helps!

How can I get rid of this error? Visual Basic 2010

I have an array of pictureboxes, like this (they all have an image displayed on them, from the designer, from my.resources):
Dim imgSourcePic() As PictureBox = {imgAcronym, imgAcrostic, imgAdjective, imgAdverb, imgAlliteration, imgApostrophe, imgClause, imgComma, imgDialogue}
and another array of pictureboxes (which don't have an image displayed, yet):
Dim imgDefinitiontoMatch() As PictureBox = {imgDefinition1, imgDefinition2, imgDefinition3, imgDefinition4, imgDefinition5}
In the sub NewGame() I have a line of code which is:
imgDefinitiontoMatch(intX).Image = imgSourcePic(intRandomNumber).Image
But, whenever it executes this line of code I get this error:
I debugged it and saw that the pictureboxes, in the array, are displaying the Image property as 'Nothing'.
How else can I assign an image to the pictureboxes in imgSourcePic() that'll work?
I have tried creating variables for all of the images, such as:
Dim picAcronym As Image = My.Resources.Acronymn_definition
...
But still doesn't achieve anything.
I hope this isn't a duplicate, but I have been trying to figure it out all day and can't seem to make
it work :(
Many thanks and, as always, hope this makes sense.
You're populating those arrays before InitializeComponent() runs, so all of the variables are still Nothing.
You need to assign the array in Sub New(), after InitializeComponent() (which creates the actual PictureBoxes)
You can also use the Load() Event of the Form, which I see you already have in your code:
Public Class Form1
Private PBs() As PictureBox
Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
PBs = {PictureBox1, PictureBox2, PictureBox3}
' ... more code ...
End Sub
End Class

Error with code?

Hi can anyone tell me why the following dose not work:
(p.s I dont want the file to append upon clicking abutton just upon clicking the checkbox.
Private Sub CheckBox1_CheckedChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles CheckBox1.CheckedChanged
Dim FILE_NAME As String = "C:\RXF\log.txt"
'Adding items for AutoCAD 2006...
If CheckBox1.CheckState = CheckState.Checked Then
Dim objWriter As New System.IO.StreamWriter(FILE_NAME, True)
objWriter.WriteLine("module: 4FNV-67-5H")
objWriter.Close()
End If
End Sub
End Class
Not reproducible, even with your exact code as posted. This works perfectly fine for me, creating a text file in the specified location if one does not exist and appending the specified text to the end of the file.
The only thing I suggest is wrapping your StreamWriter object in a Using statement to ensure that its Dispose method always gets called, even if an exception is thrown (which is all the more likely when you're doing disk I/O). So, your existing code would simply change to:
Private Sub CheckBox1_CheckedChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles CheckBox1.CheckedChanged
Dim FILE_NAME As String = "C:\RXF\log.txt"
''#Adding items for AutoCAD 2006...
If CheckBox1.CheckState = CheckState.Checked Then
Using objWriter as New System.IO.StreamWriter(FILE_NAME, True)
objWriter.WriteLine("module: 4FNV-67-5H")
objWriter.Close()
End Using
End If
End Sub
Also, if you anticipate this method getting called a lot (i.e., the user clicking and unclicking and clicking the checkbox repeatedly), you might consider creating the StreamWriter object once and saving it as a private class-level variable, instead of creating and disposing of it each time the method is called. Then you just have to make sure that you dispose of it whenever your class (presumably the containing form) is disposed.