Comment obtenir que DocumentViewer de WPF libère son verrou de fichier sur le document XPS source?

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

Question

Après avoir affiché un fichier XPS dans WPF DocumentViewer et fermé l'instance de DocumentViewer, le fichier XPS est verrouillé et je ne peux pas le supprimer. Je dois libérer le verrou sur le fichier XPS pour pouvoir le supprimer, en écrire un autre portant le même nom et éventuellement afficher ce nouveau fichier XPS dans une nouvelle instance de DocumentViewer. Je dois le faire dans la même instance d'application, sans avoir à fermer l'application (scénario d'aperçu avant impression).

En d'autres termes, comment puis-je exécuter le code suivant sans déclencher une exception au niveau de "File.Delete (tempXpsFile);" déclaration?

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();

La fermeture de l'application déclenche le verrouillage du fichier, comme indiqué dans WPF DocumentViewer ne libérez pas le fichier XPS , mais ce n’est pas une option dans ce scénario.

Était-ce utile?

La solution

Vous devez fermer le fichier System.IO.Packaging.Package à partir duquel le document XpsDocument affecté au visualiseur a été ouvert. En outre, si vous souhaitez pouvoir ouvrir le même fichier à nouveau au cours de la même session d'application, vous devez supprimer le package du PackageStore. La fermeture du package libère le verrou de fichier et vous permet de supprimer le fichier, mais vous ne pourrez alors pas rouvrir ce même fichier (ou plus précisément tout fichier du même emplacement portant le même nom, même s'il a déjà été ajouté.) contenu différent) jusqu'à ce que vous supprimiez le package du PackageStore.

Dans le contexte du code de la question, insérez ce qui suit après le premier previewWindow.ShowDialog (); avant le 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);

Le segment de code fixe présenté dans la question devient:

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();

Oui, je sais que je n'ai pas ouvert le document XpsDocument avec un package. NET l'a fait "pour" et ";" moi dans les coulisses et oublie de nettoyer après lui-même.

Autres conseils

Je ne sais pas quelle version de .Net cette question a été posée à l'origine, ni si cela a changé entre les versions 3.x et 4.x, mais d'après certaines investigations menées contre .Net 4.0, la solution pourrait être un peu plus simple que cela.

XpsDocument implémente IDisposable, indiquant qu’il doit être Dispose () après utilisation. Le problème, c’est que IDisposable.Dispose () est implémenté de telle sorte qu’il est caché afin que vous ne puissiez pas l’appeler directement. Vous devez appeler Close () à la place. Utilisation de dotPeek pour analyser XpsDocument.Dispose ():

  • XpsDocument.Close () appelle XpsDocument.Dispose ()
  • XpsDocument.Dispose () appelle XpsManager.Close ()
  • XpsManager.Close () appelle XpsManager.RemovePackageReference ()
  • XpsManager.RemovePackageReference () appelle PackageStore.RemovePackage () et Package.Close ()

Donc, à moins que quelque chose ne me manque, juste fermer () le XpsDocument (ce que vous êtes supposé faire de toute façon) devrait donner le même résultat sans avoir à fouiller dans la gestion interne des paquets que XpsDocument devrait gérer.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top