我正在尝试从我的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);
}
}
一旦下载图像并尝试将图像添加到(已打开的)弹出窗口,我就会得到异常说明该线程 不是对象的所有者
有人可以指出我正确的方向吗?
答案 0 :(得分:14)
如果公共网络服务器上的图像可以使用普通的HTTP URI进行处理,那么您可以直接将源设置为:
<Image Source="http://www.someserver.com/myimage.png" />
WPF将负责下载它 - 它甚至会异步地进行下载我认为虽然我不是100%肯定。
您当然也可以使用数据绑定来完成此操作:
<Image Source="{Binding TheImage}" />
在viewmodel中
public string TheImage
{
get { return "http://www.someserver.com/myimage.png"; }
}
答案 1 :(得分:2)
您可以通过集合,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();
}
}
}
答案 2 :(得分:1)
我认为有更好的方法来加载图像。
不是绑定到后面代码中的图像,而是绑定到包含图像位置的字符串。之后,我在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中设置资源:
}
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;
}
}