2009-06-28 17 views
2

我试图编写代码,将从资源加载图像,然后裁剪它。当我在XAML中完成全部或部分工作时,此代码就可以工作。我想从全部XAML切换到全部代码,所以我可以在不同的Uris中重复使用这个不止一个地方。在运行时创建一个CroppedBitmap - 将不会从资源加载

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

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

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

对于以下所有代码示例,我在我的项目中创建了一个Images文件夹,并在其中添加了一个名为“elf.png”的现有图像,并将属性设置为默认值(Build Action =“Resource”; Copy到输出目录=“不要复制”)。情况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;代码3中的BitmapImage;代码3中的BitmapImage;代码3中的BitmapImage;代码3中的BitmapImage;代码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:“无法创建‘窗口1’汇编定义的实例‘WpfApplication8,版本= 1.0.0.0,文化=中立,公钥=空’异常被抛出由目标在标记文件'Window1.xaml'中的错误行1位置13.“
    • 内部异常: TargetInvocationException:“异常被调用的目标抛出。
      • 内部异常: DirectoryNotFoundException: “找不到路径 ':\ SVN \ WpfApplication8 \ WpfApplication8 \ BIN \调试\图像\ elf.png C' 的一部分。”

内-内异常堆栈跟踪显示原始异常(在DirectoryNotFoundException)正被用于实例化CroppedBitmap线抛出。我不知道为什么那行会尝试从磁盘读取数据,或者为什么它不能正常工作,当我可以告诉等效的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版本中发生了什么变化?

回答

4

神奇竟然是在BitmapImage的BaseUri属性中。 BaseUri显然是UriSource相关的“当前目录”。

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

为什么从XAML工作(从刚刚的BitmapImage - 不 - CroppedBitmap)

那么,没有这种神奇的基本URI值从何而来?

BaseUri是IUriContext接口的一部分。 IUriContext.BaseUri被设置在WPF程序集中的两个地方,并且在我的各种示例之间,我设法击中了两个。难怪我很困惑。

  • BamlRecordReader.ElementInitialize。 BAML加载器在加载实现IUriContext的元素时自动设置BaseUri。这解释了为什么我的例子#1和#2工作:他们从编译的BAML资源加载。
  • Image.UpdateBaseUri(每当Source属性发生变化时调用)。这将检查Source是否实现IUriContext,如果是,则设置它的BaseUri。这解释了为什么我的示例#3工作:将BitmapImage推入GUI迫使它获得正确的搜索路径。

当BaseUri设置为magic pack:// URI时,它仅查找EXE资源中的图像。如果没有这种情况(当所有事情都是在代码中创建并且没有推入GUI时),它只会在磁盘上显示。

的修复

如上所述,我可以硬编码的基本URI。但BaseUriHelper类提供了一个更好的解决办法:

fullImage.BaseUri = BaseUriHelper.GetBaseUri(this); 

这台fullImage具有相同的基本URI的窗口(this)。如果这是在创建CroppedBitmap之前完成的,则一切正常。