【问题标题】:ValueConverter for StorageFile => ImageStorageFile => Image 的 ValueConverter
【发布时间】:2016-12-03 20:59:25
【问题描述】:

我正在尝试将 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;
    }
}

在我选择要加载的新图像之前,我收到错误“{”应用程序调用了一个为不同线程编组的接口。 (来自 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。当然,这对我来说绝对有意义 - 异步调度程序将控制权交给父进程,尚未分配图像,空引用异常。但是我不知道正确的方法是什么!

【问题讨论】:

  • 值转换器根本不应该返回 UIElement。你为什么认为你需要这样的东西?
  • 我想获取可能较大的图像文件并将它们显示在应用程序中,提供有关它们的像素/摄影信息,因此我不想冻结 UI 等待文件加载。跨度>
  • 不过,转换器应该返回一个 ImageSource(例如 BitmapImage),而不是一个 Image 控件。
  • 不确定这种方法是否可行。据我所知,UWP 中的绑定始终是同步的,因此绑定转换器始终被同步调用,即某些异步方法没有等待。但是有内置的异步下载(参见 BitmapImage 类中的 DownloadProgress 和 ImageOpened 事件),但这可能仅限于远程文件(即 http URI)。
  • 对于绑定转换器,是的。我正在考虑将 StorageFile 转换为 ImageSource 的 IValueConverter 的外观,但没有成功。 StorageFile 仅提供异步 API,不能在 IValueConverter 中使用。对于表示图像文件的视图模型属性类型,StorageFile 可能不是一个好的选择。

标签: c# async-await uwp win-universal-app storagefile


【解决方案1】:

感谢 Clemens 对 OP 的 cmets,这让我意识到我痴迷于模式并专注于使用我喜欢的东西而不是做正确的设计。

导致问题的来源是:

    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 组件订阅到属性来委派这些操作,但这是一个开始,不再有例外!

注意这里没有包含,值转换器已经从主应用层中移除了。

【讨论】:

    猜你喜欢
    • 2010-11-02
    • 1970-01-01
    • 2013-05-06
    • 1970-01-01
    • 2010-10-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多