我是WPF的新手,但我已经做了很长时间的C#并且我正在开发一个简单的窗口(Windows桌面),它应该可视化目录中的所有照片。应用程序还应该知道像ISO,光圈等EXIF数据,我使用DLL。
我定义了一个Photo
类:
public class Photo {
public string FileName { get; set; }
public int ISO { get; set; }
...
}
我希望在运行时存储在List<Photo>
中。
然后我声明了PhotoItem
(XAML用户控件),其上有一个Image控件和一个TextBlock。对于创建的每个Photo
,都会创建一个PhotoItem
,其中包含相应的Photo
作为属性:
public partial class PhotoItem : UserControl {
...
public Photo Photo { get; set; }
...
}
从Photo
属性中,PhotoItem
知道在哪里查找图像以及要显示的ISO等。
现在我的问题。因为如果用户选择目录,加载Image本身以及元数据将花费太长时间,我想首先将所有PhotoItem
添加到窗口(仍为空),然后运行元数据查找和每个图像缩略图加载。当然最好是这些操作不会阻止UI线程,因此我目前使用一个Task
来收集元数据,一个用于收集缩略图。
如果图像的元数据现在可用,我将如何更新PhotoItems?基本上,您如何拥有一个存储所有数据的集中位置,任务可以提供更新,UI线程可以从中构建信息。我对XAML / WPF中的绑定有所了解,但是绑定例如如果尚未收集元数据,则Photo.ISO
变量的TextBlock文本将始终显示为零。在这种情况下,我想隐藏PhotoItem
上的所有文字细节。
另一方面,我也考虑在PhotoItem
内部实现类似'Refresh'功能的东西,但这会重新加载图像并需要很长时间(这可能是我最喜欢的WinForms方式做吧,哈哈)。
有人能告诉我如何实现这个目标吗?
提前致谢!
答案 0 :(得分:4)
让我们看看没有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";
}
}
视图模型的第二部分是一个包含Photo
实例集合的类:
public class ViewModel
{
public ObservableCollection<Photo> Photos { get; }
= new ObservableCollection<Photo>();
}
对于典型的数据绑定方案,您可以在代码或XAML中将此类的实例分配给窗口的DataContext
:
<Window.DataContext>
<local:ViewModel/>
</Window.DataContext>
最后一部分是ListBox的声明,其中DataTemplate
可视化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>
现在,您可以在MainWindow的异步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();
}
}