我有从Uri到BitmapImage的转换器的问题。 Uri是网络上的图像网址。我在列表框中的项目上使用此转换器。
我从网页下载图片并从此流BitampImage创建
问题是如果列表框包含大约100 - 250个项目,应用程序冻结,我尝试在另一个线程中调用WebRequestMethod但它不起作用。
以下是代码的根部分:
private static BitmapImage GetImgFromAzet(int sex, Uri imgUri)
{
try
{
if (imgUri == null)
{
if (sex == (int)Sex.Man)
{
return new BitmapImage(new Uri(@"pack://application:,,,/Spirit;Component/images/DefaultAvatars/man.jpg",
UriKind.RelativeOrAbsolute));
}
else
{
return new BitmapImage(new Uri(@"pack://application:,,,/Spirit;Component/images/DefaultAvatars/woman.jpg",
UriKind.RelativeOrAbsolute));
}
}
else
{
BitmapImage image = null;
Task.Factory.StartNew(() =>
{
WebRequest webRequest = WebRequest.CreateDefault(imgUri);
webRequest.ContentType = "image/jpeg";
WebResponse webResponse = webRequest.GetResponse();
image = new BitmapImage();
image.CreateOptions = BitmapCreateOptions.None;
image.CacheOption = BitmapCacheOption.OnLoad;
image.BeginInit();
image.StreamSource = webResponse.GetResponseStream();
image.EndInit();
return image;
//((System.Action)(() =>
//{
// //webResponse.Close();
//})).OnUIThread();
});
return image;
}
}
catch (Exception)
{
//默认 返回 新的BitmapImage(新的Uri(PokecUrl.Avatar,UriKind.RelativeOrAbsolute)); } }
我的目标是从网上下载图片,从他创建BitamImage对象并返回作为图像源控件,但我需要避免应用程序冻结。另外一个问题是如果我关闭webResponse就会破坏所有代码。
编辑:
我试试这个:
BitmapImage image;
WebRequest req = WebRequest.CreateDefault(imgUri);
req.ContentType = "image/jpeg";
using (var res = req.GetResponse())
{
image = new BitmapImage();
image.CreateOptions = BitmapCreateOptions.None;
image.CacheOption = BitmapCacheOption.OnLoad;
image.BeginInit();
image.UriSource = imgUri;
image.StreamSource = res.GetResponseStream();
image.EndInit();
}
但某处必须是bug,代码被破坏。
有什么建议吗?
答案 0 :(得分:3)
绑定转换器始终在UI线程上执行。因此,您可以在Convert方法中启动其他线程,但最终(因为您需要来自此线程的反馈),您必须等到它完成,从而阻止您的应用程序。
例如,为了解决此问题,您可以使用Binding.IsAsync property:
public class ListItemViewData
{
private readonly Uri _uri;
private readonly Sex _sex;
ListItemViewData(Uri uri, Sex sex)
{
this._uri = uri;
this._sex = sex;
}
public BitmapSource Image
{
get
{
// Do synchronous WebRequest
}
}
}
xaml中的用法(列表框项目的DataTemplate内部):
<Image Source="{Binding Path=Image, IsAsync=True}"/>
<强> EDITED 强>
我已经深入研究了BitmapImage类,并发现它具有非常符合Uri参数的ctor,它可以异步工作。
所以你不应该自己执行WebRequest。就像这样:
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var uri = (Uri)value;
return new BitmapImage(uri) { CacheOption = BitmapCacheOption.None };
}
已编辑2
您的视图数据类。
public class ListItemViewData : INotifyPropertyChanged
{
public ListItemViewData(Uri uri)
{
this._uri = uri;
}
private readonly Uri _uri;
public Uri Uri
{
get
{
return this._uri;
}
}
private BitmapSource _source = null;
public BitmapSource Image
{
get
{
return this._source;
}
set
{
this._source = value;
this.OnPropertyChanged("Image");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string p)
{
var pc = this.PropertyChanged;
if (pc!=null)
{
pc(this, new PropertyChangedEventArgs(p));
}
}
}
Helper,执行图像下载:
public static class WebHelper
{
public static Stream DownloadImage(Uri uri, string savePath)
{
var request = WebRequest.Create(uri);
var response = request.GetResponse();
using (var stream = response.GetResponseStream())
{
Byte[] buffer = new Byte[response.ContentLength];
int offset = 0, actuallyRead = 0;
do
{
actuallyRead = stream.Read(buffer, offset, buffer.Length - offset);
offset += actuallyRead;
}
while (actuallyRead > 0);
File.WriteAllBytes(savePath, buffer);
return new MemoryStream(buffer);
}
}
}
当您填写模型时 - 您应该启动单独的线程,它将下载文件并设置图像源。
this._listItems.Add(new ListItemViewData(new Uri(@"http://lifeboat.com/images/blue.ocean.jpg")));
//...
var sc = SynchronizationContext.Current;
new Thread(() =>
{
foreach (var item in this._listItems)
{
var path = "c:\\folder\\"+item.Uri.Segments.Last();
var stream = WebHelper.DownloadImage(item.Uri, path);
sc.Send(p =>
{
BitmapImage bi = new BitmapImage();
bi.BeginInit();
bi.StreamSource = (Stream)p;
bi.EndInit();
item.Image = bi;
}, stream);
}
}).Start();