wpf - Image edit and save Out of Memory Exception C# -
i'm working in wpf application show images in 2 places means same image gets loaded in 2 places. in 1 of place image shown along other few images in slider able edit , save. if there no image available in location should showing separate image image not found not editable.
when started working on functionality got used process exception during edit , save. after searching came solution , @ rare time out of memory exception when click next or previous or first or last in slider. slider image control 4 buttons. when buttons clicked below method called. i'm not sure if there memory leaks.
bool noimage = true; private static readonly object _syncroot = new object(); private bitmapsource loadimage(string path) { lock (_syncroot) //lock object doesn't executed more once @ time. { bitmapdecoder decoder = null; try { //if image not found in folder, show image not found. if (!file.exists(path) && (path != null)) { system.drawing.bitmap ss = xxxx.resources.imagenotfound; var stream = new system.io.memorystream(); if (!file.exists(path.gettemppath() + "imagenotfound.jpg")) { filestream file = new filestream(path.gettemppath() + "imagenotfound.jpg", filemode.create, fileaccess.write); ss.save(stream, imageformat.jpeg); stream.position = 0; stream.writeto(file); file.close(); stream.close(); } path = path.combine(path.gettemppath(), "imagenotfound.jpg"); noimage = false; } else { if (!enableforedit) noimage = false; else noimage = true; } if (!string.isnullorempty(path) && (!noimage || file.exists(path))) { using (var stream = new filestream(path, filemode.open, fileaccess.read)) { decoder = bitmapdecoder.create(stream, bitmapcreateoptions.none, bitmapcacheoption.onload); } return decoder.frames.firstordefault(); } else return null; } catch (outofmemoryexception ex) { messagebox.show("insufficient memory handle process. please try again later.", "application alert"); return null; } catch (exception ex) { // error handling. throw new applicationexception(ex.message); } { decoder = null; gc.waitforfullgccomplete(1000); gc.collect(0, gccollectionmode.forced); } } } <image x:name="viewimage" grid.row="2" height="100" width="135" source="{binding displayimage, mode=twoway, updatesourcetrigger=propertychanged, notifyonsourceupdated=true}" />
if approach wrong, let me know should change or if there simpler way do. kindly help.
note: images loaded above 5mb
firstly when ever create stream need dispose of once finished (note close not dispose, dispose close), if not stream stays in memory consuming resources
so code should follows
using(var stream = new system.io.memorystream()) { if (!file.exists(path.gettemppath() + "imagenotfound.jpg")) { using(filestream file = new filestream(path.gettemppath() + "imagenotfound.jpg", filemode.create, fileaccess.write)) { ss.save(stream, imageformat.jpeg); stream.position = 0; stream.writeto(file); } } }
second need reduce apps memory impact
to suggest leveraging functionality in wpf here quick example of how should this
your model
public class imageitem { public uri uri{ get; set; } private bitmapsource _source; public bitmapsource source { { try { if (_source == null) _source = new bitmapimage(uri);//lazy loading } catch (exception) { _source = null; } return _source; } } public void save(string filename) { var img = bitmapframe.create(source); var encoder = new jpegbitmapencoder(); encoder.frames.add(img); using(var savestream = system.io.file.openwrite(filename)) encoder.save(savestream) } }
your view model
public class imagelist : inotifypropertychanged { public event propertychangedeventhandler propertychanged; public observablecollection<imageitem> images { get; } = new observablecollection<imageitem>(); private int _selectedindex; // c# >= 6 public static readonly propertychangedeventargs selectedindexproperty = new propertychangedeventargs(nameof(selectedindex)); // c# < 6 // public static readonly propertychangedeventargs selectedindexproperty = new propertychangedeventargs("selectedindex"); public int selectedindex { { return _selectedindex; } set { _selectedindex = value; // c# >= 6 propertychanged?.invoke(this, selectedindexproperty); propertychanged?.invoke(this, currentimageproperty); // c# < 6 // var handler = propertychanged; // if(handler !=null) // { // handler (this, selectedindexproperty); // handler (this, currentimageproperty); // } } } // c# >= 6 public static readonly propertychangedeventargs currentimageproperty = new propertychangedeventargs(nameof(currentimage)); // c# < 6 // public static readonly propertychangedeventargs currentimageproperty = new propertychangedeventargs("currentimage"); public imageitem currentimage => images.count>0 ? images[selectedindex] : null; public void next() { if (selectedindex < images.count - 1) selectedindex++; else selectedindex = 0; } public void back() { if (selectedindex == 0) selectedindex = images.count - 1; else selectedindex--; } public void add(string filename) { images.add(new imageitem() { uri= new uri(filename) }); // c# >= 6 propertychanged?.invoke(this, currentimageproperty); // c# < 6 // var handler = propertychanged; // if(handler !=null) // { // handler (this, currentimageproperty); // } } }
and view
<window x:class="imagedemo.mainwindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:imagedemo" mc:ignorable="d" title="mainwindow" height="350" width="525"> <window.resources> <bitmapimage x:key="notfound" urisource="c:\...\notfound.png"/> </window.resources> <window.datacontext> <local:imagelist/> </window.datacontext> <dockpanel> <button content="<" click="back_click"/> <button dockpanel.dock="right" content=">" click="next_click"/> <image source="{binding currentimage.source, mode=oneway, targetnullvalue={staticresource notfound}, fallbackvalue={staticresource notfound}}"/> </dockpanel> </window> public partial class mainwindow : window { public mainwindow() { initializecomponent(); } //c# >= 6 private imagelist list => datacontext imagelist; //c# < 6 //private imagelist list {get{ return datacontext imagelist;}} private void next_click(object sender, routedeventargs e) { list.next(); } private void back_click(object sender, routedeventargs e) { list.back(); } }
note: because model separate view can show same image in several places no issue @ all
also system.drawing.bitmap
not wpf compatible should use wpf classes in system.windows.media.imaging
Comments
Post a Comment