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.
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.
- 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.
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").
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.
Case 2: BitmapImage in XAML; CroppedBitmap in code-behind.
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>
Constructor in code-behind:
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.
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;
}
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.
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;
}
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: "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'."
- Inner exception: TargetInvocationException: "Exception has been thrown by the target of an invocation."
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.
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;
}
Same exception, this time from the croppedImage.EndInit();
line.
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?