在 wpf/surface 中从 Web 加载图像

发布于 2024-10-03 11:32:51 字数 707 浏览 1 评论 0原文

我正在尝试在我的 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);
            }
    }

一旦下载了图像并尝试将图像添加到(已打开的)弹出窗口中,我就会收到异常消息: 不是该对象的所有者

有人可以指出我正确的方向吗?

I'm trying to load images from the web in my wpf application.

The idea is the following:
When I click on a button, a popup with additional information is raised. In this popup I'm using some images from the web.

The problem:
When the popup is being loaded the systems hangs while waiting for the images. I'm binding
the images from my code behind. The images are stored in an ObservableCollection. I tried
using a thread for loading the images but everytime I run into an exception saying the thread is not the owner of the object.

I tried using an Invoke to get the downloaded images to the UserinterfaceThread but I can't reach it. My code is the following:

        IList<Image> imagesFromWeb = downloadImagesFromWeb(url);


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

As soon as the images are downloaded and it tries to add the images to the (already opened) popup I get the exception saying the thread
is not the owner of the object

Can someone please point me into the right direction?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

迷爱 2024-10-10 11:32:51

如果您在公共 Web 服务器上有可用的图像,可以使用普通的 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"; } 
}    

If you have the image available on a public web server which can be adressed using a normal HTTP URI then you can set the source directly to that:

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

WPF will take care of downloading it - it'll even do it asynchronously I think although I'm not 100% sure.

You can of course do this with databinding as well:

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

And in the viewmodel

public string TheImage 
{ 
    get { return "http://www.someserver.com/myimage.png"; } 
}    
小巷里的女流氓 2024-10-10 11:32:51

您可能会遇到有关集合、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();
        }
    }
}

You can get a variety of issues with collections, WPF, binding and threading

The best thing (in my opinion) is to use a dispatcher-safe observable collection

here is an implementation, with also includes thread-safety:

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();
        }
    }
}
丶视觉 2024-10-10 11:32:51

我认为有更好的方法来加载图像。

与其在后面的代码中绑定到图像,不如绑定到包含图像位置的字符串。之后,我在 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);

    }

    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>

I figured there's a better way to load the image.

Instead of binding to an image in the code behind it's better to bind to a string containing the location of the image. After that I use a converter in the xaml code which converts the string to an image. (the image downloader is now inside the converter class)

the code in xaml:

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

The code for the converter:


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

    }

    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;
    }
}

and ofcourse don't forget to set-up the resource in the app.xaml with:

<Application.Resources>
   <ResourceDictionary>
       <namespace:ImageDownloader x:Key="ImageDownloader" />
   </ResourceDictionary>
</Application.Resources>
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文