Fab*_*abi 1 c# wpf xaml multithreading
我是 WPF 新手,但我已经使用 C# 有一段时间了,目前正在开发一个简单的窗口(Windows 桌面),该窗口应该可视化目录中的所有照片。应用程序还应该了解 EXIF 数据,例如 ISO、光圈等,我为此使用了 DLL。
我定义了一个Photo类:
public class Photo {
public string FileName { get; set; }
public int ISO { get; set; }
...
}
Run Code Online (Sandbox Code Playgroud)
我想List<Photo>在运行时存储在 a 中。
然后我声明了一个PhotoItem(XAML 用户控件),其中包含一个图像控件和一个 TextBlock。对于每一个Photo创建的对象,都会有一个PhotoItem创建对象将相应的Photo属性保存为:
public partial class PhotoItem : UserControl {
...
public Photo Photo { get; set; }
...
}
Run Code Online (Sandbox Code Playgroud)
从这个Photo属性PhotoItem中,知道在哪里寻找图像以及要显示什么 ISO 等。
现在我的问题。因为如果用户选择目录,加载图像本身以及元数据会花费太长时间,所以我想首先将所有 s 添加PhotoItem到窗口(仍然为空),然后运行元数据查找和图像缩略图加载对于他们每个人来说。当然,如果这些操作不阻塞 UI 线程那就最好了,因此我目前使用一个Task用于收集元数据,一个用于收集缩略图。
如果图像的元数据现在可用,我将如何更新 PhotoItems?基本上,如何才能拥有一个集中位置来存储所有数据、任务可以向该位置提供更新以及 UI 线程可以从中构建信息。我对 XAML/WPF 中的绑定了解一些,但是Photo.ISO如果尚未收集元数据,则将 TextBlock 的文本绑定到变量将始终显示零。在这种情况下,我想隐藏PhotoItem.
另一方面,我也考虑过在 中实现类似“刷新”功能的功能PhotoItem,但这会重新加载图像并且需要很长时间(这可能是我最喜欢的 WinForms 方法,哈哈)。
谁能告诉我如何实现这一点?
提前致谢!
让我们看一个没有 UserControl 的基本示例。
第一步是创建视图模型以启用数据绑定。您可以让 Photo 类实现接口,INotifyPropertyChanged以便在属性值更改时更新绑定。
下面的类还声明了Image一个保存派生对象的属性ImageSource,该对象是异步加载的。
public class Photo : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this,
new PropertyChangedEventArgs(propertyName));
}
public string FileName { get; set; }
private string iso = string.Empty;
public string ISO
{
get { return iso; }
set
{
iso = value;
NotifyPropertyChanged(nameof(ISO));
}
}
private ImageSource image;
public ImageSource Image
{
get { return image; }
set
{
image = value;
NotifyPropertyChanged(nameof(Image));
}
}
public async Task Load()
{
Image = await Task.Run(() =>
{
using (var fileStream = new FileStream(
FileName, FileMode.Open, FileAccess.Read))
{
return BitmapFrame.Create(
fileStream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
}
});
ISO = "1600";
}
}
Run Code Online (Sandbox Code Playgroud)
视图模型的第二部分是一个包含Photo实例集合的类:
public class ViewModel
{
public ObservableCollection<Photo> Photos { get; }
= new ObservableCollection<Photo>();
}
Run Code Online (Sandbox Code Playgroud)
DataContext对于典型的数据绑定场景,您可以在代码或 XAML 中将此类的实例分配给您的窗口:
<Window.DataContext>
<local:ViewModel/>
</Window.DataContext>
Run Code Online (Sandbox Code Playgroud)
DataTemplate最后一部分是带有可视化 a的 ListBox 的声明Photo:
<ListBox ItemsSource="{Binding Photos}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image Source="{Binding Image}" Width="100" Height="100"/>
<StackPanel>
<TextBlock Text="{Binding ISO, StringFormat=ISO: {0}}"/>
</StackPanel>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Run Code Online (Sandbox Code Playgroud)
Photos现在,您可以在主窗口的异步事件处理程序中填充该集合Loaded,如下所示:
private async void Window_Loaded(object sender, RoutedEventArgs e)
{
var vm = (ViewModel)DataContext;
foreach (var file in Directory.EnumerateFiles(...))
{
vm.Photos.Add(new Photo { FileName = file });
}
foreach (var photo in vm.Photos)
{
await photo.Load();
}
}
Run Code Online (Sandbox Code Playgroud)