WPFのDocumentViewerを取得して、ソースXPSドキュメントのファイルロックを解除するにはどうすればよいですか?
-
22-07-2019 - |
質問
WPF DocumentViewerでXPSファイルを表示し、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ファイルをリリースしないが、このシナリオのオプションではありません。
解決
ビューアに割り当てられたXpsDocumentを開いたSystem.IO.Packaging.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() '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をClose()するだけで(とにかく行うはずです)、XpsDocumentが処理すべき内部パッケージ管理を掘り下げることなく同じ結果を得ることができます。