StorageFile 的 ValueConverter => 图片
ValueConverter for StorageFile => Image
我正在尝试将 MVVM 用于通用 Windows 项目,但存储文件的接口对使用异步有很多抱怨。以下代码有时有效:
public object Convert(object value, Type targetType, object parameter, string language)
{
var storageFile = value as StorageFile;
return GetImageAsync(storageFile).Result;
}
private static async Task<ImageSource> GetImageAsync(StorageFile storageFile)
{
var bitmapImage = new BitmapImage();
var stream = await storageFile.OpenAsync(FileAccessMode.Read).AsTask().ConfigureAwait(false);
bitmapImage.SetSource(stream);
return bitmapImage;
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
return null;
}
}
直到我 select 加载新图像,然后我收到错误“{"The application called an interface that was marshalled for a different thread. (Exception from HRESULT: 0x8001010E (RPC_E_WRONG_THREAD))"}”
所以我尝试将其更改为每个线程使用 CoreDispatcher:
public class FileToImageConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
var storageFile = value as StorageFile;
Task<ImageSource> image = null;
Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
image = GetImageAsync(storageFile);
image.RunSynchronously();
});
return image.Result;
}
private static async Task<ImageSource> GetImageAsync(StorageFile storageFile)
{
var bitmapImage = new BitmapImage();
var stream = await storageFile.OpenAsync(FileAccessMode.Read).AsTask().ConfigureAwait(false);
bitmapImage.SetSource(stream);
return bitmapImage;
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
return null;
}
}
位图图像出现 NullReferenceException。这对我来说当然是绝对有意义的——异步调度程序将控制权让给父进程,图像尚未分配,空引用异常。但是我不知道怎样才是正确的做法!
感谢 Clemens 对 OP 的评论,这让我意识到我对模式很着迷,专注于使用我喜欢的东西而不是做正确的设计。
导致该问题的来源是:
private async void GetFile()
{
var filePicker = new FileOpenPicker();
filePicker.FileTypeFilter.Add(".jpg");
filePicker.FileTypeFilter.Add(".png");
filePicker.FileTypeFilter.Add(".gif");
filePicker.FileTypeFilter.Add(".bmp");
filePicker.ViewMode = PickerViewMode.Thumbnail;
filePicker.SuggestedStartLocation = PickerLocationId.Desktop;
filePicker.CommitButtonText = "Open";
CurrentFile = await filePicker.PickSingleFileAsync(); //Bad code used CurrentFile set and NotifyPropertyChanged to start up the value converter code from OP
//New, obvious better code
CurrentImage = await GetImageSource(CurrentFile);
var statistics = new ImageStatistics();
Logger.Log("Metadata start");
var data = statistics.GetMetaData(CurrentFile);
Logger.Log("Color Counts start");
var colorCounts = statistics.GetColorCounts(CurrentFile);
var filterer = new ColorFilterer();
Logger.Log("Color Counts await start");
var filteredColors = filterer.GetTopColors(await colorCounts, 10);
Logger.Log("Color Counts await end");
Logger.Log("Metadata await start");
var metaData = await data;
Logger.Log("Metadata await end");
Make = metaData[SystemProperty.CameraManufacturer];
Model = metaData[SystemProperty.CameraModel];
ExposureTime = string.Format("1/{0}",1/Convert.ToDouble(metaData[SystemProperty.ExposureTime]));
ISOSpeed = metaData[SystemProperty.ISOSpeed];
FStop = string.Format("f/{0}", metaData[SystemProperty.FStop]);
ExposureBias = metaData[SystemProperty.ExposureBias];
TopColors = filteredColors.Select(pair => new ColorStatistics { Color = pair.Key, Count = pair.Value }).ToList();
}
所以我只是在选择图像后继续执行我想要的操作。这里还有很多需要解决的问题,特别是因为我在执行工作时阻塞了 UI,并且没有通过将这些其他 UI 组件订阅到 属性 来委托这些操作,但这是一个开始,没有更多例外!
注意这里没有包含,值转换器已经从主应用层移除。
我正在尝试将 MVVM 用于通用 Windows 项目,但存储文件的接口对使用异步有很多抱怨。以下代码有时有效:
public object Convert(object value, Type targetType, object parameter, string language)
{
var storageFile = value as StorageFile;
return GetImageAsync(storageFile).Result;
}
private static async Task<ImageSource> GetImageAsync(StorageFile storageFile)
{
var bitmapImage = new BitmapImage();
var stream = await storageFile.OpenAsync(FileAccessMode.Read).AsTask().ConfigureAwait(false);
bitmapImage.SetSource(stream);
return bitmapImage;
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
return null;
}
}
直到我 select 加载新图像,然后我收到错误“{"The application called an interface that was marshalled for a different thread. (Exception from HRESULT: 0x8001010E (RPC_E_WRONG_THREAD))"}”
所以我尝试将其更改为每个线程使用 CoreDispatcher:
public class FileToImageConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
var storageFile = value as StorageFile;
Task<ImageSource> image = null;
Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
image = GetImageAsync(storageFile);
image.RunSynchronously();
});
return image.Result;
}
private static async Task<ImageSource> GetImageAsync(StorageFile storageFile)
{
var bitmapImage = new BitmapImage();
var stream = await storageFile.OpenAsync(FileAccessMode.Read).AsTask().ConfigureAwait(false);
bitmapImage.SetSource(stream);
return bitmapImage;
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
return null;
}
}
位图图像出现 NullReferenceException。这对我来说当然是绝对有意义的——异步调度程序将控制权让给父进程,图像尚未分配,空引用异常。但是我不知道怎样才是正确的做法!
感谢 Clemens 对 OP 的评论,这让我意识到我对模式很着迷,专注于使用我喜欢的东西而不是做正确的设计。
导致该问题的来源是:
private async void GetFile()
{
var filePicker = new FileOpenPicker();
filePicker.FileTypeFilter.Add(".jpg");
filePicker.FileTypeFilter.Add(".png");
filePicker.FileTypeFilter.Add(".gif");
filePicker.FileTypeFilter.Add(".bmp");
filePicker.ViewMode = PickerViewMode.Thumbnail;
filePicker.SuggestedStartLocation = PickerLocationId.Desktop;
filePicker.CommitButtonText = "Open";
CurrentFile = await filePicker.PickSingleFileAsync(); //Bad code used CurrentFile set and NotifyPropertyChanged to start up the value converter code from OP
//New, obvious better code
CurrentImage = await GetImageSource(CurrentFile);
var statistics = new ImageStatistics();
Logger.Log("Metadata start");
var data = statistics.GetMetaData(CurrentFile);
Logger.Log("Color Counts start");
var colorCounts = statistics.GetColorCounts(CurrentFile);
var filterer = new ColorFilterer();
Logger.Log("Color Counts await start");
var filteredColors = filterer.GetTopColors(await colorCounts, 10);
Logger.Log("Color Counts await end");
Logger.Log("Metadata await start");
var metaData = await data;
Logger.Log("Metadata await end");
Make = metaData[SystemProperty.CameraManufacturer];
Model = metaData[SystemProperty.CameraModel];
ExposureTime = string.Format("1/{0}",1/Convert.ToDouble(metaData[SystemProperty.ExposureTime]));
ISOSpeed = metaData[SystemProperty.ISOSpeed];
FStop = string.Format("f/{0}", metaData[SystemProperty.FStop]);
ExposureBias = metaData[SystemProperty.ExposureBias];
TopColors = filteredColors.Select(pair => new ColorStatistics { Color = pair.Key, Count = pair.Value }).ToList();
}
所以我只是在选择图像后继续执行我想要的操作。这里还有很多需要解决的问题,特别是因为我在执行工作时阻塞了 UI,并且没有通过将这些其他 UI 组件订阅到 属性 来委托这些操作,但这是一个开始,没有更多例外!
注意这里没有包含,值转换器已经从主应用层移除。