Cannot load resource dictionary with converter - wpf

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 :)

Related

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

Where do XAML files that build as "Page" go?

I've added a XAML file to a Windows Phone 8 project. Its build action is "Page". I want to load the XAML as a text string (to feed into XamlReader.Load()). How can I accomplish this?
It's not available as a separate file in the XAP package; it's probably somewhere in the DLL.
When set to Page, the compiler will compile the XAML into BAML and add the BAML file as a resource to the assembly.
If you wish to get the original XAML back out from the BAML resource at runtime, then you will need to deserialize the BAML, and then serialize your object to XAML.
You can have a look at the Baml2006Reader, or a better option would be to use Application.LoadComponent which is what the InitializeComponent method uses internally. InitializeComponent is called by the partially generated class for your XAML code behind.
var uri = new Uri("/MyAppName;component/MyXaml.xaml", //Note extension: XAML, not BAML
UriKind.Relative);
Page rootObject = new Page(); //Assuming XAML root element is Page - it could be anything
Application.LoadComponent(rootObject, uri);
(assuming the root element of your XAML file is a Page).
You can then serialize the Page to a XAML string using the XamlWriter:
string xaml = XamlWriter.Save(rootObject);
Note that this is the XamlWriter in the System.Windows.Markup namespace, not System.Xaml. If your XAML has WPF types, then you should use this XamlWriter to avoid errors.

How to get at ResourceDictionary style when it is loaded from external xap and assemblies are MEF-fed?

I've got the following setup:
The main application loads a XAP with an IPlugin implementation. The Plugin contains a 'DisplayPanel' that contains a referenced Control with other controls. The DisplayPanel here is simply a container control to show referenced Control.
This referenced Control, from an assembly, uses a Style from a ResourceDictionary xaml in this assembly. At least that's what I want to have. The problem is that the referenced Control throws an error:
Cannot find a Resource with the Name/Key PlayerPanelGrad [Line: 1500
Position: 127]
I've tried to get at the style by referencing theResourceDictionary through a Merged Resource dictionary reference:
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="TableControls;component/ControlsStyle.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
But that doesn't work.
How would you approch this?
the only way i got it to work is by loading the Resource dictionary into the control (in a Class Library) programmatically before the InitializeComponent call:
public ActionPanel()
{
StreamResourceInfo sr = Application.GetResourceStream(
new Uri("TableControls;component/ControlsStyle.xaml", UriKind.Relative));
Application.Current.Resources.Add("plop",sr.Stream);
// Required to initialize variables
InitializeComponent();
}
This question may be of help, although, honestly, I'm still trying to figure it out myself:
Using MEF to import a WPF DataTemplate?
//load other.dll dynamically first,and then use the following code:
StreamResourceInfo srf = Application.GetResourceStream(new Uri("otherdll;component/Resources/Brush.xaml", UriKind.Relative));
StreamReader sr = new StreamReader(srf.Stream);
string stt = sr.ReadToEnd();
ResourceDictionary dict = XamlReader.Load(stt) as ResourceDictionary;
Application.Current.Resources.MergedDictionaries.Add(dict);
For future reference, my XAML file was found in a subdirectory of the solution which needed the / character but also file was further in a subdirectory named Assets within it.
<ResourceDictionary
Source="/MyAssemblyName;component/Assets/RadResources.xaml" />
Also the .XAML file was built as Page in the solution.

Where is the right place for resource used by controls in module when using prism?

Normally, all resources are put in app.xaml, or other resources xaml files (as resource dictionary) and then reference it in app.xaml.
When applying prism pattern, for those module, there is no app.xaml file. Application class is replaced by a class implementing interface IModule. So where is the right place for resources used by controls in the module?
this is how i do it: have modules register resources with the app.
Composite WPF (Prism) module resource data templates
You can add the resource dictionary in the same assembly as the module or in other loaded assembly, then access it programmatically by using the Application.GetResourceStream method; however you will need to know the name of the assembly where the resource is defined.
For example, if your assembly is named TheAssembly and the resource dictionary is named TheDictionary.xaml, you can do (Disposes not shown for brevity):
StreamResourceInfo sr = Application.GetResourceStream(
new Uri("/TheAssembly;component/TheDictionary.xaml", UriKind.Relative));
StreamReader r=new StreamReader(sr.Stream);
string xaml=r.ReadToEnd();
ResourceDictionary rd = (ResourceDictionary)XamlReader.Load(xaml);
From here, you can for example use the Unity container to make the resource dictionary available application-wide.
UPDATE
The following version avoids having to hard-code the assembly name, provided that the resource is in the currently executing assembly:
string assemblyName = Assembly.GetExecutingAssembly().FullName.Split(',')[0];
string uri = string.Format("/{0};component/Dictionary1.xaml", assemblyName);
StreamResourceInfo sr = Application.GetResourceStream(
new Uri(uri, UriKind.Relative));
//etc...

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

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.

Resources