Как я могу получить DocumentViewer WPF для снятия блокировки файла на исходном документе XPS? - PullRequest
9 голосов
/ 18 сентября 2009

После отображения файла 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 , но это не вариант в этом сценарии.

Ответы [ 2 ]

14 голосов
/ 18 сентября 2009

Вам необходимо закрыть 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 сделал это "для меня" за кулисами и забывает убирать за собой.

5 голосов
/ 07 июня 2013

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

XpsDocument реализует IDisposable, что указывает на необходимость использования Dispose () 'd после использования. Проблема заключается в том, что 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. 1015 *

...