¿Cómo consigo el DocumentViewer de WPF para liberar su bloqueo de archivo en el documento XPS de origen?

StackOverflow https://stackoverflow.com/questions/1442607

Pregunta

Después de mostrar un archivo XPS en el WPF DocumentViewer y cerrar la instancia de DocumentViewer, el archivo XPS se bloquea y no puedo eliminarlo. Necesito liberar el bloqueo en el archivo XPS para poder eliminarlo, escribir otro con el mismo nombre y, opcionalmente, mostrar ese nuevo archivo XPS en una nueva instancia de DocumentViewer. Necesito hacer esto en la misma instancia de la aplicación, sin tener que cerrar la aplicación (este es un escenario de Vista previa de impresión).

En otras palabras, ¿cómo podría ejecutar el siguiente código sin lanzar una excepción en el " File.Delete (tempXpsFile); " declaración?

var tempXpsFile = @"c:\path\to\Temporary.xps";

var previewWindow = new Window();
var docViewer = new DocumentViewer();
previewWindow.Content = docViewer;

GenerateXpsFile(tempXpsFile);

var xpsDocument = new XpsDocument(tempXpsFile);

previewWindow.ShowDialog();

File.Delete(tempXpsFile);  //this will throw an exception due to a file lock on tempXpsFile

GenerateXpsFile(tempXpsFile); //assume this generates a different file
//otherwise the scenario doesn't make sense as we could just skip the above delete
//and this statement and re-use the same file

previewWindow = new Window();
docViewer = new DocumentViewer();
previewWindow.Content = docViewer;

previewWindow.ShowDialog();

Al cerrar la aplicación se libera el bloqueo de archivos, como se menciona en WPF DocumentViewer no no publique el archivo XPS , pero esa no es una opción en este escenario.

¿Fue útil?

Solución

Debe cerrar el System.IO.Packaging.Package desde el que se abrió el XpsDocument asignado al visor. Además, si desea poder abrir el mismo archivo nuevamente dentro de la misma sesión de aplicación, deberá eliminar el paquete de PackageStore. Al cerrar el paquete, se liberará el bloqueo de archivos y le permitirá eliminar el archivo, pero no podrá volver a abrir ese mismo archivo (o, más precisamente, cualquier archivo en la misma ubicación con el mismo nombre, incluso si tiene contenido diferente) hasta que elimine el paquete de PackageStore.

En el contexto del código en la pregunta, inserte lo siguiente después de la primera previewWindow.ShowDialog (); antes de File.Delete (tempXpsFile);

//Get the Uri from which the system opened the XpsPackage and so your XpsDocument
var myXpsUri = xpsDocument.Uri; //should point to the same file as tempXpsFile

//Get the XpsPackage itself
var theXpsPackage = System.IO.Packaging.PackageStore.GetPackage(myXpsUri);

//THIS IS THE KEY!!!! close it and make it let go of it's file locks
theXpsPackage.Close();

//if you don't remove the package from the PackageStore, you won't be able to
//re-open the same file again later (due to System.IO.Packaging's Package store/caching
//rather than because of any file locks)
System.IO.Packaging.PackageStore.RemovePackage(myXpsUri);

Entonces el segmento de código fijo presentado en la pregunta se convierte en:

var tempXpsFile = @"c:\path\to\Temporary.xps";

var previewWindow = new Window();
var docViewer = new DocumentViewer();
previewWindow.Content = docViewer;

GenerateXpsFile(tempXpsFile);

var xpsDocument = new XpsDocument(tempXpsFile);

previewWindow.ShowDialog();

//BEGIN NEW CODE
var myXpsUri = xpsDocument.Uri; //should point to the same file as tempXpsFile
var theXpsPackage = System.IO.Packaging.PackageStore.GetPackage(myXpsUri);
theXpsPackage.Close();
System.IO.Packaging.PackageStore.RemovePackage(myXpsUri);
//END NEW CODE

File.Delete(tempXpsFile);  //this will succeed now

GenerateXpsFile(tempXpsFile);

previewWindow = new Window();
docViewer = new DocumentViewer();
previewWindow.Content = docViewer;

previewWindow.ShowDialog();

Sí, sé que no abrí el XpsDocument con un paquete - .NET lo hizo " para " yo detrás de escena y se olvida de limpiar después de sí mismo.

Otros consejos

No estoy seguro de a qué versión de .Net se le hizo originalmente esta pregunta con respecto, o si esto podría haber cambiado entre 3.xy 4.x, pero de alguna investigación en contra de .Net 4.0 parece que la solución podría ser bastante más simple que esto.

XpsDocument implement IDisposable, lo que indica que debe ser Dispose () 'd después del uso. La arruga es que IDisposable.Dispose () se implementa de tal manera que está oculto para que no pueda llamarlo directamente. En su lugar, debe llamar a Close (). Usando dotPeek para analizar XpsDocument.Dispose ():

  • XpsDocument.Close () llama a XpsDocument.Dispose ()
  • XpsDocument.Dispose () llama a XpsManager.Close ()
  • XpsManager.Close () llama a XpsManager.RemovePackageReference ()
  • XpsManager.RemovePackageReference () llama a PackageStore.RemovePackage () y Package.Close ()

Entonces, a menos que me falte algo, solo cerrar () el XpsDocument (que se supone que debes hacer de todos modos) debería lograr el mismo resultado sin tener que profundizar en las cosas de administración de paquetes internos que XpsDocument debería manejar.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top