Вызывающий поток не может получить доступ к этому объекту, потому что ему принадлежит другой поток

Почему я не могу создать 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

Удачи.