Как загрузить изображения в фоновом режиме? - PullRequest
3 голосов
/ 04 апреля 2009

Я пытаюсь загрузить изображение в фоновом режиме, а затем обновить интерфейс. Я играл с этим весь день, и я не знаю, чего мне не хватает. Я продолжаю получать следующую ошибку:

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

Я искал следующий пример за примером, но, похоже, не могу найти ответ. Я также обернул код, который касается пользовательского интерфейса, в другой BeginInvoke.

Обновление 3: мораль истории. ImageSource не является потокобезопасным для доступа.

Обновление 2: это должно быть простое решение :). Я попытался клонировать, но это не привело к успеху, но я получил другую ошибку: «Исключение было выдано целью вызова».

Обновление 1: я пробовал BackgroundWorker, но все еще получаю ту же ошибку, но она возникает на brush.ImageSource.Height. Я правильно сигнализирую пользовательский интерфейс? Есть предложения?

Вот мой XAML:

<Window x:Class="Slideshow.Show"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <DockPanel>
        <Canvas Background="Black" Name="canvas">
            <Viewbox Name="background" Stretch="Uniform">
                <Rectangle name="background" />
            </Viewbox>
        </Canvas>
    </DockPanel>
</Window>

Вот часть кода:

namespace Slideshow
{
    public class Show 
    {
        public Show()
        {
            BackgroundWorker bw = new BackgroundWorker();
            bw.DoWork += new DoWorkEventHandler(bw_DoWork);
            bw.RunWorkerCompleted += 
                new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
            bw.RunWorkerAsync();
        }

        void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            BitmapSource bitmap = e.Result as BitmapSource;

            if (bitmap != null)
            {
                this.Dispatcher.BeginInvoke(DispatcherPriority.Normal 
                    (ThreadStart)delegate()
                {
                    Image image = new Image();
                    image.Source = bitmap;
                    background.Child = image;
                 });
            }
        }

        void bw_DoWork(object sender, DoWorkEventArgs e)
        {
            BitmapSource bitmapSource = CreateBrush(GetRandomFile());
            e.Result = bitmapSource;
         }
    }
}

Ответы [ 5 ]

11 голосов
/ 04 апреля 2009

То, что я склонен использовать ThreadPool.QueueUserWorkItem для загрузки изображения, затем, когда операция завершается, я перезваниваю в поток пользовательского интерфейса, используя поточно-безопасный Dispatcher объект , Источник изображения не является потокобезопасным, вам придется использовать что-то вроде JpegBitmapDecoder, также есть PngBitmapDecoder.

Например:

public Window()
{
    InitializeComponent();

    ThreadPool.QueueUserWorkItem(LoadImage,
         "http://z.about.com/d/animatedtv/1/0/1/m/simp2006_HomerArmsCrossed_f.jpg");
}

public void LoadImage(object uri)
{
    var decoder = new JpegBitmapDecoder(new Uri(uri.ToString()), BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.OnLoad);
    decoder.Frames[0].Freeze();
    this.Dispatcher.Invoke(DispatcherPriority.Send, new Action<ImageSource>(SetImage), decoder.Frames[0]);
}

public void SetImage(ImageSource source)
{
    this.BackgroundImage.Source = source;
}
2 голосов
/ 08 апреля 2009

Еще одна вещь, которую мы имели в нашем проекте. Поскольку ImageSource размещен в пользовательском интерфейсе, вы должны проверить, заморожен ли он:

public void SetImage(ImageSource source)
{
   ImageSource src = null;
   if(!source.IsFrozen)
       src = source.GetAsFrozen();
   else
       src = source; 
   this.BackgroundImage.Source = src;
}
1 голос
/ 04 апреля 2009

Вы хотите включить несколько потоков в ваше приложение WPF. Как указано в статье ниже, WPF заставляет вас выполнять всю работу с пользовательским интерфейсом в потоке, который создал пользовательский интерфейс.

Проверьте это: http://pjbelfield.wordpress.com/2007/10/29/worker-threads-and-the-dispatcher-in-wpf/

и для интенсивной работы пользовательского интерфейса:

http://eprystupa.wordpress.com/2008/07/28/running-wpf-application-with-multiple-ui-threads/

0 голосов
/ 04 апреля 2009

Ошибка, которую вы получаете сейчас, является хорошей новостью, это означает, что BackgroundWorker работает. Вы можете попробовать клонировать его в RunWorkerCompleted с brush.Clone();.

Попробуйте удалить часть Dispatcher.BeingInvoke. Весь смысл BackgroundWorker заключается в том, что событие автоматически направляется обратно в поток пользовательского интерфейса, поэтому вам не о чем беспокоиться.

0 голосов
/ 04 апреля 2009

Ваш код для загрузки изображения (.jpg) в память должен выполняться в фоновом режиме. Код для загрузки изображения из памяти в элемент управления WPF должен выполняться в обычном потоке пользовательского интерфейса WPF. Только медленные / долго выполняющиеся задачи должны быть помещены в фоновый поток. Как только длинное задание завершено, он должен дать сигнал потоку пользовательского интерфейса обновить представление с результатом длинного задания.

Мой любимый способ - использовать класс BackgroundWorker:

var bg = new System.ComponentModel.BackgroundWorker();
bg.DoWork += Work_Function;
bg.RunWorkerCompleted += UI_Function;
bg.RunWorkerAsync();

void Work_Function(object sender, DoWorkEventArgs e) { ... }
void UI_Function(object sender, RunWorkerCompletedEventArgs e) { ... }

Когда вы вызываете RunWorkerAsync (), сначала вызывается ваша Work_Function (). Когда он завершен, вызывается UI_Function (). Рабочая функция может поместить одну переменную в свойство DoWorkEventArgs.Result, которое доступно из свойства RunWorkerCompletedEventArgs.Result.

Думаю, вас может заинтересовать эта статья: http://www.codeproject.com/KB/WPF/WPF_Explorer_Tree.aspx В ней показано, как настроить отложенную загрузку в виде дерева в WPF. Я думаю, что основная концепция (добавить фиктивный узел, перехватить отображение фиктивного узла, загрузив вместо этого следующее изображение) применима к вашей работе.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...