Вызывающий поток не может получить доступ к этому объекту, потому что ему принадлежит другой поток
Почему я не могу создать CroppedBitmap в следующем коде? Я получил исключение:
Вызывающий поток не может получить доступ к этому объекту, потому что ему принадлежит другой поток.
Если я изменил код на
CroppedBitmap cb = new CroppedBitmap(new WriteableBitmap(bf), new Int32Rect(1, 1, 5, 5));
исключение не прошло? почему?
Код 1, исключение в cb.Freeze()
:
public MainWindow()
{
InitializeComponent();
ThreadPool.QueueUserWorkItem((o) =>
{
//load a large image file
var bf = BitmapFrame.Create(
new Uri("D:\\1172735642.jpg"),
BitmapCreateOptions.None,
BitmapCacheOption.None);
bf.Freeze();
Dispatcher.BeginInvoke(
new Action(() =>
{
CroppedBitmap cb = new CroppedBitmap(bf, new Int32Rect(1,1,5,5));
cb.Freeze();
//set Image source to cb....
}),
DispatcherPriority.ApplicationIdle);
}
);
}
Код 2, работает:
ThreadPool.QueueUserWorkItem((o) =>
{
var bf = BitmapFrame.Create(
new Uri("D:\\1172740755.jpg"),
BitmapCreateOptions.None,
//BitmapCreateOptions.DelayCreation,
BitmapCacheOption.None);
bf.Freeze();
var wb = new WriteableBitmap(bf);
wb.Freeze();
this.Dispatcher.Invoke(
new Action(() =>
{
var r = new Int32Rect(1, 1, 5, 5);
CroppedBitmap cb = new CroppedBitmap(wb, r);
cb.Freeze();
//set Image source to cb....
Image.Source = cb;
}),
DispatcherPriority.ApplicationIdle);
}
);
Код 3, работает без WritableBitmap:
ThreadPool.QueueUserWorkItem((o) =>
{
var bf = BitmapFrame.Create(
new Uri("D:\\1172735642.jpg"),
BitmapCreateOptions.None,
//BitmapCreateOptions.DelayCreation,
BitmapCacheOption.None);
bf.Freeze();
var bf2 = BitmapFrame.Create(bf);
bf2.Freeze();
this.Dispatcher.Invoke(
new Action(() =>
{
var r = new Int32Rect(1, 1, 5, 5);
BitmapSource cb = new CroppedBitmap(bf2, r);
cb.Freeze();
//set Image source to cb....
Image.Source = cb;
}),
DispatcherPriority.ApplicationIdle);
}
);
Ответы
Ответ 1
Вы можете просматривать эти классы в отражателе. Исключение будет возрастать в cb.Freeze(). В
CroppedBitmap cb = new CroppedBitmap(bf, new Int32Rect(1,1,5,5));
Конструктор case сделал что-то вроде этого:
this.this.Source = source;
Таким образом, источник не был создан в текущем потоке, и поэтому исключение будет расти.
В
new WriteableBitmap(bf)
конструктор синхронизируется с объектом bf, а новый источник создается в текущем потоке, поэтому никаких исключений не будет.
Если вы интересуетесь деталями In Depth, вы всегда можете отображать базовые библиотеки с помощью Reflector:)
Ответ 2
Следующий код может помочь решить проблему обновления элемента gui из другого потока:
Уровень модуля
delegate void updateCallback(string tekst);
Это метод обновления вашего элемента:
private void UpdateElement(string tekst)
{
if (element.Dispatcher.CheckAccess() == false)
{
updateCallback uCallBack = new updateCallback(UpdateElement);
this.Dispatcher.Invoke(uCallBack, tekst);
}
else
{
//update your element here
}
}
Ответ 3
При работе с WPF следует помнить, что если вы создаете объект интерфейса в одном потоке, вы не сможете получить к нему доступ из другого потока. Ваши объекты пользовательского интерфейса должны (как правило) создавать поток пользовательского интерфейса, а затем вам нужен поток пользовательского интерфейса для доступа к ним позже. Ни один другой поток не сможет получить доступ к объектам, созданным в потоке пользовательского интерфейса.
Если вам нужно получить доступ к объекту пользовательского интерфейса из другого потока, вам понадобится поток пользовательского интерфейса Dispatcher, а затем вы можете использовать его для вызова вызовов в потоке пользовательского интерфейса.
Я потратил много часов на расстройство подобных проблем на это - поверьте мне.. Проверьте этот вопрос - он дал мне много полезной информации о предмет.
Ответ 4
У меня была такая же проблема и исправлена проблема, создав мой UIElement
в потоке пользовательского интерфейса, используя его диспетчера (к которому можно получить доступ Application.Current.Dispatcher
).
До:
public static UIElement CreateUIElement()
{
UIElement element = new UIElement();
//Initialized the UIElement here
return element;
}
Этот код вызвал исключение XamlParseException, поскольку он был вызван в другом потоке, чем поток пользовательского интерфейса.
Мое рабочее решение:
public static UIElement CreateUIElement()
{
UIElement element = null;
Application.Current.Dispatcher.Invoke(
System.Windows.Threading.DispatcherPriority.Normal, new Action(
delegate()
{
element = new UIElement();
// Initialize your UIElement here
}));
return element;
}
Более подробную информацию о диспетчере можно найти здесь
http://tech.pro/tutorial/800/working-with-the-wpf-dispatcher
Удачи.