创作原因
闲来无事突然想把图片拖到wpf程序让他下载,然后出现了一堆问题,记录一下,免得下次折腾
问题过程
这是一张图片(小特,可爱!)
这是一个普通的wpf程序
右侧的图片栏就是万恶之源
在xaml中我给他添加了一个接受拖放的功能
<i:Interaction.Triggers>
<i:EventTrigger EventName="Drop">
<i:InvokeCommandAction Command="{Binding OnDropImageCommand}" PassEventArgsToCommand="True" />
</i:EventTrigger>
</i:Interaction.Triggers>
private void OnDropImage(DragEventArgs e)
{
ImageBar imageBar = new();
Uri? uri = null;
if (e.Data.GetDataPresent("System.String"))
{
if(e.Data.GetData("System.String") is string obj && obj != null)
{
try
{
uri = new(obj);
Bitmap bitmap= BitmapService.Get(uri);
}
catch(Exception ex)
{
RaiseConfirm(ex.Message);
}
}
}
if(uri!=null)
{
Images.Add(imageBar);
JsonReader.Save(ImageTag, Images);
}
}
BitmapService.Get
是用于获取图像的
一切都是水到渠成,那么合理,来,运行一下
轻轻的把图片拖上去
卡了……都卡了,连edge都卡了……
(发现连带着edge都卡了还是在测试了好几遍内部程序没问题才发现的)
问题分析
这是BitmapService.Get
的实现
public static Bitmap Get(Uri uri)
{
string name = Path.GetFileName(uri.ToString());
string extension = Path.GetExtension(uri.ToString());
string path = Path.Combine(PathConstants.Instance.Images, name);
if (File.Exists(path))
{
return Get(path);
}
BitmapImage bitmapImage = GetFromUri(uri);
//需要释放流,否则无法读取图像
using (var fileStream = new FileStream(path, FileMode.OpenOrCreate))
{
BitmapEncoder encoder = ImageEncoder.SetExtension(extension);
encoder.Frames.Add(BitmapFrame.Create(bitmapImage));
encoder.Save(fileStream);
}
return Get(name);
}
读取本地文件没有一点压力,重点就是从网络中读取文件
罪魁祸首
private static async Task<BitmapImage> GetFromHttp(Uri uri)
{
BitmapImage bitmapImage = new();
byte[] origin =await HttpClient.GetByteArrayAsync(uri);
using (MemoryStream fs=new(origin))
{
bitmapImage.BeginInit();
bitmapImage.StreamSource = fs;
bitmapImage.EndInit();
}
return bitmapImage;
}
根据我自己的分析,大概是这样一个过程
edge给程序发送拖动的文件,在完成对拖动操作的执行结束之前,wpf又向edge发送 HttpClient
请求,然后就像陷入死锁了一样两个一起不动了
解决问题
第一想法
你卡我,我卡你,那我拿新线程跑不就行了
然后就这样改了
OnDropImage
try
{
uri = new(obj);
Thread thread = new(() =>
{
Bitmap bitmap = BitmapService.Get(uri);
});
thread.Start();
thread.Join();
}
然后又爆出这样的问题,更稀罕了
然后又是一通检查,从uri
开查,查过一通之后才开始怀疑BitmapImage
最后才发现一个特性,BitmapImage
的BitmapImage.EndInit()
操作必须在UI线程上实现,不是随便什么线程都能更新的
然后在一通修改后就变这样了……
private void OnDropImage(DragEventArgs e)
{
ImageBar imageBar = new();
Uri? uri = null;
if (e.Data.GetDataPresent("System.String"))
{
if(e.Data.GetData("System.String") is string obj && obj != null)
{
try
{
uri = new(obj);
imageBar.Title=System.IO.Path.GetFileName(obj);
imageBar.ImageSource= BitmapService.Get(uri);
}
catch(Exception ex)
{
RaiseConfirm(ex.Message);
}
}
}
if(uri!=null)
{
Images.Add(imageBar);
JsonReader.Save(ImageTag, Images);
}
}
//从网络获取图像
private static async Task<byte[]> GetFromHttp(Uri uri)
{
byte[] origin =await HttpClient.GetByteArrayAsync(uri);
return origin;
}
public static BitmapImage GetFromUri(Uri uri)
{
if (uri.ToString().StartsWith("http"))
{
MemoryStream memoryStream = new();
Thread thread = new(() =>
{
memoryStream = new(GetFromHttp(uri).Result);
});
thread.Start();
thread.Join();
BitmapImage bitmapImage = new();
bitmapImage.BeginInit();
bitmapImage.StreamSource = memoryStream;
bitmapImage.EndInit();
return bitmapImage;
}
else if (uri.ToString().StartsWith("file"))
{
return GetFromLocal(uri);
}
else
{
throw new InvalidDataException($"{uri}\n无法识别的数据");
}
}
怎么真解决了感觉这问题这么傻呢……
算了,解决了就是好事,现在的问题是下载的时候UI也在等(好歹不是忙等了),之后再想办法怎么解决吧