Как мне заставить DocumentViewer WPF снять блокировку файла в исходном документе XPS?

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

Вопрос

После отображения файла XPS в WPF DocumentViewer и закрытия экземпляра DocumentViewer файл XPS блокируется, и я не могу его удалить.Мне нужно снять блокировку файла XPS, чтобы я мог удалить его, записать другой файл с тем же именем и, при необходимости, отобразить этот новый файл XPS в новом экземпляре DocumentViewer.Мне нужно сделать это в том же экземпляре приложения, не закрывая его (это сценарий предварительного просмотра перед печатью).

Другими словами, как бы я получил следующий код, чтобы запустить, не бросая исключение в «file.delete (tempxpsfile)»; заявление?

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

Закрытие приложения снимает блокировку файла, как указано в WPF DocumentViewer не выпускает файл XPS, но в данном сценарии это не вариант.

Это было полезно?

Решение

Вам необходимо закрыть System.IO.Packaging.Package, из которого был открыт XpsDocument, назначенный для средства просмотра. Кроме того, если вы хотите снова открыть тот же файл в том же сеансе приложения, вам придется удалить Package из хранилища PackageStore. Закрытие пакета снимет блокировку файла и позволит вам удалить файл, но вы не сможете повторно открыть этот же файл (или, точнее, любой файл в том же месте с тем же именем, даже если он имеет другой контент), пока вы не удалите пакет из PackageStore.

В контексте кода в вопросе вставьте следующее после первого previewWindow.ShowDialog (); перед 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);

Таким образом, сегмент фиксированного кода, представленный в вопросе, становится следующим:

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

Да, я знаю, что не открывал XpsDocument с пакетом - это сделал .NET для " я за кадром и забывает убирать за собой.

Другие советы

Не уверен, к какой версии .Net изначально был задан этот вопрос, и могло ли это измениться между 3.x и 4.x, но, судя по некоторым исследованиям .Net 4.0, похоже, что решение может быть довольно сложным. проще, чем это.

XpsDocument реализует IDisposable, что указывает на необходимость Dispose() после использования.Проблема в том, что IDisposable.Dispose() реализован так, что он скрыт, поэтому вы не можете вызвать его напрямую.Вместо этого вам нужно вызвать Close().Использование dotPeek для анализа XpsDocument.Dispose():

  • XpsDocument.Close() вызывает XpsDocument.Dispose()
  • XpsDocument.Dispose() вызывает XpsManager.Close()
  • XpsManager.Close() вызывает XpsManager.RemovePackageReference()
  • XpsManager.RemovePackageReference() вызывает PackageStore.RemovePackage() и Package.Close()

Поэтому, если я что-то не упускаю, простое выполнение Close() XpsDocument (что вы все равно должны сделать) должно привести к тому же результату без необходимости копаться во внутренних материалах управления пакетами, которые должен обрабатывать XpsDocument.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top