问题描述
我正在尝试编写代码,该代码将从资源中加载图片,然后对其进行裁剪。当我在XAML中全部或部分执行此代码时,此代码将起作用。我想从全XAML切换到全代码,因此我可以使用不同的Uris在一个地方重复使用。
I'm trying to write code that will load an image from a resource, and then crop it. This code works when I do all, or part, of it in XAML. I want to switch from all-XAML to all-code, so I can reuse this more than one place, with different Uris.
但是当我尝试执行相同操作时在代码中,我得到了DirectoryNotFoundException,因为它突然开始尝试在磁盘上查找文件夹,而不是从资源中加载图像。
But when I try to do the same thing in code, I get a DirectoryNotFoundException, because suddenly it starts trying to look for a folder on disk, instead of loading the image from the resource.
- 如果我在XAML中加载BitmapImage,然后在XAML中创建一个CroppedBitmap,则一切正常。
- 如果我在XAML中加载BitmapImage,然后编写代码以从中创建CroppedBitmap
- 如果我将BitmapImage加载到代码中,则不从中创建CroppedBitmap,一切正常。
- 但是如果我在代码中加载BitmapImage并在代码中创建一个CroppedBitmap,它将尝试从文件系统而不是资源中加载,并且我得到DirectoryNotFoundException。
- If I load the BitmapImage in XAML, and then create a CroppedBitmap in XAML, everything works.
- If I load the BitmapImage in XAML, and then write code to create a CroppedBitmap from it, everything works.
- If I load the BitmapImage in code, without creating a CroppedBitmap from it, everything works.
- But if I load the BitmapImage in code and create a CroppedBitmap in code, it tries to load from the filesystem instead of the resources, and I get a DirectoryNotFoundException.
代码示例如下。我确定我做某件事很愚蠢,但是现在我已经经历了整整三遍了(一次在我的真实应用程序中,一次在测试应用程序中,一次在编写此问题时),并且我得到了相同的结果对所有三个代码都返回结果。
Code samples are below. I'm sure I'm doing something stupid, but I've run through the whole thing three times now (once in my real app, once in a test app, and once while writing up this question), and I got the same results all three times.
对于以下所有代码示例,我都在项目内部创建了一个Images文件夹,并在其中添加了一个现有的图片,称为 elf.png ,其属性设置为默认值(生成操作=资源;复制到输出目录=不复制)。
For all of the following code samples, I've created an Images folder inside my project, and added an existing image there called "elf.png", with properties set to defaults (Build Action = "Resource"; Copy to Output Directory = "Do not copy").
案例1:XAML中的BitmapImage和CroppedBitmap。
Case 1: Both BitmapImage and CroppedBitmap in 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"/>
<CroppedBitmap x:Key="croppedImage" Source="{StaticResource fullImage}"
SourceRect="0 0 240 320"/>
</Window.Resources>
<Image Source="{StaticResource croppedImage}"/>
</Window>
这显示了位图的裁剪部分,如预期的那样。
This shows the cropped portion of the bitmap, as expected.
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;
}
这也显示了预期的位图裁剪部分。
This also shows the cropped portion of the bitmap, as expected.
案例3:代码中的BitmapImage;
Case 3: BitmapImage in code; no CroppedBitmap.
public Window1()
{
InitializeComponent();
var uri = new Uri("Images/elf.png", UriKind.RelativeOrAbsolute);
var fullImage = new BitmapImage(uri);
image.Source = fullImage;
}
这将显示整个位图。这不是我想要的,但确实告诉我,我知道如何编写C#代码来创建正确的Uri并从资源中加载BitmapImage。
This shows the entire bitmap. This isn't what I want, but does tell me that I know how to write C# code to create the right kind of Uri and load a BitmapImage from a resource.
案例4:代码中的BitmapImage和CroppedBitmap。
Case 4: BitmapImage and CroppedBitmap in code.
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中裁剪出一部分。但是以某种方式,当将两者放在一起时,它会忘记资源在那里,而是尝试从磁盘加载。我收到以下异常:
As far as I can tell, this just puts together the same pieces as before. It uses code that I know will load a BitmapImage from a resource, and code that I know will crop a section from a loaded BitmapImage. But somehow, when the two are put together, it forgets that the resource is there, and tries to load from disk. I get the following exception:
- XamlParseException:无法创建在程序集'WpfApplication8,Version = 1.0.0.0中定义的'Window1'实例, 文化=中性,PublicKeyToken =空'。调用的目标引发了异常。标记文件'Window1.xaml'第1行位置13出错。
- 内部异常: TargetInvocationException:调用的目标抛出了异常。
- 内部异常: DirectoryNotFoundException:找不到路径'C:\svn\WpfApplication8\的一部分 WpfApplication8\bin\Debug\Images\elf.png'。
- XamlParseException: "Cannot create instance of 'Window1' defined in assembly 'WpfApplication8, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'. Exception has been thrown by the target of an invocation. Error in markup file 'Window1.xaml' Line 1 Position 13."
- Inner exception: TargetInvocationException: "Exception has been thrown by the target of an invocation."
- Inner exception: DirectoryNotFoundException: "Could not find a part of the path 'C:\svn\WpfApplication8\WpfApplication8\bin\Debug\Images\elf.png'."
内部异常异常堆栈跟踪显示,实例化CroppedBitmap的行引发了原始异常(DirectoryNotFoundException)。我不知道为什么该行会尝试从磁盘读取数据,或者当我可以告诉等效的XAML正常工作时为什么它行不通。
The inner-inner-exception stack trace shows that the original exception (the DirectoryNotFoundException) is being thrown by the line that instantiates the CroppedBitmap. I don't know why that line would be trying to read from disk, or why it doesn't work when the as-far-as-I-can-tell-equivalent XAML works fine.
由于我知道XAML使用的是无参数构造函数,因此我也尝试了以下版本,该版本应与XAML实际工作更接近:
Since I know the XAML is using the parameterless constructors, I also tried the following version, which should be much closer to what the XAML actually does:
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版本中发生了什么不同?
Any ideas on how I can get the all-code version to correctly load the resource and crop the image? What's happening in the XAML version that's different?
推荐答案
魔术竟然是在BitmapImage的 BaseUri 中>属性。 BaseUri显然是UriSource相对的当前目录。
The magic turned out to be in the BitmapImage's BaseUri property. BaseUri apparently serves as the "current directory" that UriSource is relative to.
当从XAML加载BitmapImage时,BaseUri被神奇地设置为 pack://。 application:,, / WpfApplication8; component / window1.xaml。当我在创建CroppedBitmap之前修改了代码片段#4来将fullImage.BaseUri显式设置为相同的值时,一切正常。
When my BitmapImage was being loaded from XAML, BaseUri was being magically set to "pack://application:,,,/WpfApplication8;component/window1.xaml". When I modified code snippet #4 to explicitly set fullImage.BaseUri to that same value before creating the CroppedBitmap, everything worked.
为什么它可以从XAML运行 (以及来自Just-BitmapImage-without-CroppedBitmap)
那么这个神奇的BaseUri值是从哪里来的?
So where did this magic BaseUri value come from?
BaseUri是接口。 IUriContext.BaseUri在WPF程序集中的两个位置设置,在我的各个示例之间,我设法都击中了它们。难怪我很困惑。
BaseUri is part of the IUriContext interface. IUriContext.BaseUri is set in two places in the WPF assemblies, and between my various examples, I managed to hit both of them. No wonder I was confused.
- BamlRecordReader.ElementInitialize。 BAML加载器在加载元素时会自动设置BaseUri。实现IUriContext。这解释了为什么我的示例#1和#2起作用的原因:它们是从编译后的BAML资源加载的。
- Image.UpdateBaseUri (只要Source属性发生更改,就会调用)。这将检查Source是否实现IUriContext,如果是,则设置其BaseUri。这解释了我的示例#3为何起作用的原因:将BitmapImage推入GUI强制其获得正确的搜索路径。
- BamlRecordReader.ElementInitialize. The BAML loader automatically sets BaseUri anytime it loads an element that implements IUriContext. This explains why my examples #1 and #2 worked: they were loading from the compiled BAML resource.
- Image.UpdateBaseUri (called whenever the Source property is changed). This checks to see if the Source implements IUriContext, and if so, sets its BaseUri. This explains why my example #3 worked: pushing the BitmapImage into the GUI forced it to get the right search path.
当BaseUri设置为magic pack:// URI时,在EXE资源中获取图像。否则(仅当在代码中创建所有内容而不将其推送到GUI中时发生),它只会在磁盘上显示。
It only looks for the image in the EXE resources when BaseUri is set to the magic pack:// URI. Without that (as happens when everything is created in code and not pushed into the GUI), it only looks on disk.
修复
如上所述,我可以对BaseUri进行硬编码。但是
BaseUriHelper
类提供了更好的解决方案:As noted above, I could hard-code BaseUri. But the
BaseUriHelper
class provides a better fix:fullImage.BaseUri = BaseUriHelper.GetBaseUri(this);
这会将fullImage设置为与窗口具有相同的BaseUri(
此
)。如果在创建CroppedBitmap之前完成此操作,则一切正常。This sets fullImage to have the same BaseUri as the window (
this
). If this is done before creating the CroppedBitmap, everything works.这篇关于在运行时创建CroppedBitmap-不会从资源加载的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!
- Inner exception: TargetInvocationException: "Exception has been thrown by the target of an invocation."
- 内部异常: TargetInvocationException:调用的目标抛出了异常。