【问题标题】:BitmapImage created on separate thread and frozen does not show在单独的线程上创建并冻结的 BitmapImage 不显示
【发布时间】:2019-09-17 17:27:15
【问题描述】:

我有一个 ImageSource 绑定到视图模型 BitmapImage 属性。它是为树视图中的每个树视图项显示的图标。我想在单独的线程中加载图像,以减少树初始化期间 UI 线程的阻塞。

我已经阅读了类似问题的各种答案,但我还没有找到解决这个特定问题的方法。我知道要使BitmapImage 跨线程可用,您必须Freeze 图像,但正是这种冻结停止了它的渲染。

我加载图片如下:

private void LoadImage()
{
    Task.Run(() =>
    {
        if (string.IsNullOrWhiteSpace(_imagePath))
            return ;
        string[] resources = GetResourceNames();
        var result = resources?.FirstOrDefault(x => String.Equals(x, _image, StringComparison.CurrentCultureIgnoreCase)) ??
                     resources?.FirstOrDefault(x => String.Equals(Path.GetFileName(x), _imagePath, StringComparison.CurrentCultureIgnoreCase));
        var image = result == null ? null : new BitmapImage(GetUri(ImageResourceCache.ImagesAssembly.FullName, result));
        if (image == null) return;

        image.CacheOption = BitmapCacheOption.OnLoad; //<== a suggested solution that does not make a difference
        image.Freeze();//<== freezing stops the cross thread exception but stops rendering

        DispatcherHelp.CheckInvokeOnUI(() => Image = image);
    });
}

private static string[] GetResourceNames()
{
    var asm = ImageResourceCache.ImagesAssembly;
    var resName = asm.GetName().Name + ".g.resources";
    using (var stream = asm.GetManifestResourceStream(resName))
    {
        if (stream == null) return null;
        using (var reader = new ResourceReader(stream))
            return reader.Cast<DictionaryEntry>().Select(entry => (string)entry.Key).ToArray();
    }
}

private static Uri GetUri(string dllName, string relativeFilePath)
{
    return new Uri($"/{dllName};component/{relativeFilePath}", UriKind.RelativeOrAbsolute);
}

LoadImage 在视图模型构造函数中被调用。 _imagePath 被传递给构造函数。

如果我删除 Task.Run 并冻结它呈现。将冻结恢复,它不再呈现。

绑定如下:

<Image Source="{Binding Image}" Stretch="Uniform" Margin="0 0 3 0" />

视图模型:

public BitmapImage Image
{
    get => _image;
    set
    {
        _image = value;
        RaisePropertyChanged();
    }
}

【问题讨论】:

  • LoadImage 在构造函数中被调用
  • 你尝试在主线程中创建它吗?也许图像无法显示此流
  • 不,我在单独的线程(Task.Run)中创建它,因此需要冻结图像
  • @TimRutter 你能尝试设置图像控件的源吗?不要使用绑定。
  • 图像属性是见上文 - 提升属性已更改。 DispatcherHelp.CheckInvokeOnUI - 检查当前线程上下文是否为 ui 线程,如果不是,则在主线程上调用它。这是必需的,否则会引发异常,因为另一个线程拥有它而无法访问对象

标签: c# wpf bitmapimage


【解决方案1】:

您应该使用pack URI 来识别图像资源。如果有 image/pic.jpeg 资源,这应该可以工作:

Task.Run(() =>
{
    BitmapImage image = new BitmapImage(new Uri("pack://application:,,,/images/pic.jpeg", UriKind.RelativeOrAbsolute));
    image.Freeze();
    Dispatcher.BeginInvoke(new Action(() => Image = image));
});

您的GetUri 方法似乎缺少pack://application:,,,/ 部分。当您可以使用包 URI 时,没有理由像您当前那样查找资源。

【讨论】:

  • @lindexi:上面的代码对我来说很好用。包 URI 可在任何线程上工作。
  • 所以我说maybe
  • @lindexi:因为Uri 中缺少pack://application:,,,/
  • 就是这样,谢谢。我已将 pack://application:,,,/ 添加到 GetUri 并且它可以工作。你是对的,我不需要循环资源。这实际上用于加载 DevExpress 图像,我认为这是他们发送给我的代码。
猜你喜欢
  • 1970-01-01
  • 2010-10-05
  • 2020-10-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-08-10
相关资源
最近更新 更多