2016-12-03 4 views
0

J'essaie d'utiliser MVVM pour un projet Windows Universal, mais les interfaces pour le fichier de stockage se plaint beaucoup d'utiliser async. Le code suivant fonctionne parfois:ValueConverter for StorageFile => Image

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; 
    } 
} 

Jusqu'à ce que je sélectionne une nouvelle image à charger, je reçois l'erreur « { » L'application appelée une interface qui est attelée pour un thread différent. (Exception de HRESULT: 0x8001010E (RPC_E_WRONG_THREAD)) "}"

J'ai essayé de changer pour utiliser le CoreDispatcher par un autre 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 sur BitmapImage. Cela a un sens absolu pour moi bien sûr - le répartiteur asynchrone cède le contrôle au processus parent, l'image n'a pas été assignée, l'exception de référence nulle. Mais je ne sais pas ce que la bonne façon est!

+1

Un convertisseur de valeur ne doit pas du tout renvoyer un UIElement. Pourquoi pensez-vous que vous avez besoin de quelque chose comme ça? – Clemens

+0

Je voulais obtenir des fichiers d'image potentiellement volumineux et les afficher dans une application, en fournissant des informations sur les pixels et la photographie à leur sujet, donc je ne voulais pas geler l'interface utilisateur en attente de chargement du fichier. –

+0

Toutefois, le convertisseur doit renvoyer une ImageSource (par exemple une image Bitmap), pas un contrôle Image. – Clemens

Répondre

0

Merci Clemens pour vos commentaires sur OP, ce qui m'a fait réaliser que j'étais obsédée par les modèles et concentrée sur l'utilisation de quelque chose que j'aimais au lieu de faire le bon design.

La source qui a été l'origine du problème est la suivante:

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(); 
    } 

Je continue donc en train d'exécuter les opérations que je voulais sur l'image après son été sélectionné. Il y a encore beaucoup à corriger ici, d'autant plus que je bloque l'interface utilisateur en effectuant du travail sans déléguer ces opérations en souscrivant ces autres composants à la propriété, mais c'est un début, et plus d'exceptions!

Notez que non inclus ici, le convertisseur de valeur a été retiré de la couche d'application principale.