2009-09-18 41 views
9

在WPF DocumentViewer中顯示XPS文件並關閉DocumentViewer實例後,XPS文件被鎖定,我無法將其刪除。我需要釋放XPS文件上的鎖,以便我可以刪除它,使用相同的名稱編寫另一個,並可選擇在新的DocumentViewer實例中顯示該新的XPS文件。我需要在同一個應用程序實例中執行此操作,而無需關閉應用程序(這是打印預覽方案)。如何讓WPF的DocumentViewer在源XPS文檔上發佈其文件鎖定?

換句話說,我將如何獲得下面的代碼來運行,而不會在「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 doesn't release the XPS file提及,但不是在這種情況下的一個選項。

回答

14

您需要關閉從其中打開分配給查看器的XpsDocument的System.IO.Packaging.Package。此外,如果您希望能夠在同一個應用程序會話中再次打開相同的文件,則必須從PackageStore中刪除Package。關閉軟件包將釋放文件鎖並允許您刪除該文件,但是您將無法重新打開該文件(或者更準確地說,即使該文件具有相同名稱的同一位置的任何文件不同的內容),直到你從PackageStore中移除Package。

在問題代碼的上下文中,在第一個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做到了「爲」我的背後場景和忘記清理後自己。

+0

這一個引起了我的注意,我有一個瀏覽器下載一個文檔,一旦它設置了文檔,即使在應用這些更改後它也會失敗。當我加載文檔時,我正在處理原始文檔,這會在我重新加載文檔時導致發佈失敗。 – 2011-05-10 13:27:07

4

不知道最初問這個問題的最初版本是.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()

所以,除非我失去了一些東西,只是關閉()荷蘭國際集團的XpsDocument(你」應該是d無論如何)都應該達到相同的結果,而不必深入XpsDocument應該處理的內部包管理。

+0

這可以說是最簡單的方法! – 2014-11-24 10:02:29