4

In my WPF app, I am creating a FlowDocument by building its XAML markup as a string, and then using XamlReader.Parse to turn the string into a FlowDocument object, which I then save to an XPS document file. It works.

I needed to include an image in my document, and so to achieve this, I create and save the image as a temporary file in the temp directory, and then reference it with an absolute path in my FlowDocument's XAML. This works too - during the XPS document creation process, the image actually gets embedded into the XPS document, which is great.

But the problem is, my app retains a file lock on this image until the app quits.

I am cleaning up all resources. There is NO file lock on my generated XPS file - just the image file. If I comment out the part of my code which creates the XPS file, then the image file does not get locked.

My code (I'm on .NET 4 CP):

var xamlBuilder = new StringBuilder();

// many lines of code like this
xamlBuilder.Append(...);

// create and save image file
// THE IMAGE AT THE PATH imageFilePath IS GETTING LOCKED
// AFTER CREATING THE XPS FILE
var fileName = string.Concat(Guid.NewGuid().ToString(), ".png");
var imageFilePath = string.Format("{0}{1}", Path.GetTempPath(), fileName);
using (var stream = new FileStream(imageFilePath, FileMode.Create)) {
  var encoder = new PngBitmapEncoder();
  using (var ms = new MemoryStream(myBinaryImageData)) {
    encoder.Frames.Add(BitmapFrame.Create(ms));
    encoder.Save(stream);
  }
  stream.Close();
}

// add the image to the document by absolute path
xamlBuilder.AppendFormat("<Paragraph><Image Source=\"{0}\" ...", imageFilePath);

// more lines like this
xamlBuilder.Append(...);

// create a FlowDocument from the built string
var document = (FlowDocument) XamlReader.Parse(xamlBuilder.ToString());

// set document settings
document.PageWidth = ...;
...

// save to XPS file
// THE XPS FILE IS NOT LOCKED. IF I LEAVE OUT THIS CODE
// AND DO NOT CREATE THE XPS FILE, THEN THE IMAGE IS NOT LOCKED AT ALL
using (var xpsDocument = new XpsDocument(filePath, FileAccess.ReadWrite)) {
  var documentWriter = XpsDocument.CreateXpsDocumentWriter(xpsDocument);
  documentWriter.Write(((IDocumentPaginatorSource) document).DocumentPaginator);
  xpsDocument.Close();
}

(Actually, the fact that's it's a dynamically generated image in the temp directory is irrelevant - this issue occurs if I hard code in the path of any image file on my machine - it will get locked.)

One would think that there is a bug in the XPS creation code that causes the file lock.

Is there something else I can try? Or a way to remove the file lock via code?

Ross
  • 4,460
  • 2
  • 32
  • 59
  • I used to have the same issue. I solved it somehow. Let me see if I can find the question I asked somewhere (if I even asked it on here). I think it had something to do with freezing the image by using the `Freeze()` method. – Mathias Lykkegaard Lorenzen May 02 '12 at 23:44
  • In the FlowDocument markup, I've tried adding the po:Freeze="True" attribute (with the xmlns:po declaration in root tag) - didn't work unfortunately. – Ross May 03 '12 at 07:13
  • Have you tried to declare the image like this: – Simon Mourier May 22 '12 at 09:41
  • Thanks Simon. Unfortunately, "None" didn't work for the CacheOption. But it lead me to a search in which I found this page: http://www.devnewsgroups.net/group/microsoft.public.dotnet.framework/topic59281.aspx, in which the user prevents file locking by using the "OnLoad" CacheOption. So I did that, and it works! Awesome. – Ross May 22 '12 at 13:39
  • BTW, put in an answer and I'll award the bounty, I wouldn't have figured it out without your input. – Ross May 22 '12 at 13:39

1 Answers1

2

You could change your xaml like this instead:

<Image>
    <Image.Source>
        <BitmapImage CacheOption="None" UriSource="your path" />
    </Image.Source>
</Image>

to be able to play with the CacheOption parameter, to specify how the Xaml Builder should load the image file, as the default value seems to be keeping a lock on it (waiting for the GC to do its work it seems).

Here is some related question here on SO: How do you make sure WPF releases large BitmapSource from Memory?

Community
  • 1
  • 1
Simon Mourier
  • 132,049
  • 21
  • 248
  • 298