DispatcherObject(BitMapsource)を別のスレッドにコピーする方法は?
-
02-10-2019 - |
質問
DispatcherObject(私の場合はBitMapsource)を別のスレッドにコピーするにはどうすればよいですか?
使用事例:
新しいスレッドにウィンドウを表示する必要があるWPFアプリがあります(アプリは実際にはOutlook Addinです。OutlookにはメインUIスレッドにいくつかのフックがあり、使用する必要がある特定のホットキーを盗んでいるため、これを行う必要があります。 Outlook、WPF(UIに使用)、およびWinforms(特定のMicrosoftが提供するWinformsコントロールを使用する必要がある)の翻訳 'では)。
それで、いくつかの静的プロパティを設定することによって構成されているWPFMessageBoxの実装があり、そのうちの1つはアイコンのビットマプシュースです。これは、スタートアップでwpfmessagebox.iconを1回設定できるように使用され、それ以来、すべてのwpfmessageboxが同じアイコンを持つことができます。
問題は、アイコンに割り当てられているBitMapsourceがDispatcherObjectであり、読み取るとInvalidoperationExceptionをスローすることです。
そのbitmapsourceを実際のスレッドにクローンするにはどうすればよいですか? Clone()およびCloneCurrentValue()メソッドがありますが、機能しません(同じ例外をスローします)。また、Originalicon.dispatcher.invoke(ここでクローニングを行います)を使用することもできましたが、ビットマプセースのディスパッチャーはnullです。 bitmapsource.isfrozen == true。
Bitmapsourceを別のスレッドにコピーする方法については(新しいスレッドの画像ファイルから完全に再構築することなく)?
編集:したがって、凍結は役に立ちません:最終的に私はビットマップフレームを持っています(window.iconはとにかく他の種類の画像を取得しません)。 InvalidOperationExceptionを取得:「異なるスレッドが所有しているため、呼び出しスレッドはこのオブジェクトにアクセスできません。」次のスタックトレースで:
WindowsBase.dll!System.Windows.Threading.Dispatcher.VerifyAccess() + 0x4a bytes
WindowsBase.dll!System.Windows.Threading.DispatcherObject.VerifyAccess() + 0xc bytes
PresentationCore.dll!System.Windows.Media.Imaging.BitmapDecoder.Frames.get() + 0xe bytes
PresentationFramework.dll!MS.Internal.AppModel.IconHelper.GetIconHandlesFromBitmapFrame(object callingObj = {WPFControls.WPFMBox.WpfMessageBoxWindow: header}, System.Windows.Media.Imaging.BitmapFrame bf = {System.Windows.Media.Imaging.BitmapFrameDecode}, ref MS.Win32.NativeMethods.IconHandle largeIconHandle = {MS.Win32.NativeMethods.IconHandle}, ref MS.Win32.NativeMethods.IconHandle smallIconHandle = {MS.Win32.NativeMethods.IconHandle}) + 0x3b bytes
> PresentationFramework.dll!System.Windows.Window.UpdateIcon() + 0x118 bytes
PresentationFramework.dll!System.Windows.Window.SetupInitialState(double requestedTop = NaN, double requestedLeft = NaN, double requestedWidth = 560.0, double requestedHeight = NaN) + 0x8a bytes
PresentationFramework.dll!System.Windows.Window.CreateSourceWindowImpl() + 0x19b bytes
PresentationFramework.dll!System.Windows.Window.SafeCreateWindow() + 0x29 bytes
PresentationFramework.dll!System.Windows.Window.ShowHelper(object booleanBox) + 0x81 bytes
PresentationFramework.dll!System.Windows.Window.Show() + 0x48 bytes
PresentationFramework.dll!System.Windows.Window.ShowDialog() + 0x29f bytes
WPFControls.dll!WPFControls.WPFMBox.WpfMessageBox.ShowDialog(System.Windows.Window owner = {WPFControlsTest.MainWindow}) Line 185 + 0x10 bytes C#
解決
重要なのは次のとおりです 作成 使用するスレッドのビットマップ。したがって、いくつかの静的フィールド/プロパティにアイコンをキャッシュすることはできません。新しいスレッドで新しいウィンドウを開くたびに(ファイル、リソース、ストリームなど)バッドロード(ファイル、リソース、ストリームなど)。
BitMapFrameは、作成されたスレッドでのみ使用できます。
あなたが正しく述べたように、クローニングでさえここでは機能しません(ただ吸うだけです)。
私は持っていた まさに 私の特定のケースでは、単に呼び出すだけで、同じ問題と解決しました。
// get your stream somewhere -
window.Icon = BitmapFrame.Create(stream)
そして、これがWPFでリソースからアイコンを取得する方法です。
var streamResourceInfo = Application.GetResourceStream(new Uri(@"pack://application:,,,/YourAssembly;relative path to the icon", UriKind.RelativeOrAbsolute));
// use streamResourceInfo.Stream
他のヒント
電話したら Freeze
, 、複数のスレッドで動作するはずです。
bitmapSourceForOtherThread = new WriteableBitmap(previousBitmapSource);
これには価格がありますが、シリアル化と比較してかなり安いです。
長い答え.
動作する1つの回避策は、あまりパフォーマンスではありませんが、画像データからメモリストリームを作成し、使用するスレッドの画像を再構築することです。
の例 BitmapSource
:
Dispatcher.BeginInvoke(DispatcherPriority.Normal, (Action)delegate()
{
//serialize image on UI thread
imageStream = GetImageBytes(cameraImage);
}
...
//reconstruct image on a different thread:
Bitmap bitmap = new Bitmap(imageStream);
private MemoryStream GetImageBytes(BitmapSource image)
{
MemoryStream ms = new MemoryStream();
BitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(image));
encoder.Save(ms);
ms.Seek(0, SeekOrigin.Begin);
return ms;
}