Every time the user presses a button my application gets the image from the web, but this slow since the image isn't getting cached. Any way I can cache the image so it doesn't download it over and over making things faster.
This is my code so you know how my application works.
if (listBox1.SelectedIndex == 0)
{
richTextBox1.Text = "Explore any game with Dex";
pictureBox1.Load("THE LINK ");
ProtoxRe.lol = 0;
}
else if ((listBox1.SelectedIndex == 1))
{
richTextBox1.Text = "SOME TEXT";
pictureBox1.Load("LINK");
ProtoxRe.lol = 1;
}
There's probably more sophisticated methods, and this assumes your image is not expected to change, but you can do something like this:
//Image class is from this library
using System.Drawing;
//The scope of this will depend on where you need to access it from
Image img;
private Image GetImage()
{
if (img == null)
{
img = GetImageFromHttp();
}
return img;
}
This is very basic, and has a number of flaws; you're not caching it between sessions so every application start will require the http get, and you're not checking if the image on the web is different, but it's a starting point.
Related
I am doing an image processing project using Windows Forms (c#). You can see the design of my application below.
What does this app do : take the original image, create a copy and modify the copy.
My app is working well but, if I process the same original image another time without closing the app, I get an error due to (I think) the display of the modified image. I think that the display on the bottom right corner uses the resources of the image and, when I try to modify it again, the system considers that the image is already used by another program so it can't be modified.
So my question is : "How can I stop using the modified image if the user clicks on PROCESS again ?"
I tried to use the .Dispose() method but it didn't work.
Code of the c# function linked to the PROCESS button :
private async void button8_Click(object sender, EventArgs e)
{
// start the waiting animation
progressBar1.Visible = true;
progressBar1.Style = ProgressBarStyle.Marquee;
if (csI != csP)
{
MessageBox.Show("The selected profil does not match the selected image. Colorspaces are different.", "WARNING",
MessageBoxButtons.OK, MessageBoxIcon.Error);
}
else
{
pictureBox2.Image = null;
if (checkBox2.Checked == false)
{
rendered = false;
button8.Enabled = false;
await Task.Run(() => wrapper.DominantColors(trackBar1.Value, rendered));
//wrapper.DominantColors(trackBar1.Value, rendered);
}
else
{
rendering = comboBox1.Text;
string outputImage = wrapper.Link(rendering, bpc);
rendered = true;
button8.Enabled = false;
await Task.Run(() => wrapper.DominantColors(trackBar1.Value, rendered));
//wrapper.DominantColors(trackBar1.Value, rendered);
}
// re-enable things
button8.Enabled = true;
progressBar1.Visible = false;
MessageBox.Show("processing done");
Bitmap bit = new Bitmap(imgDstPath);
float WidthImg = bit.Width;
float HeightImg = bit.Height;
float alpha = WidthImg / pictureBox2.Width;
float beta = HeightImg / pictureBox2.Height;
alpha = Math.Max(alpha, beta);
float newWidthf = WidthImg / alpha;
float newHeightf = HeightImg / alpha;
int newHeight = (int)newHeightf;
int newWidth = (int)newWidthf;
pictureBox2.ClientSize = new Size(newWidth, newHeight);
pictureBox2.Image = bit;
pictureBox2.SizeMode = PictureBoxSizeMode.StretchImage;
}
}
If possible, I'd like to clear the use of the resources when I click on the process button.
Thank you in advance for your help
The basic rule is that all objects you create that implements IDisposable need to be disposed. When writing winforms apps all controls added to a forms are disposed when the form is disposed. But whenever you change things you might need to handle disposal yourself.
For example:
pictureBox2.Image = bit;
If pictureBox2.Image is already set to something you need to ensure that it is disposed.
var oldImage = pictureBox2.Image;
pictureBox2.Image = bit;
oldImage.Dispose();
I'm not sure this is the actual problem you are having, your example code is insufficient to make that determination. To discover this you need to debug your program! Start by examining your exceptions, does it fail when opening a file or some other resource? Where was that resource created? where is it disposed? Perhaps even use a memory debugger to produce a list of all objects that are alive to see if there are any suspicious objects kept around. Will disposal correctly occur if any arbitrary code throws an exception?
It is sometimes useful to check the identity of objects in the debugger, to see if it has been switched out, or see what object your breakpoint was triggered in. You can rightclick an object in the watch panel in visual studio and select "Make ObjectId", this will associate a number with the object that appears at the end of the value.
If anyone in the future wants to know the solution I found, here it is :
At the beginning of the PROCESS function I added those simple lines :
if (pictureBox2.Image != null)
{
pictureBox2.Image.Dispose();
pictureBox2.Image = null;}
I have a winforms application where user can select an image from a list of available images and the corresponding image is shown in a PictureBox. The images can be very huge with a minimum of 10MB. This obviously makes the rest of the UI unresponsive while the image loads. So I thought of loading the image on a separate thread using the following code:
private void LoadImage()
{
// loadViewerThread is a Thread object
if (loadViewerThread != null && loadViewerThread.IsAlive)
{
loadViewerThread.Abort(); // Aborting the previous thread if the user has selected another image
}
loadViewerThread = new Thread(SetViewerImage);
loadViewerThread.Start();
}
The SetViewerImage function is as below:
private void SetViewerImage()
{
if (pictureBox1.Image != null)
pictureBox1.Image.Dispose();
pictureBox1.Image = new Bitmap(/*Some stream*/);
}
After this the images load smoothly and the UI can also be accessed.
But if the user moves very fast between the set of images then a big red X mark comes up. This happens because I have that call to Dispose in SetViewerImage.
I have assigned an ErrorImage to the PictureBox but that ErrorImage is never shown in this case.
Questions:
Is there anything wrong with my thread implementation? Why does the
Image gets disposed?
Is there any way that I can display a different ErrorImage and not
the red X?
You need to manipulate controls in the UI thread. You can do this by using Control.Invoke() from a different thread.
The big bottleneck is creating the image from the stream, so you should be able to reorganize your method like this to keep the UI thread freed up:
private void SetViewerImage()
{
Bitmap image = new Bitmap(/* Some stream*/);
pictureBox1.Invoke(new Action(() =>
{
if (pictureBox1.Image != null)
{
pictureBox1.Image.Dispose();
pictureBox1.Image = null; // This might help? Only add if it does.
}
pictureBox1.Image = image;
}));
}
I’m using WinForms. My application works like a simple image document (tif) viewer. The tif/image documents I work with have multiple pages, so by pressing the next button my application goes to the next page. The problem with my application is that it is slow when I move to the next page. Previously, my application was slower. I’ve modified the application a little bit to make it faster, but it’s still not fast enough.
I’ve compared my application’s speed to Windows Photo Viewer and the result was that my application still needs improvement on performance. Does anyone know how I could make my application faster?
In the link below I’ve provided a sample tif document for testing purposes.
Link: http://www.filedropper.com/tiftestingdoc
My Code:
FileStream _stream;
Image _myImg; // setting the selected tiff
string _fileName;
private int intCurrPage = 0; // defining the current page
private void Open_Btn_Click(object sender, EventArgs e)
{
if (openFileDialog1.ShowDialog() == DialogResult.OK)
{
lblFile.Text = openFileDialog1.FileName; //Shows the filename in the lable
Image img = Image.FromFile(openFileDialog1.FileName);
pictureBox1.Image = img;
Size size = new Size(img.Height, img.Width);
pictureBox1.Size = size;
Open_Image_Control();
}
}
public void Open_Image_Control()
{
Image myBmp;
if (_myImg == null) //I made a copy of the file because i want to be able to modify the file in the directory for example go to directory and delete the file while still having the ability to view it on the application
{
_fileName = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString("N"));
File.Copy(#"C:\my_Image_document", _fileName);
_stream = new FileStream(_fileName, FileMode.Open, FileAccess.Read);
_myImg = Image.FromStream(_stream);
}
int intPages = _myImg.GetFrameCount(System.Drawing.Imaging.FrameDimension.Page); // getting the number of pages of this tiff
intPages--; // the first page is 0 so we must correct the number of pages to -1
lblNumPages.Text = Convert.ToString(intPages); // showing the number of pages
lblCurrPage.Text = Convert.ToString(intCurrPage); // showing the number of page on which we're on
_myImg.SelectActiveFrame(System.Drawing.Imaging.FrameDimension.Page, intCurrPage); // going to the selected page
myBmp = new Bitmap(_myImg, pictureBox1.Width, pictureBox1.Height);
//myBmp = new Bitmap(_myImg, pictureBox1.Height, pictureBox1.Width);
pictureBox1.Image = myBmp; // showing the page in the pictureBox1
}
private void NextPage_btn_Click(object sender, EventArgs e)
{
if (intCurrPage == Convert.ToInt32(lblNumPages.Text)) // if you have reached the last page it ends here
// the "-1" should be there for normalizing the number of pages
{ intCurrPage = Convert.ToInt32(lblNumPages.Text); }
else
{
intCurrPage++; //page increment (Goes to next page)
Open_Image_Control();
}
}
Load the image directly into the PictureBox, then to change pages call the pictureBox1.Image.SelectActiveFrame() method directly and refresh the PictureBox.
This prevents making new bitmaps copies of each page each time.
That was causing extra memory to be allocated each time and slowness while it copies all the pixels from the page.
Please see the code changes below:
// Variable to hold the current page number
private int intCurrPage = 0;
private int intTotalPages = 0;
private void Open_Btn_Click(object sender, EventArgs e)
{
if (openFileDialog1.ShowDialog() == DialogResult.OK)
{
lblFile.Text = openFileDialog1.FileName;
// Before loading you should check the file type is an image
pictureBox1.Image = Image.FromFile(openFileDialog1.FileName);
pictureBox1.Size = new Size(pictureBox1.Image.Height, pictureBox1.Image.Width);
// Reset the current page when loading a new image
intCurrPage = 0;
intTotalPages = pictureBox1.Image.GetFrameCount(System.Drawing.Imaging.FrameDimension.Page);
lblNumPages.Text = intTotalPages.ToString();
}
}
private void NextPage_btn_Click(object sender, EventArgs e)
{
// Check that the current page is not going past the max page
if (intCurrPage < (intTotalPages-1))
{
//page increment (Go to next page)
intCurrPage++;
// Directly increment the active frame within the image already in the PictureBox
pictureBox1.Image.SelectActiveFrame(System.Drawing.Imaging.FrameDimension.Page, intCurrPage);
// Adjust the size of the picturebox control to the size of the current page.
// not sure if this is necessary, but including it due to prior example
pictureBox1.Size = new Size(pictureBox1.Image.Height, pictureBox1.Image.Width);
// Refresh the PictureBox so that it will show the currently active frame
pictureBox1.Refresh();
lblCurrPage.Text = intCurrPage.ToString();
}
}
Also, your code to copy the image had a hard coded directory name that was throwing errors for me, so I removed that.
I have an OpenFileDialog and PictureBox in user control. To understand the problem better I'll explain in few words how this user control works. The user can select an image to be opened for the form. The name of this image is saved in a DataBase and the file for the image is copied in a default location. When there is some image saved in the database it is load in the picturebox when the form with the picturebox control is loaded. If the user select another image and want to save the form with the new image I have a method that takes care to delete the old image file from my default location and that is where the problem occurs.
When I have loaded image and try to save new one, sometimes (very rare in fact) I get an error that The resource is being used by another process.. I can paste the exact error if needed. I think that the problem is caused from the picturebox and the way it deals with images.
Here is my code:
if (openFileDialog1.ShowDialog() == DialogResult.OK)
{
try
{
if (MyImage != null)
{
MyImage.Dispose();
}
selectedFile = openFileDialog1.FileName;
selectedFileName = openFileDialog1.SafeFileName;
MyImage = new Bitmap(openFileDialog1.FileName);
pictureBox1.Image = (Image)MyImage;
int imageWidth = pictureBox1.Image.Width;
int picBoxWidth = pictureBox1.Width;
if (imageWidth != 0 && picBoxWidth > imageWidth)
{
pictureBox1.Width = imageWidth;
}
else
{
pictureBox1.Width = defaultPicBoxWidth;
}
}
catch (Exception ex)
{
logger.Error(ex.ToString());
MessageBox.Show("Error loading image!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
and my delete method:
public void DeleteImage(AppConfig imageInfo, string imageName)
{
string imgPath = imageInfo.ConfigValue.ToString();
try
{
File.Delete(imgPath + "\\" + imageName);
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
I thought that :
if (MyImage != null)
{
MyImage.Dispose();
}
will deal with this problem but still sometimes it occurs. And because it's not everytime it's even more critical to deal with it because at some point I may decide that I have solved it but in fact to be just lucky for some time.
MyImage = new Bitmap(openFileDialog1.FileName);
pictureBox1.Image = (Image)MyImage;
Yes, that code puts a lock on the file. The lock is produced by a memory mapped file object that GDI+ creates to efficiently map the pixel data of the file into memory without having to allocate space in the paging file. You will not be able to delete the file as long as the image is displayed in the picture box and not disposed, the lock prevents that. You will have to dispose the image and set the Image property back to null before you can delete the file.
You can prevent the file from getting locked by making an in-memory copy of the image:
using (var temp = new Bitmap(openFileDialog1.FileName)) {
pictureBox1.Image = new Bitmap(temp);
}
It is not as efficient of course, to be avoided if the image is large. And do beware that another process may in fact have a similar lock on the file. Nothing you can do about that.
A major difficulty with things like PictureBox is that because a PictureBox has no way of knowing whether it is the only user of an image, it consequently has no way of knowing whether it should dispose of that image when it no longer needs it.
Consequently, whatever code owns a picture box must also take ownership of the image associated therewith. There are three approaches I could suggest for doing so:
Create a control derived from PictureBox which documents itself as assuming ownership of any image given to it. Such a control should probably replace the image property with a SetImageWithOwnership method (with the semantics that once an image is passed to the PictureOwningBox, the box will be expected to "own" it, and will dispose it either when the box is Disposed or when a different image is given to the box).
Attach event handlers to a PictureBox to handle the scenarios where either the box is destroyed or a different image is assigned to it.
Have any code which would cause the PictureBox to be disposed or have a different image loaded, also dispose the Image that had been assigned to it.
While there may be cases where it would be appropriate to call GC.Collect and let the garbage-collector take care of things, such an approach is generally unsound.
try that:
using(Bitmap MyImage = new Bitmap(openFileDialog1.FileName))
{
pictureBox1.Image = (Image)MyImage;
int imageWidth = pictureBox1.Image.Width;
int picBoxWidth = pictureBox1.Width;
if (imageWidth != 0 && picBoxWidth > imageWidth)
{
pictureBox1.Width = imageWidth;
}
else
{
pictureBox1.Width = defaultPicBoxWidth;
}
}
I've had problems like this before, and one way that I've found to make sure that the resource is released, even after Dispose(), which really only marks the object for removal by the garbage collector, is by using GC.Collect(). I'm sure that there is a cleaner way to handle the resource disposal, but the time that it takes the GC.Collect() to run shouldn't hinder your program.
I develop a Winform Application with the framlework .NET 3.5 in C#.
I would like to allow the user to drag&drop a picture from Word 2007. Basically the user open the docx, select a picture and drag&drop them to my PictureBox.
I've already done the same process with picture files from my desktop and from Internet pages but I can't go through my problem with my Metafile. I've done few researches but I didn't find any solutions solving my issue.
Here is what I've done on my Drag&Drop event :
private void PictureBox_DragDrop(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent(DataFormats.MetafilePict)){
Image image = new Metafile((Stream)e.Data.GetData(DataFormats.MetafilePict));
}
}
I can obtain a stream with this code : (Stream)e.Data.GetData(DataFormats.MetafilePict) but I don't know how to convert it into a Metafile or better an Image object.
If you have any idea or solution, I'll be glad to read it.
Thanks,
Here is a working example of Drag n Drop from Word (not for PowerPoint and Excel):
static Metafile GetMetafile(System.Windows.Forms.IDataObject obj)
{
var iobj = (System.Runtime.InteropServices.ComTypes.IDataObject)obj;
var etc = iobj.EnumFormatEtc(System.Runtime.InteropServices.ComTypes.DATADIR.DATADIR_GET);
var pceltFetched = new int[1];
var fmtetc = new System.Runtime.InteropServices.ComTypes.FORMATETC[1];
while (0 == etc.Next(1, fmtetc, pceltFetched) && pceltFetched[0] == 1)
{
var et = fmtetc[0];
var fmt = DataFormats.GetFormat(et.cfFormat);
if (fmt.Name != "EnhancedMetafile")
{
continue;
}
System.Runtime.InteropServices.ComTypes.STGMEDIUM medium;
iobj.GetData(ref et, out medium);
return new Metafile(medium.unionmember, true);
}
return null;
}
private void Panel_DragDrop(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent(DataFormats.EnhancedMetafile) & e.Data.GetDataPresent(DataFormats.MetafilePict))
{
Metafile meta = GetMetafile(e.Data);
Image image = meta;
}
}
After this you can use image.Save to save picture or you can use it on picturebox or other control.
I think you need to call new Metafile(stream) as there is no method .FromStream in Metafile.
I'm still digging into he web to try different way to solve my issue.
Hopefully I've found this unanswered thread talking about my problem but without any response :
Get Drag & Drop MS Word image + DataFormats.EnhancedMetafile & MetafilePict :
http://www.codeguru.com/forum/showthread.php?t=456722
I work around with another io be able to copy floating Image (Image stored in Shape and not InlineShape) with Word 2003 and pasting into my winform. I can't paste the link of the second source (because of my low reputation on this website) but I'll do if someone request.
So apparently there are a common issue with the fact that you cannot access to your Metafile stored in the Clipboard and by Drag&Drop.
I still need to understand how to get my Metafile by Drag&Drop.