【问题标题】:Creating a CroppedBitmap at runtime - won't load from resource在运行时创建 CroppedBitmap - 不会从资源加载
【发布时间】:2009-06-28 23:17:59
【问题描述】:

我正在尝试编写代码来从资源中加载图像,然后对其进行裁剪。当我在 XAML 中完成全部或部分代码时,此代码有效。我想从全 XAML 切换到全代码,所以我可以在多个地方重用它,使用不同的 Uris。

但是当我尝试在代码中做同样的事情时,我得到了 DirectoryNotFoundException,因为它突然开始尝试在磁盘上查找文件夹,而不是从资源中加载图像。

  • 如果我在 XAML 中加载 BitmapImage,然后在 XAML 中创建 CroppedBitmap,一切正常。
  • 如果我在 XAML 中加载 BitmapImage,然后编写代码从中创建 CroppedBitmap,一切正常。
  • 如果我在代码中加载 BitmapImage, 从它创建 CroppedBitmap,一切正常。
  • 但是如果我在代码中加载 BitmapImage 在代码中创建一个 CroppedBitmap,它会尝试从文件系统而不是资源加载,我得到一个 DirectoryNotFoundException。

代码示例如下。我确定我在做一些愚蠢的事情,但我现在已经完成了整件事三遍(一次在我的真实应用程序中,一次在测试应用程序中,一次在写这个问题时),我得到了同样的结果结果全部 3 次。

对于以下所有代码示例,我在我的项目中创建了一个 Images 文件夹,并在其中添加了一个名为“elf.png”的现有图像,其属性设置为默认值(Build Action = “Resource”;复制到输出目录 = "请勿复制")。


案例 1:XAML 中的 BitmapImage 和 CroppedBitmap。

<Window x:Class="WpfApplication8.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Window1" Height="300" Width="300">
    <Window.Resources>
        <BitmapImage x:Key="fullImage" UriSource="Images/elf.png"/>
        <CroppedBitmap x:Key="croppedImage" Source="{StaticResource fullImage}"
                       SourceRect="0 0 240 320"/>
    </Window.Resources>
    <Image Source="{StaticResource croppedImage}"/>
</Window>

这显示了位图的裁剪部分,正如预期的那样。


案例 2:XAML 中的 BitmapImage;代码隐藏中的 CroppedBitmap。

XAML:

<Window x:Class="WpfApplication8.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Window1" Height="300" Width="300">
    <Window.Resources>
        <BitmapImage x:Key="fullImage" UriSource="Images/elf.png"/>
    </Window.Resources>
    <Image Name="image"/>
</Window>

代码隐藏中的构造函数:

public Window1()
{
    InitializeComponent();
    var fullImage = (BitmapImage) FindResource("fullImage");
    var croppedImage =
        new CroppedBitmap(fullImage, new Int32Rect(0, 0, 240, 320));
    image.Source = croppedImage;
}

这也显示了位图的裁剪部分,正如预期的那样。


案例3:代码中的BitmapImage;没有 CroppedBitmap。

public Window1()
{
    InitializeComponent();
    var uri = new Uri("Images/elf.png", UriKind.RelativeOrAbsolute);
    var fullImage = new BitmapImage(uri);
    image.Source = fullImage;
}

这显示了整个位图。这不是我想要的,但确实告诉我我知道如何编写 C# 代码来创建正确类型的 Uri 并从资源加载 BitmapImage。


案例 4:代码中的 BitmapImage 和 CroppedBitmap。

    public Window1()
    {
        InitializeComponent();
        var uri = new Uri("Images/elf.png", UriKind.RelativeOrAbsolute);
        var fullImage = new BitmapImage(uri);
        var croppedImage =
            new CroppedBitmap(fullImage, new Int32Rect(0, 0, 240, 320));
        image.Source = croppedImage;
    }

据我所知,这只是将与以前相同的部分组合在一起。它使用我知道将从资源中加载 BitmapImage 的代码,以及我知道将从加载的 BitmapImage 中裁剪一部分的代码。但是不知何故,当两者放在一起时,它忘记了资源在那里,并尝试从磁盘加载。我得到以下异常:

  • XamlParseException:“无法创建在程序集 'WpfApplication8,Version=1.0.0.0,Culture=neutral,PublicKeyToken=null' 中定义的 'Window1' 的实例。调用的目标已引发异常。标记文件中的错误 ' Window1.xaml' 第 1 行位置 13。”
    • 内部异常: TargetInvocationException:“调用的目标已抛出异常。”
      • 内部异常: DirectoryNotFoundException:“找不到路径'C:\svn\WpfApplication8\WpfApplication8\bin\Debug\Images\elf.png'的一部分。”

inner-inner-exception 堆栈跟踪显示原始异常(DirectoryNotFoundException)是由实例化 CroppedBitmap 的行抛出的。我不知道为什么该行会尝试从磁盘读取,或者为什么当 as-far-as-I-can-tell-equivalent XAML 工作正常时它不起作用。

由于我知道 XAML 使用的是无参数构造函数,因此我还尝试了以下版本,它应该更接近 XAML 的实际作用:

public Window1()
{
    InitializeComponent();
    var uri = new Uri("Images/elf.png", UriKind.RelativeOrAbsolute);
    var fullImage = new BitmapImage();
    fullImage.BeginInit();
    fullImage.UriSource = uri;
    fullImage.EndInit();
    var croppedImage = new CroppedBitmap();
    croppedImage.BeginInit();
    croppedImage.Source = fullImage;
    croppedImage.SourceRect = new Int32Rect(0, 0, 240, 320);
    croppedImage.EndInit();
    image.Source = croppedImage;
}

同样的例外,这次来自croppedImage.EndInit(); 行。

关于如何获得全代码版本以正确加载资源并裁剪图像的任何想法? XAML 版本中发生了什么不同的情况?

【问题讨论】:

    标签: wpf exception resources bitmapimage


    【解决方案1】:

    神奇之处在于 BitmapImage 的 BaseUri 属性。 BaseUri 显然是 UriSource 相对的“当前目录”。

    当我的 BitmapImage 从 XAML 加载时,BaseUri 被神奇地设置为“pack://application:,,,/WpfApplication8;component/window1.xaml”。当我修改代码 sn-p #4 以在创建 CroppedBitmap 之前将 fullImage.BaseUri 显式设置为相同的值时,一切正常。

    为什么它在 XAML 中起作用 (以及来自 just-BitmapImage-without-CroppedBitmap)

    那么这个神奇的 BaseUri 值从何而来?

    BaseUri 是IUriContext 接口的一部分。 IUriContext.BaseUri 设置在 WPF 程序集中的两个位置,在我的各种示例之间,我设法击中了它们中的 both。难怪我一头雾水。

    • BamlRecordReader.ElementInitialize。 BAML 加载器在加载实现 IUriContext 的元素时自动设置 BaseUri。这解释了为什么我的示例 #1 和 #2 有效:它们是从编译后的 BAML 资源加载的。
    • Image.UpdateBaseUri(在 Source 属性更改时调用)。这将检查 Source 是否实现 IUriContext,如果是,则设置其 BaseUri。这解释了为什么我的示例 #3 有效:将 BitmapImage 推入 GUI 会强制它获得正确的搜索路径。

    当 BaseUri 设置为 magic pack:// URI 时,它只会在 EXE 资源中查找图像。没有它(就像在代码中创建所有内容而不是推送到 GUI 中一样),它只会在磁盘上显示。

    修复

    如上所述,我可以硬编码 BaseUri。但是BaseUriHelper 类提供了更好的解决方法:

    fullImage.BaseUri = BaseUriHelper.GetBaseUri(this);
    

    这会将 fullImage 设置为与窗口具有相同的 BaseUri (this)。如果在创建 CroppedBitmap 之前完成此操作,则一切正常。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-08-16
      • 2014-09-10
      • 1970-01-01
      • 2012-04-04
      • 2015-11-30
      • 1970-01-01
      • 2013-02-26
      相关资源
      最近更新 更多