I'm trying to use MVVM for a Universal Windows project but the interfaces for Storage File complains a lot about using async. The following code sometimes works:
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;
}
}
Until I select a new image to load, then I get the error "{"The application called an interface that was marshalled for a different thread. (Exception from HRESULT: 0x8001010E (RPC_E_WRONG_THREAD))"}"
So I tried changing it to use the CoreDispatcher per another thread:
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 on bitmapimage. This makes absolute sense to me of course - the async dispatcher cedes control to the parent process, image has not been assigned, null reference exception. But I don't know what the right way is!
Thank you Clemens for your comments on OP, which made me realize that I was pattern-obsessing and focused on using something I liked instead of doing the right design.
The source that was causing the issue is this:
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();
}
So I just continue performing the operations that I wanted on the image after its been selected. There's still a lot to fix here, especially since I'm blocking UI while performing work and not delegating these operations by subscribing these other UI components to the property, but it's a start, and no more exceptions!
Note that not included here, the value converter has been removed from the main application layer.
User contributions licensed under CC BY-SA 3.0