【问题标题】:Read BitmapImage from Zip archive从 Zip 存档中读取 BitmapImage
【发布时间】:2018-02-09 08:20:34
【问题描述】:

对于 WPF 项目,我需要从 Zip 文件中读取 BitmapImage。原始文件格式是 .png 我知道如何直接从文件系统执行此操作,并且工作正常。不幸的是,从 Zip 文件中,图像未显示,但似乎已读取图像。

我创建了一个简单的测试项目:

public partial class MainWindow : Window
    {

    public BitmapImage RouteImage { get; set; }

    public MainWindow()
        {
        InitializeComponent();
        LoadBitmap();
        DataContext = RouteImage;
        }

    public void LoadBitmap()
        {
        RouteImage = new BitmapImage();
        var PackedFile = @"D:\Temp\MainContent.ap";
        try
            {
                {
                using (ZipArchive archive = ZipFile.OpenRead(PackedFile))
                    {
                    var file = archive.GetEntry("RouteInformation/image.png");
                    if (file != null)
                        {
                        using (var zipEntryStream = file.Open())
                            {
                            RouteImage.BeginInit();
                            RouteImage.CacheOption = BitmapCacheOption.OnLoad;
                            RouteImage.StreamSource = zipEntryStream;
                            RouteImage.EndInit();
                            return;
                            }
                        }
                    }
                }
            }
        catch (Exception e)
            {
            var s = "Exception: " + e.Message;
            }
        }
    }
}

XAML 代码如下所示:

<Image Height="128" Width="256"  Source="{Binding BitmapImage}"/>

在调试器中,看起来流已创建并绑定到 BitmapImage,但宽度和高度设置为 1。所以我认为读取 zip 文件中的数据有问题。

【问题讨论】:

  • 不应该是 Source="{Binding}" 因为 DataContext 是 BitmapImage?
  • 首先。您的 Window DataContext 不是您的 BitmapImage。例如,创建一个新类作为您的窗口 DataContext=new yourclass(必须实现 inotifypropertychanged 接口的 ViewModel)并创建一个属性。 (此属性名称应与 xaml 部分中的绑定源名称相同。)设置 BitmapImage 后调用 OnPropertyChanged。
  • 感谢您的快速回复,S.Toniolo 我测试过,但没有任何区别。是的,如果绑定对象是明确的,您可以省略它,但显式使用它似乎没有害处。我发布了测试代码,在现实世界中,我想要公开的类有更多的数据要绑定。你也许是对的,我仍然对 WPF 难以学习它感到很困惑。

标签: c# wpf zip bitmapimage


【解决方案1】:

不确定确切原因,但似乎有必要将 zip 流复制到中间 MemoryStream 并从那里读取 BitmapImage。

您还应该编写一个带有属性更改通知的视图模型类:

public class ViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    private BitmapImage routeImage;

    public BitmapImage RouteImage
    {
        get { return routeImage; }
        set
        {
            routeImage = value;
            PropertyChanged?.Invoke(this,
                new PropertyChangedEventArgs(nameof(RouteImage)));
        }
    }

    public void LoadImage(string archiveName, string entryName)
    {
        using (var archive = ZipFile.OpenRead(archiveName))
        {
            var entry = archive.GetEntry(entryName);
            if (entry != null)
            {
                using (var zipStream = entry.Open())
                using (var memoryStream = new MemoryStream())
                {
                    zipStream.CopyTo(memoryStream); // here
                    memoryStream.Position = 0;

                    var bitmap = new BitmapImage();
                    bitmap.BeginInit();
                    bitmap.CacheOption = BitmapCacheOption.OnLoad;
                    bitmap.StreamSource = memoryStream;
                    bitmap.EndInit();

                    RouteImage = bitmap;
                }
            }
        }
    }
}

将视图模型的实例分配给 Window 的 DataContext:

public MainWindow()
{
    InitializeComponent();

    var viewModel = new ViewModel();
    DataContext = viewModel;

    viewModel.LoadImage(
        @"D:\Games\steamapps\common\RailWorks\Content\Routes\00000036-0000-0000-0000-000000002012\MainContent.ap",
        "RouteInformation/image.png");
}

并像这样绑定RouteImage 属性:

<Image Source="{Binding RouteImage}"/>

如果您打算从 zip 存档中加载大图像文件,我建议您以异步方法调用视图模型代码:

public async Task LoadImageAsync(string archiveName, string entryName)
{
    RouteImage = await Task.Run(() => LoadImage(archiveName, entryName));
}

private BitmapImage LoadImage(string archiveName, string entryName)
{
    BitmapImage bitmap = null;

    using (var archive = ZipFile.OpenRead(archiveName))
    {
        var entry = archive.GetEntry(entryName);
        if (entry != null)
        {
            using (var zipStream = entry.Open())
            using (var memoryStream = new MemoryStream())
            {
                zipStream.CopyTo(memoryStream);
                memoryStream.Position = 0;

                bitmap = new BitmapImage();
                bitmap.BeginInit();
                bitmap.CacheOption = BitmapCacheOption.OnLoad;
                bitmap.StreamSource = memoryStream;
                bitmap.EndInit();
                bitmap.Freeze(); // necessary when loaded in non-UI thread
            }
        }
    }

    return bitmap;
}

从 MainWindow 中的异步 Loaded 事件处理程序调用方法:

Loaded += async (s, e) => await viewModel.LoadImageAsync
    @"D:\Games\steamapps\common\RailWorks\Content\Routes\00000036-0000-0000-0000-000000002012\MainContent.ap",
    "RouteInformation/image.png");

【讨论】:

  • 非常感谢克莱门斯!复制到内存流就可以了。不需要 PropertyChanged,因为我打算在类构造函数中加载一次图像。并且图像很小。但是你给了我一个非常有用的例子。 :-)
  • 不客气。迟早您一定会发现 MVVM 模式非常很有用。我强烈建议始终实施它,即使在这样的简单情况下也是如此。
【解决方案2】:

在我从 zip 容器中删除图像的工作中,根据 Clemens 的回答使用中间线程只会创建一个异常并且是不成立的。

最终我能够使用此代码提取BitmapImage

public static class StreamExtensions
{
    public static BitmapImage ToImage(this Stream stream)
    {       
        var bitmap = new BitmapImage();
        bitmap.BeginInit();
        bitmap.StreamSource = stream;
        bitmap.CacheOption = BitmapCacheOption.OnLoad;
        bitmap.EndInit();

        return bitmap;
     }
}

用法

var image = zipEntry.Open().ToImage();

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-06-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多