Загрузка изображения из сети в wpf / surface - PullRequest
5 голосов
/ 19 ноября 2010

Я пытаюсь загрузить изображения из Интернета в моем приложении wpf.

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

Проблема: при загрузке всплывающего окна система зависает во время ожидания изображений.Я связываю изображения из своего кода.Изображения хранятся в ObservableCollection.Я пытался использовать поток для загрузки изображений, но каждый раз, когда я сталкивался с исключением, говоря, что поток не является владельцем объекта.

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

        IList<Image> imagesFromWeb = downloadImagesFromWeb(url);


        DispatcherHelper.UIDispatcher.Invoke(DispatcherPriority.Normal, (ThreadStart)delegate()
        {
            foreach (Image img in imagesFromWeb 
            {
                this.ObservableCollection_Images.Add(img);
            }
    }

Как только изображения загружаются, и он пытается добавить изображения в (уже открытое) всплывающее окно, я получаю исключение, говорящее, что поток не является владельцем объекта

Может кто-нибудь указать мне правильное направление?

Ответы [ 3 ]

14 голосов
/ 07 декабря 2010

Если у вас есть изображение, доступное на общедоступном веб-сервере, к которому можно обратиться с помощью обычного HTTP URI, вы можете напрямую указать источник:

<Image Source="http://www.someserver.com/myimage.png" />

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

Конечно, вы можете сделать это и с привязкой данных:

<Image Source="{Binding TheImage}" />

И в модели представления

public string TheImage 
{ 
    get { return "http://www.someserver.com/myimage.png"; } 
}    
2 голосов
/ 19 ноября 2010

Вы можете получить множество проблем с коллекциями, WPF, связыванием и многопоточностью

Лучше всего (на мой взгляд) использовать безопасную для диспетчеров наблюдаемую коллекцию

Вот реализация, которая также включает в себя безопасность потоков:

public class SafeObservable<T> : IList<T>, INotifyCollectionChanged, INotifyPropertyChanged
{
    private readonly IList<T> collection = new List<T>();
    private readonly Dispatcher dispatcher;
    public event NotifyCollectionChangedEventHandler CollectionChanged;
    public event PropertyChangedEventHandler PropertyChanged;
    private readonly ReaderWriterLock sync = new ReaderWriterLock();

    public SafeObservable()
    {
        dispatcher = Dispatcher.CurrentDispatcher;
    }

    public void Add(T item)
    {
        if (Thread.CurrentThread == dispatcher.Thread)
            DoAdd(item);
        else
            dispatcher.BeginInvoke((Action)(() => DoAdd(item)));
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs("Count"));
    }

    private void DoAdd(T item)
    {
        sync.AcquireWriterLock(Timeout.Infinite);
        collection.Add(item);
        if (CollectionChanged != null)
            CollectionChanged(this,
                new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item));
        sync.ReleaseWriterLock();
    }

    public void Clear()
    {
        if (Thread.CurrentThread == dispatcher.Thread)
            DoClear();
        else
            dispatcher.BeginInvoke((Action)(DoClear));
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs("Count"));
    }

    private void DoClear()
    {
        sync.AcquireWriterLock(Timeout.Infinite);
        collection.Clear();
        if (CollectionChanged != null)
            CollectionChanged(this,
                new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
        sync.ReleaseWriterLock();
    }

    public bool Contains(T item)
    {
        sync.AcquireReaderLock(Timeout.Infinite);
        var result = collection.Contains(item);
        sync.ReleaseReaderLock();
        return result;
    }

    public void CopyTo(T[] array, int arrayIndex)
    {
        sync.AcquireWriterLock(Timeout.Infinite);
        collection.CopyTo(array, arrayIndex);
        sync.ReleaseWriterLock();
    }

    public int Count
    {
        get
        {
            sync.AcquireReaderLock(Timeout.Infinite);
            var result = collection.Count;
            sync.ReleaseReaderLock();
            return result;
        }
    }

    public bool IsReadOnly
    {
        get { return collection.IsReadOnly; }
    }

    public bool Remove(T item)
    {
        if (Thread.CurrentThread == dispatcher.Thread)
            return DoRemove(item);
        var op = dispatcher.BeginInvoke(new Func<T, bool>(DoRemove), item);
        if (op == null || op.Result == null)
            return false;
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs("Count"));
        return (bool)op.Result;
    }

    private bool DoRemove(T item)
    {
        sync.AcquireWriterLock(Timeout.Infinite);
        var index = collection.IndexOf(item);
        if (index == -1)
        {
            sync.ReleaseWriterLock();
            return false;
        }

        var result = collection.Remove(item);
        if (result && CollectionChanged != null)
            CollectionChanged(this, new
                NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));

        sync.ReleaseWriterLock();
        return result;
    }

    public IEnumerator<T> GetEnumerator()
    {
        return collection.GetEnumerator();
    }

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        return collection.GetEnumerator();
    }

    public int IndexOf(T item)
    {
        sync.AcquireReaderLock(Timeout.Infinite);
        var result = collection.IndexOf(item);
        sync.ReleaseReaderLock();
        return result;
    }

    public void Insert(int index, T item)
    {
        if (Thread.CurrentThread == dispatcher.Thread)
            DoInsert(index, item);
        else
            dispatcher.BeginInvoke((Action)(() => DoInsert(index, item)));
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs("Count"));
    }

    private void DoInsert(int index, T item)
    {
        sync.AcquireWriterLock(Timeout.Infinite);
        collection.Insert(index, item);
        if (CollectionChanged != null)
            CollectionChanged(this,
                new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, index));
        sync.ReleaseWriterLock();
    }

    public void RemoveAt(int index)
    {
        if (Thread.CurrentThread == dispatcher.Thread)
            DoRemoveAt(index);
        else
            dispatcher.BeginInvoke((Action)(() => DoRemoveAt(index)));
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs("Count"));
    }

    private void DoRemoveAt(int index)
    {
        sync.AcquireWriterLock(Timeout.Infinite);
        if (collection.Count == 0 || collection.Count <= index)
        {
            sync.ReleaseWriterLock();
            return;
        }
        collection.RemoveAt(index);
        if (CollectionChanged != null)
            CollectionChanged(this,
                new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
        sync.ReleaseWriterLock();
    }

    public T this[int index]
    {
        get
        {
            sync.AcquireReaderLock(Timeout.Infinite);
            var result = collection[index];
            sync.ReleaseReaderLock();
            return result;
        }

        set
        {
            sync.AcquireWriterLock(Timeout.Infinite);
            if (collection.Count == 0 || collection.Count <= index)
            {
                sync.ReleaseWriterLock();
                return;
            }
            collection[index] = value;
            sync.ReleaseWriterLock();
        }
    }
}
1 голос
/ 19 ноября 2010

Я подумал, что есть лучший способ загрузить изображение.

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

код в xaml:

<Image Source="{Binding imageUrl, Converter={StaticResource url}}" Height="200" Width="200"></Image>

код для конвертера:


    class ImageDownloader : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            string url =(string)value;
            return getImage(url);</p>

<pre><code>    }

    private object getImage(string imagefile)
    {
       /// IMPLEMENT FUNCTION TO DOWNLOAD IMAGE FROM SERVER HERE
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        return null;
    }
}

и, конечно же, не забудьте настроить ресурс в app.xaml с помощью:

<Application.Resources>
   <ResourceDictionary>
       <namespace:ImageDownloader x:Key="ImageDownloader" />
   </ResourceDictionary>
</Application.Resources>
...