Creating a CroppedBitmap at runtime - won't load from resource - wpf

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'."
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?

The magic turned out to be in the BitmapImage's BaseUri property. BaseUri apparently serves as the "current directory" that UriSource is relative to.
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.
Why it worked from XAML (and from just-BitmapImage-without-CroppedBitmap)
So where did this magic BaseUri value come from?
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. 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.
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.
The fix
As noted above, I could hard-code BaseUri. But the BaseUriHelper class provides a better fix:
fullImage.BaseUri = BaseUriHelper.GetBaseUri(this);
This sets fullImage to have the same BaseUri as the window (this). If this is done before creating the CroppedBitmap, everything works.

Related

WPF Cropping an imagesource in DrawingVisual

I need to display a cropped region of an image source within a DrawingVisual, but for the past few hours I've just drawn blanks for what should be a simple task.
My plan is to implement timed animation (for a HMI system) which will take a big image containing various frames. Depending on a state variable, the DrawingVisual will extract from this big image, the current frame to show - hence the need to crop.
My code stands as follows:
int width = Convert.ToInt32(_imageSource.Width / 2);
int height = Convert.ToInt32(_imageSource.Height);
BitmapImage bm = _imageSource.Clone() as BitmapImage;
bm.BaseUri = BaseUriHelper.GetBaseUri(this);
CroppedBitmap c = new CroppedBitmap(bm, new Int32Rect(0, 0, width, height));
ImageDrawing id = new ImageDrawing(c, new Rect(0, 0, width, height));
dg.Children.Add(id);
dc.DrawDrawing(dg);
However, upon creating the cropped image, I receive
"value does not fall within expected range"
BTW The image is a png loaded within a resource dictionary (in the current assembly) as
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<BitmapImage x:Key="elbowSmallBlueBL" UriSource="./Assets/HMI/Elbows/Small/Elbow Blue BottomLeft.png" />
and then assigned to the DrawingVisual using (ImageSource)FindResource("...
Firstly I don't understand the error message and can't find an satisfactory explanation anywhere and secondly, why is it sooooo difficult to perform a simple task?
Thanks for any help.
p.s. I think this due to not being able to locate the resource. But if the original imagesource is fine why would the cloned version behave any differently?
p.p.s Removing the line "bm.BaseUri = ..." results in same error.
To simplify things, I've created a test project that only has a button which attempts to create CroppedBitmap when clicked.
The button click =
private void Button_Click(object sender, RoutedEventArgs e)
{
BitmapSource i = (BitmapSource)FindResource("elbowSmallBlueBL");
try
{
CroppedBitmap c = new CroppedBitmap(i, new Int32Rect(0, 0, 100, 100));
}
catch (Exception ex)
{
}
}
}
The resource dictionary references the bitmap :
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<!-- Elbows -->
<BitmapImage x:Key="elbowSmallBlueBL" UriSource="./Assets/HMI/Elbows/Small/Elbow Blue BottomLeft.png" />
and included the App.xaml
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="rdHMIControls.xaml" />
The BitmapSource (i) is valid and the call to CroppedBitmap throws the ArgumentException, "Value does not fall within the accepted range".
Creating, or trying to create a cropped image surely can't be any simpler than this?
Another example which I've taken off MSDN
BitmapSource i = (BitmapSource)this.FindResource("test");
try
{
// Create an Image element.
Image croppedImage = new Image();
croppedImage.Width = 200;
croppedImage.Margin = new Thickness(5);
// Create a CroppedBitmap based off of a xaml defined resource.
CroppedBitmap cb = new CroppedBitmap(
i,
new Int32Rect(30, 20, 105, 50)); //select region rect
croppedImage.Source = cb; //set image source to cropped
Again new CroppedBitmap throws InvalidArgument exceptin
Any thoughts?
Thanks

How can I access a resource from a ValueConverter?

I have a UserControl in a custom DLL assembly where I've defined two static BitmapImage resources that represent the state of data in our ItemsControl. I want to use a converter to set the Source property of an Image to one of the BitmapImage resources depending on some condition. However, I'm not sure how to access the resources from inside the Convert method since I don't have an instance of the control that I'm using the converter on.
I've tried loading the resources into static variables in a static constructor for the converter, which is also in the same DLL, but I haven't been successful.
This fails...
public class MyConverter : IValueConverter
{
static BitmapImage myFirstResource;
static MyConverter()
{
// This can't seem to find the resource...
myFirstResource = (BitmapImage)Application.Current.FindResource("MyResourceKey");
}
}
...but in the XAML, this succeeds, so I know the resource key is valid.
<Image Source="{StaticResource MyResourceKey}" />
I don't know if this makes any difference, but this is in a DLL, not in the EXE. Still, I thought all resources were flattened down to the application depending on where you were executing from.
Found perfect solution here Accessing a resource via codebehind in WPF
(better than using Application.Current)
#itsho
You can simply add x:Class to it:
<ResourceDictionary x:Class="Namespace.NewClassName"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" >
<ds:MyCollection x:Key="myKey" x:Name="myName" />
</ResourceDictionary>
And then use it in code behind:
var res = new Namespace.NewClassName();
var col = res["myKey"];
Then a little fix should be applied:
#Stephen Ross
But to be able to find resources using it's key I had to call res.InitializeComponent() before attempting to access the key otherwise the object would show no keys and the call to res["myKey"] would return null

Cannot load resource dictionary with converter

Everything happens within the same VS project. I have a resource dictionary file living on it's own. When I try to load it programmatically I get the error
"Cannot create unknown type '{clr-namespace:MyAssembly.Helpers}IsNullConverter".
Here is how I load it :
StreamResourceInfo stream = Application.GetResourceStream(new Uri(#"MyAssembly;component/Resources/Resources.xaml", UriKind.Relative));
this.dynamicResources = XamlReader.Load(stream.Stream) as ResourceDictionary;
And here is the resource dictionary :
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:helpers="clr-namespace:MyAssembly.Helpers">
<helpers:IsNullConverter x:Key="IsNullConverter" />
Styles go here...
Note that it is tied to a code-behind file, but there is nothing in it. The Build-Action of the resource file is set to "Resource". This is driving me crazy since this morning and still no clue what the heck is going on...
Help.
Thank you.
Halelujah I fugured it out. All I had to do is load the resource dictionary directly
Uri uri = new Uri(#MyAssembly;component/Resources/Resources.xaml", UriKind.Relative);
this.dynamicResources.Source = uri;
And make sure Build Action of resource dictionary file is set to "Page"
\m/
Is the assembly referenced by your project? If not try adding a reference - if you don't want a dependency you could try loading the assembly:
http://www.dreamincode.net/forums/topic/78974-using-reflection-to-load-unreferenced-assemblies-at-runtime/
Alternatively you could add a x:Class definition to the resourcedictionary, and instantiate the class from the assembly instead of loading the xaml, and remember to call the generated InitializeComponent() from the constructor, the it will load.
Is it possible to set code behind a resource dictionary in WPF for event handling?
Your example would work fine if the ResourceDictionary and converter was in the same assembly as where you load from, as far as I can see :)

Writing out FlowDocument xaml with namespace using XmlWriter

I've got a collection of data that needs to be converted to a .xaml file that can later be loaded as a FlowDocument into a FlowDocumentReader. I don't directly instantiate Paragraphs, Runs, rather I generate the xaml to create the document later.
What I've tried:
I iterate through the data, creating XElements for Paragraphs, Runs, InlineUIContainers, etc. and build up the FlowDocument structure just fine and then call:
XmlWriter writer = XmlWriter.Create("output.xaml");
flowDocElem.WriteTo(writer);
writer.Close();
In the consuming app, I do this:
flowDocument = XamlReader.Load(xamlFile) as FlowDocument;
flowDocumentReader.Document = flowDocument;
xamlFile.Close();
But the loading fails because it doesn't know what a FlowDocument is. The FlowDocument element looks like so:
<FlowDocument Name="testDoc">
(There's no namespace there to shed light as to what a FlowDocument is when it is read in.)
If I hand edit the .xaml and modify the element to be:
<FlowDocument xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
Name="testDoc">
Then it'll load just fine.
When creating the XElement for the FlowDocument, I've tried to do this:
new XElement("FlowDocument", new XAttribute("Name", "testDoc"), new XAttribute("xmlns", "http://schemas.microsoft.com/winfx/2006/xaml/presentation"));
but that doesn't work either - gives me an error if I try to create the namespace attribute.
I can completely cheat and stuff that xmlns into the element and then call something like
File.WriteAllText("output.xaml", fixedTxt);
but that feels dirty and so I think I'm just plain doing it wrong.
Thoughts?
Update:
While this probably isn't the prescriptive solution to the problem, it does work:
By adding a ParserContext to the XamlReader, I was able to get past the problem with loading the FlowDocument xml.
FileStream xamlFile = new FileStream("output.xaml", FileMode.Open, FileAccess.Read);
XamlReader x = new XamlReader();
ParserContext parserContext = new ParserContext();
parserContext.XmlnsDictionary.Add("","http://schemas.microsoft.com/winfx/2006/xaml/presentation");
flowDocument = XamlReader.Load(xamlFile, parserContext) as FlowDocument;
flowDocumentReader.Document = flowDocument;
xamlFile.Close();
Try using XamlWriter instead of XmlWriter.
If you use XLinq you should try the following:
XNamespace ns = #"http://schemas.microsoft.com/winfx/2006/xaml/presentation";
XNamespace xns = #"http://schemas.microsoft.com/winfx/2006/xaml";
XElement someElement = new XElement(ns + "FlowDocument",
new XAttribute(xns + "Name", name),
...);

How do I use an icon that is a resource in WPF?

I have a .ico file that is embedded as a resource (build action set to resource). I am trying to create a NotifyIcon. How can I reference my icon?
notifyIcon = new NotifyIcon();
notifyIcon.Icon = ?? // my icon file is called MyIcon.ico and is embedded
Your icon file should be added to one of your project assemblies and its Build Action should be set to Resource. After adding a reference to the assembly, you can create a NotifyIcon like this:
System.Windows.Forms.NotifyIcon icon = new System.Windows.Forms.NotifyIcon();
Stream iconStream = Application.GetResourceStream( new Uri( "pack://application:,,,/YourReferencedAssembly;component/YourPossibleSubFolder/YourResourceFile.ico" )).Stream;
icon.Icon = new System.Drawing.Icon( iconStream );
A common usage pattern is to have the notify icon the same as the main window's icon. The icon is defined as a PNG file.
To do this, add the image to the project's resources and then use as follows:
var iconHandle = MyNamespace.Properties.Resources.MyImage.GetHicon();
this.notifyIcon.Icon = System.Drawing.Icon.FromHandle(iconHandle);
In the window XAML:
<Window x:Class="MyNamespace.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:local="clr-namespace:Seahorse"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="600"
Icon="images\MyImage.png">
Well, you don't want to use the resx style resources: you just stick the ico file in your project in a folder (lets say "ArtWork") and in the properties, set the Build Action to "Resources" ...
Then you can reference it in XAML using PACK URIs ... "pack://application:,,,/Artwork/Notify.ico"
See here: http://msdn.microsoft.com/en-us/library/aa970069.aspx and the sample
If you want to be a little bit more ... WPF-like, you should look into the WPF Contrib project on CodePlex which has a NotifyIcon control which you can create in XAML and which uses standard WPF menus (so you can stick "anything" in the menu).
If you are just looking for the simple answer, I think this is it where MyApp is your application name and where that's the root namespace name for your application. You have to use the pack URI syntax, but it doesn't have to be that complicated to pull an icon out of your embedded resources.
<Window x:Class="MyApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Height="100"
Width="200"
Icon="pack://application:,,,/MyApp;component/Resources/small_icon.ico">
I created a project here and used an embedded resource (build action was set to Embedded Resource, rather than just resource). This solution doesn't work with Resource, but you may be able to manipulate it. I put this on the OnIntialized() but it doesn't have to go there.
//IconTest = namespace; exclamic.ico = resource
System.IO.Stream stream = this.GetType().Assembly.GetManifestResourceStream("IconTest.Resources.exclamic.ico");
if (stream != null)
{
//Decode the icon from the stream and set the first frame to the BitmapSource
BitmapDecoder decoder = IconBitmapDecoder.Create(stream, BitmapCreateOptions.None, BitmapCacheOption.None);
BitmapSource source = decoder.Frames[0];
//set the source of your image
image.Source = source;
}

Resources