我有一个静态类,其中包含文件扩展名和BitMapSource对象的字典。该类包含一个函数,给定一个FileInfo对象将返回关联的BitMapSource对象,如果它不在字典中,它将获取它并在返回之前将其放入字典中。
从GUI线程执行时,这很好用。但是,当我尝试将它放在后台线程中时,我没有得到任何回报。我有什么理由不能从后台线程执行此操作吗?
静态类
namespace Test.Classes
{
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Windows;
using System.Windows.Interop;
using System.Windows.Media.Imaging;
public static class IconMap
{
private static Dictionary<string, BitmapSource> iconDictionary = new Dictionary<string, BitmapSource>();
public static BitmapSource GetFileIcon(FileInfo fileInfo)
{
if (iconDictionary.ContainsKey(fileInfo.Extension))
{
return iconDictionary[fileInfo.Extension];
}
else
{
lock (iconDictionary)
{
Icon icon = Icon.ExtractAssociatedIcon(fileInfo.FullName);
BitmapSource bitMapSource = Imaging.CreateBitmapSourceFromHIcon(icon.Handle, new Int32Rect(0, 0, icon.Width, icon.Height), BitmapSizeOptions.FromEmptyOptions());
iconDictionary.Add(fileInfo.Extension, bitMapSource);
return bitMapSource;
}
}
}
}
}
Control.cs
namespace Test.Controls
{
using System;
using System.Diagnostics;
using System.IO;
using System.Threading.Tasks;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media.Imaging;
using Microsoft.Office.Interop.Outlook;
public partial class AttachedFileInfo : UserControl
{
private FileInfo file;
public AttachedFileInfo(FileInfo fileInfo)
{
this.InitializeComponent();
this.file = fileInfo;
this.FileLink.NavigateUri = new Uri(fileInfo.FullName);
this.FileName.Text = fileInfo.Name;
this.LoadFileIcon(fileInfo);
}
private async void LoadFileIcon(FileInfo fileInfo)
{
Task<BitmapSource> getFileIconTask = Task<BitmapSource>.Factory.StartNew(() =>
{
// If I change this to BitmapSource icon = null; it works as expected.
BitmapSource icon = Classes.IconMap.GetFileIcon(fileInfo);
return icon;
});
await getFileIconTask;
this.FileIcon.Source = Classes.IconMap.GetFileIcon(fileInfo);
// getFileIconTask.Result;
}
private void FileLink_RequestNavigate(object sender, System.Windows.Navigation.RequestNavigateEventArgs e)
{
Process.Start(this.file.FullName);
}
}
}
答案 0 :(得分:2)
您的图标不会显示,因为只有通过调用.Freeze()方法冻结WPF资源才能使用其他线程(请参阅下面的代码)。
另一个问题是即使你使用ConcurrentDictionary,你的GetFileIcon()方法也不是线程安全的,如果一个线程向iconDictionary添加了图标,当它离开锁定的代码块时,另一个线程可能会进入锁定块将相同文件类型的图标添加到字典中,从而导致ArgumentException。因此,在锁定内,您必须再次测试字典中是否已存在特定扩展名的图标。
private static ConcurrentDictionary<string, BitmapSource> iconDictionary = new ConcurrentDictionary<string, BitmapSource>();
public static BitmapSource GetFileIcon(FileInfo fileInfo)
{
BitmapSource bitMapSource;
if (iconDictionary.TryGetValue(fileInfo.Extension, out bitMapSource))
{
return bitMapSource;
}
else
{
lock (iconDictionary)
{
if (iconDictionary.TryGetValue(fileInfo.Extension, out bitMapSource))
return bitMapSource;
Icon icon = Icon.ExtractAssociatedIcon(fileInfo.FullName);
bitMapSource = Imaging.CreateBitmapSourceFromHIcon(icon.Handle, new Int32Rect(0, 0, icon.Width, icon.Height), BitmapSizeOptions.FromEmptyOptions());
bitMapSource.Freeze();//Allows BitmapSource to be used on another thread
iconDictionary[fileInfo.Extension] = bitMapSource;
return bitMapSource;
}
}
}