image get locked when assigned as node shape

Jun 6, 2013 at 7:07 AM
Edited Jun 6, 2013 at 7:44 AM

We are using NodeXL library latest version for windows(c#) for a year now. It works quite good for us.
But we struck somewhere when we assigned image as node shape. We actually using following code to do this
 File.Copy(fileDialog.FileName, strPath, true);
                    System.Windows.Media.Imaging.BitmapImage myBitmapImage = new System.Windows.Media.Imaging.BitmapImage();
                    myBitmapImage.DecodePixelHeight = 20;
                    myBitmapImage.DecodePixelWidth = 20;
                    myBitmapImage.UriSource = new Uri(strPath, UriKind.Absolute);
                    System.Windows.Media.ImageSource imgSource = myBitmapImage;
                    selectedvertex.SetValue(ReservedMetadataKeys.PerVertexShape, VertexShape.Image);
                    selectedvertex.SetValue(ReservedMetadataKeys.PerVertexImage, imgSource);     
Now what we doing here, Copy the image file to our application folder and then assign it to node. It works quite seamlessly upto here.

Next part is to remove this image from node and delete this file from application folder. we are using following code to do this
                        selectedvertex.SetValue(ReservedMetadataKeys.PerVertexShape, PerVertexShape.Sphere);
                File.Delete(strPath); // Here Fails Saying file is used by another process
Even after removing the keys from nodes, why image is still used internally by NodeXL and not
allowing us to delete from the folder .

Following is Stack Trace
System.IO.IOException was unhandled
  Message="The process cannot access the file 'D:\\Projects\\VERBALinks\\VERBALinks\\bin\\Debug\\LA_img\\56702.png' because it is being used by another process."
       at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
       at System.IO.File.Delete(String path)
       at VERBALinks.frmMain.menuRightClick_ItemClicked(Object sender, ToolStripItemClickedEventArgs e) in D:\Projects\VERBALinks\VERBALinks\frmMain.cs:line 2874
       at System.Windows.Forms.ToolStrip.OnItemClicked(ToolStripItemClickedEventArgs e)
       at System.Windows.Forms.ToolStripDropDown.OnItemClicked(ToolStripItemClickedEventArgs e)
       at System.Windows.Forms.ToolStrip.HandleItemClick(ToolStripItem dismissingItem)
       at System.Windows.Forms.ToolStripItem.HandleClick(EventArgs e)
       at System.Windows.Forms.ToolStripItem.HandleMouseUp(MouseEventArgs e)
       at System.Windows.Forms.ToolStripItem.FireEventInteractive(EventArgs e, ToolStripItemEventType met)
       at System.Windows.Forms.ToolStripItem.FireEvent(EventArgs e, ToolStripItemEventType met)
       at System.Windows.Forms.ToolStrip.OnMouseUp(MouseEventArgs mea)
       at System.Windows.Forms.ToolStripDropDown.OnMouseUp(MouseEventArgs mea)
       at System.Windows.Forms.Control.WmMouseUp(Message& m, MouseButtons button, Int32 clicks)
       at System.Windows.Forms.Control.WndProc(Message& m)
       at System.Windows.Forms.ScrollableControl.WndProc(Message& m)
       at System.Windows.Forms.ToolStrip.WndProc(Message& m)
       at System.Windows.Forms.ToolStripDropDown.WndProc(Message& m)
       at System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
       at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
       at System.Windows.Forms.NativeWindow.DebuggableCallback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
       at System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW(MSG& msg)
       at System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(Int32 dwComponentID, Int32 reason, Int32 pvLoopData)
       at System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(Int32 reason, ApplicationContext context)
       at System.Windows.Forms.Application.ThreadContext.RunMessageLoop(Int32 reason, ApplicationContext context)
       at System.Windows.Forms.Application.Run(Form mainForm)
       at VERBALinks.Program.Main() in D:\Projects\VERBALinks\VERBALinks\Program.cs:line 22
       at System.AppDomain._nExecuteAssembly(Assembly assembly, String[] args)
       at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
       at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
       at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
       at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
       at System.Threading.ThreadHelper.ThreadStart()
Jun 6, 2013 at 6:37 PM
Because of the way you are loading the image from the file, WPF retains a reference to the file. That prevents the file from being deleted.

You don't need to create a copy of your file, so you can get rid of your File.Copy() call. Instead, tell WPF to load the image into memory using the BitmapImage.CacheOption property.

I'll include sample code in my next post.

-- Tony
Jun 6, 2013 at 6:37 PM
Edited Jun 6, 2013 at 8:03 PM
using System;
using System.Windows.Media.Imaging;
using System.IO;

namespace ConsoleApplication1
class Program
    static void Main(string[] args)
        const String FilePath = @"C:\Temp\Temp.jpg";

        BitmapImage bitmapImage = new BitmapImage();

        // Tell WPF to load the image into memory and then let go of the file.

        bitmapImage.CacheOption = BitmapCacheOption.OnLoad;

        bitmapImage.UriSource = new Uri(FilePath, UriKind.Absolute);

        // The file can now be deleted, because WPF has an in-memory copy of it.


        // (Do something with bitmapImage...)
Jun 6, 2013 at 6:41 PM
Note that with my suggested solution you probably won't have to call File.Delete(), because you won't be making a temporary copy of the file selected by the user.

-- Tony
Jun 7, 2013 at 6:02 AM
Hi Tony,

We are copying this file to application folder for next time reference. User may delete this file from the original location and next time when graph is loaded, image for corrosponding node may not be there. For this reason only we are creating a copy of image and put it into application folder.

we are deleting the image from this folder only when user goes for removing the image from node option in our application.

Thus we are not copying the file for any temporary purpose.

I hope, this makes you clear the scenario....

Is there is no other alternative to overcome this problem ?
Jun 7, 2013 at 6:27 AM
If I understand your problem, you cannot delete the file from disk without getting an IOException involving shared access. The solution is to set the BitmapImage.CacheOption property to BitmapCacheOption.OnLoad when you load the Bitmap from the file, as I indicated in my sample code. If you do that, you can delete the file at any time. If you don't do that (and your code snippet indicates that you aren't doing it now), then WPF will retain a reference to the file and you will be unable to delete it.

It makes no difference whether the file in question is the original file or a copy you've made of it. The cause of the problem is the same, and the solution to the problem is the same.

-- Tony
Jun 7, 2013 at 8:06 AM
Thanks Tony.. it works...