Вопрос

Я пытаюсь выяснить, как я могу скопировать DispatcherObject (в моем случае Bitmapsource) в другой ветку.

В случае использования:
У меня есть приложение WPF, которое должно показать окно в новой ветке (приложение на самом деле является Addin Outlook, и нам нужно сделать это, потому что у Outlook есть несколько крючков в основной ветке пользовательского интерфейса и крадут определенные горячие клавиши, которые нам нужно использовать - В переводе «Interop of Outlook, WPF (который мы используем для пользовательского интерфейса) и Winforms (нам нужно использовать определенные средства управления Winforms, предоставленные Microsoft)).

При этом у меня есть реализация WPFMessagebox, которая настроена путем установки некоторых статических свойств - и один из них является Bitmapsource для значка. Это используется таким образом, чтобы при запуске я мог установить wpfmessagebox.icon один раз, и с тех пор каждый wpfmessagebox будет иметь одинаковый значок.

Проблема заключается в том, что Bitmapsource, который присваивается в значке, является диспетчером, и когда он прочитал, он будет выбросить Invalidoperation Exception: «Вызовный поток не может получить доступ к этому объекту, потому что другой поток владеет им».

Как я могу клонировать этот Bitmapsource в реальную ветку? У него есть методы Clone () и CloneCurrentValue (), которые не работают (они также бросают такое же исключение). Мне также пришло в голову использовать OriginalIcon.dispatcher.invoke (сделайте клонирование здесь) - но диспетчер Bitmapsource - нулевой, и все же - я бы создал копию в неправильном потоке и все еще не мог бы использовать ее на моем. Bitmapsource.isfrozen == true.

Есть идеи о том, как скопировать Bitmapsource в другой поток (без полной реконструкции его из файла изображения в новом потоке)?

РЕДАКТИРОВАТЬ:Итак, замораживание не помогает: в конце концов, у меня есть растровый рамский каплей (window.icon не принимает никакого другого вида изображений в любом случае), и когда я назначаю его как окно .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);

Это идет по цене, но это довольно дешево по сравнению с сериализацией.

Длинный ответ.

Один обходной путь, который работает, хотя и не очень производительный, - это создание потока памяти из данных изображения, а затем реконструировать изображение в потоке, на котором вы хотите использовать его.

Пример для 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;
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top