I am declaring an image within my resource dictionary and then displaying in a user control as follows:
ResourceDictionary.xaml (I am using a style here as I plan to update the image as the user changes what they look at, i.e., company, employee, etc.)
<ImageSource x:Key="CompanyIcon">Images/company_128.png</ImageSource>
<Style x:Key="QuickInfoIcon" TargetType="{x:Type Image}">
<!-- Default Value -->
<Setter Property="Source" Value="{StaticResource CompanyIcon}" />
</Style>
The 'Images' folder is a subfolder of 'Assests'. The 'Assests' folder contains my 'ResourceDictionary.xaml' file and I know the path is correct as I get a designer error if I change the path to something like '../Images/company_128.png'
QuickinfoView.xaml
<UserControl x:Class="SidekickAdmin.Views.QuickInfoView"
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:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d" d:DesignWidth="500" Height="100"
Background="BlanchedAlmond">
<!-- Setup a grid to house the icon and the info -->
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid Grid.Column="0" Name="InfoIcon">
<Image Style="{StaticResource QuickInfoIcon}" Height="50" Width="50"/>
</Grid>
</Grid>
</UserControl>
When viewing the layout in Visual Studio 2012 designer, everything appears correctly but when I run the program I get an error of "XamlParseException occurred: Failed to create a 'ImageSource' from the text 'Images/employee_128.png'." on the ResourceDictionary line with ImageSource.
If I change ImageSource to use a different image it updates as expected within VS2012 designer but then get the same error when trying to run the program.
I have set the Build Action to 'Embedded Resource' on the Resource.resx file but this hasn't fixed the issue.
Any idea on why I am getting the XamlParseException when I try to run this program?
As a side question, when I incorporate images in my program should the image itself (the file) be visible in the bin/debug folder somewhere or is this information hidden with one of the files in bin/debug?
I had the same issue.
You may have a try for what fixed my issue.
Add the file into your project within the Solution Explorer.
I recommend to add folders too dependend on the relative path of your file to the project file.
Then go to:
BUILD -> clean Solution
Finish.
I, too, ran afoul of this issue. Not a single suggested provided by this or any community, most of which were verbatim statements about PACK uri's and other approaches solved the problem. Sadly, for all the enthusiasm people show in answering, most of them haven't a clue on how to fix it.
Setup: 1 solution, 2 projects.
Project 1 was a class library containing resource dictionary containing a list of BitMapSource entries pointing to the local relative path to the images contained below itself.
Example:
<BitmapSource x:Key="RedoImage">Images/Redo.png</BitmapSource>
Project 2 was a WPF application which referenced that class library and which used the MergedDictionary to load the dictionary from the other assembly:
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/WPFResourceLibrary;component/Resources/images.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
In a form in the WPF Application project, I dropped a simple IMAGE control on the form. Under it's source property I could select, under LocalResources, from the list of BitMapSources (by key) from my images.xaml resource dictionary. Making a selection, the image would appear (as expected).
But lo and behold, upon running the application, I would get the dreaded "Failed to create image source from the text ..." message. I fiddled about trying all of the many suggestions, each with no success. Always either the same error, or, no error, but no image, at run time.
Fed up, I produced my own custom control. I chose to NOT override the image source property, so we might use that where appropriate, but extended it by adding a dependency property called ResourceName. This control looks through all of the BitMapSources and ImageSources recorded and available within the current app domain. Bingo, it works.
The code:
<Image x:Class="WPFResourceLibrary.Controls.ImageFromResource"
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"
>
</Image>
And the Code Behind for it.
using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Imaging;
namespace WPFResourceLibrary.Controls
{
/// <summary>
/// ImageFromResource - an extension of the standard Image control that properly handles binding Resource to an Image from a Resource
/// </summary>
public partial class ImageFromResource : Image
{
public ImageFromResource()
{
InitializeComponent();
DependencyPropertyDescriptor imageDescriptor = DependencyPropertyDescriptor.FromProperty(ImageFromResource.ResourceNameProperty, typeof(ImageFromResource));
if (imageDescriptor != null)
{
imageDescriptor.AddValueChanged(this, delegate { SetImage(); });
}
}
public static DependencyProperty ResourceNameProperty = DependencyProperty.Register("ResourceName", typeof(ImageSource), typeof(ImageFromResource), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
[Description("Resource String of the Target Image."), Category("Appearance")]
public ImageSource ResourceName
{
get { return (ImageSource)GetValue(ResourceNameProperty); }
set
{
SetValue(ResourceNameProperty, value);
}
}
private void SetImage()
{
if(ResourceName != null)
this.Source = new BitmapImage(new Uri(ResourceName.ToString()));
}
}
}
Thus, providing the functionality expected of the standard image, without the issues arising.
Note, this was performed in Visual Studio 2012 with SP3.
Paul
Related
My basic goal is to have a ResourceDictionary in a dll which I can use in another WPF project via ResourceDictionary.MergedDictionaries. But I don't want to reference the ResourceDictionary by hard-coding the URI in the XAML of the referencing application, I want to instead reference some static member which will provide the URI.
I have some simplified code which is "working", but only at runtime. At design-time it throws errors and I get no IntelliSense support. For this simplified example, everything is in one assembly (no separate dll).
Dic.xaml (the resource dictionary I want to reference):
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<SolidColorBrush Color="Blue" x:Key="BlueBrush"/>
</ResourceDictionary>
Foo (the module to hold the static member with the URI):
(VB.NET version)
Public Module Foo
'[VBTest] is the name of assembly
Public ReadOnly Property URI As New Uri("pack://application:,,,/VBTest;component/Dic.xaml")
End Module
(C# version)
public static class Foo
{
//[VBTest] is the name of assembly
public static Uri URI { get; } = new Uri("pack://application:,,,/VBTest;component/Dic.xaml");
}
And then finally, the place in the application where I want to reference the ResourceDictionary:
<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:VBTest">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="{x:Static local:Foo.URI}"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<Border Width="100" Height="100" Background="{StaticResource BlueBrush}"/>
</Window>
At design-time, I get two errors:
XDG0062 An error occurred while finding the resource dictionary "".
XDG0062 The resource "BlueBrush" could not be resolved.
However, the project will build and run just fine. And will show the intended blue square.
The question is, how can I get this to work at design-time?
I found a thankfully easy workaround. Maybe not the prettiest, but it is elegant. I took inspiration from this answer to a somewhat related question.
By creating my own class inheriting from ResourceDictionary, I can better control the loading behavior. That sounds like it would be complicated, but really all I had to do was set Source as part of the constructor and everything just worked.
The below is added to the code file (outsite of Module/static class Foo):
Public Class StylesResourceDictionary
Inherits ResourceDictionary
Public Sub New()
MyBase.New()
Source = URI
End Sub
End Class
Note that Source = URI is referencing Foo.URI from the question code.
And then the XAML file becomes:
<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:VBTest">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<local:StylesResourceDictionary/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<Border Width="100" Height="100" Background="{StaticResource BlueBrush}"/>
</Window>
And bam, full design-time support without hard-coding the URI into the referencing application. Control of the ResourceDictionary and its URI is now in the domain of the dll, and can be referenced in a (kinda) static fashion using the dedicated class.
I came across this oddity while trying to resolve issues using merged ResourceDictionaries in a WPF app I'm working on.
I have custom controls (TextButton, MenuButton) and resources (colors, brushes, control styles and custom control templates) defined in an external DLL ("common"). In another library I have a user control that uses these styles ("pluginA").
As long as I was working with the standard WPF controls (TextBlock, Button, Grid etc.) - I could apply the styles from the "common" dll without any problems. The designer would pick up the style and apply it correctly.
If I plop in one of the custom controls (TextButton) into the User Control in "pluginA" - the designer would find the custom control, but couldn't resolve the type for the style to be applied (Type reference cannot find the type named '{clr-namespace:Common}TextButton').
The xmlns declaration in my usercontrol looks like this:
<UserControl x:Class="PluginA.Views.LeftBarView"
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:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:core="clr-namespace:Common.Core;assembly=Common"
xmlns:common="clr-namespace:Common;assembly=Common"
mc:Ignorable="d"
d:DesignHeight="600" d:DesignWidth="300">
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<core:SharedResourceDictionary Source="/Common;component/Resources/DefaultTheme/DefaultTheme.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
With this definition, the designer doesn't apply any styles - but it works in runtime. Great, but not quite that helpful as I don't want to run the application to see if a minor tweak took effect.
So I tried this:
<core:SharedResourceDictionary Source="pack://application:,,,/Common;component/Resources/DefaultTheme/DefaultTheme.xaml" />
But that didn't change anything (designer still wouldn't find the resources). In the process of changing the code, I got to this:
<core:SharedResourceDictionary Source="pack:/Common;component/Resources/DefaultTheme/DefaultTheme.xaml" />
Now the designer is happy and can find the resources - runtime is happy and displays the resources, and yet I can't find any description of this being a valid PACK URI... Can anyone explain why this would work?
pack:/Common;component/Resources/DefaultTheme/DefaultTheme.xaml
This is technically a valid URI, but it is not a valid pack URI. Parsing it according to the rules of the pack format would yield:
Package URI: <empty>
Part URI: /Common;component/Resources/DefaultTheme/DefaultTheme.xaml
In effect, you have made an absolute URI out of a part URI by appending the pack: scheme. However, without a well-formed package component, the result is not a valid pack URI. And, interestingly, the Uri class will not actually parse the original string as an absolute URI; it is parsed incorrectly as a relative URI, and that is part of the reason it works when assigned to ResourceDictionary.Source. Let's take a look at the property setter:
public Uri Source
{
get { return _source; }
set
{
// ...
_source = value;
Clear();
Uri uri = BindUriHelper.GetResolvedUri(_baseUri, _source);
WebRequest request = WpfWebRequestHelper.CreateRequest(uri);
// ...
}
The key lies within BindUriHelper.GetResolvedUri(_baseUri, _source). The logic there, which differs from much of the pack URI handling in WPF, sees that _source is not an absolute URI (at least according to the broken Uri class), so it attempts to combine it with the resolved base URI, which we presume to be pack://application:,,,/. The URIs are combined via new Uri(Uri baseUri, Uri relativeUri), which works only because Uri incorrectly parsed the original string as a relative URI. The URI which ultimately gets used to create the WebRequest is equivalent to:
new Uri(
new Uri("pack://application:,,,/"),
new Uri("pack:/Common;component/Resources/DefaultTheme/DefaultTheme.xaml"))
...which produces:
pack://application:,,,/Common;component/Resources/DefaultTheme/DefaultTheme.xaml
And viola, we end up loading the resources from a valid pack URI even though we gave it an invalid one.
We know that the "bad" URI works because it gets accidentally transformed into a good one. As to why that same "good" URI not work in the designer when it's used directly, that is very curious.
Perhaps you simply need to rebuild both the Common project and the project that is attempting to merge the resource dictionary. If it still fails, then it's possible your UserControl.Resources has a different BaseUri when running in the designer than it does at runtime. Let's see if we can figure out what the BaseUri is at design time. Modify your UserControl as follows:
<UserControl xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
...
xmlns:m="clr-namespace:System.Windows.Markup;assembly=System.Xaml"
x:Name="Root">
<UserControl.Resources>
<ResourceDictionary">
<Style x:Key="BaseUriTextStyle" TargetType="TextBlock">
<Setter Property="Text"
Value="{Binding ElementName=Root,
Path=Resources.(m:IUriContext.BaseUri)}" />
</Style>
</ResourceDictionary>
</UserControl.Resources>
<TextBlock Style="{StaticResource BaseUriTextStyle}" />
</UserControl>
See what gets displayed in the designer. It may give us a clue.
I have a very simple WPF project set up as such:
MyFoo.xaml looks like:
<UserControl xmlns:bar="clr-namespace:WpfApplication1.UserControls" ...>
<Grid>
<bar:MyUserControl />
</Grid>
</UserControl>
Styles.xaml looks like:
<Style TargetType="TextBlock">
<Setter Property="Foreground"
Value="Red" />
</Style>
MyUserControl.xaml looks like:
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/Styles/Styles.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<Grid>
<TextBlock Text="Hello world!" />
</Grid>
My control won't render in design time, however it works fine at run time:
As shown in the screenshot, I get the error:
Cannot locate resource 'styles/styles.xaml'.
I've tried setting the build action of Styles.xaml to "Resource" and cleaning/building and it did not work.
Why is this happening? How do I fix it?
I was able to duplicate the problem.
The reasoning is behind:
xmlns:bar="clr-namespace:WpfApplication1.UserControls"
You're specifying the namespace specifically to UserControls. Since you're trying to merge the dictionary in MyUserControl which specifies the source using a location (Source) it can find the resource; however Foo.MyFoo doesn't have knowledge of where to look it seems. Whatever is happening when the designer tries to instantiate MyUserControl in MyFoo, it can't resolve the location of the Styles.xaml resource.
To fix it,
drag your Styles folder + Styles.xaml into UserControls folder.
In MyUserControl.xaml fix your source path to <ResourceDictionary Source="Styles/Styles.xaml" />
You can now see your controls during design time.
Edit
I found a way to keep the project as is and get the same results.
Set your Styles.xaml Build Action to Resource.
Change the Source to /WpfApplication1;component/Styles/Styles.xaml
Rebuild
As far as the difference between Build Actions, here is a good post.
In VS2012 I get a StackTrace of the exceptions, and the lowest level seems like it may be Baml related (where Baml is created when Build Action is set to Page). Here is the inner-most exception:
IOException: Cannot locate resource 'styles/styles.xaml'. at
MS.Internal.AppModel.ResourcePart.GetStreamCore(FileMode mode,
FileAccess access) at
System.IO.Packaging.PackagePart.GetStream(FileMode mode, FileAccess
access) at
System.IO.Packaging.PackWebResponse.CachedResponse.GetResponseStream()
at System.IO.Packaging.PackWebResponse.GetResponseStream() at
System.IO.Packaging.PackWebResponse.get_ContentType() at
MS.Internal.WpfWebRequestHelper.GetContentType(WebResponse response)
at MS.Internal.WpfWebRequestHelper.GetResponseStream(WebRequest
request, ContentType& contentType) at
System.Windows.ResourceDictionary.set_Source(Uri value) at
System.Windows.Baml2006.WpfSharedBamlSchemaContext. <Create_BamlProperty_ResourceDictionary_Source>b__1c4(Object
target, Object value) at
System.Windows.Baml2006.WpfKnownMemberInvoker.SetValue(Object
instance, Object value) at
MS.Internal.Xaml.Runtime.ClrObjectRuntime.SetValue(XamlMember member,
Object obj, Object value) at
MS.Internal.Xaml.Runtime.ClrObjectRuntime.SetValue(Object inst,
XamlMember property, Object value)
This is quite old, but I had a similar problem but the no type of pack URI solved my problem. UserControl worked in run time and in designer when editing it, but it couldn't be initialized in another window, but only in design mode.
I was able to fix the problem by making all the file names / path lowercase. I don't understand how this can fix the problem in a windows environment, but it did. And even source string does not have to be case sensitive, only the file and the filepath has to be all lowercase.
The accepted answer is valid but in my odd case, it didn't fix the problem. Maybe my experience helps someone else.
I'm having a problem accessing a Panel control defined on the XAML of a page, the XAML is defined this way:
<UserControl
x:Class="PhoneBook.SilverlightMainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
mc:Ignorable="d" Width="400" Height="300" d:DesignWidth="993" d:DesignHeight="887">
<Grid x:Name="LayoutRoot" />
</UserControl>
The class is defined like this:
public partial class SilverlightMainPage : UserControl
{
public SilverlightMainPage()
{
InitializeComponent();
}
}
And I'm trying to instantiate it this way:
var silverlightMainPage = new PhoneBook.SilverlightMainPage();
SomeMethod((silverlightMainPage.LayoutRoot);
What I find strange is that when I put the dot after the object instance, it actually list LayoutRoot as one of the members, but when I try to compile the application it says that there's no member with that name.
Any ideas of what can be hapenning?
Thanks
EDIT: I also tried creating a property on the SilverlightMainPage class that returned the LayoutRoot element, but it also says that the class doesn't contain a definition for Layout root.
Is there any chance that you're trying to access SilverlightMainPage.LayoutRoot from a different assembly? In the MainPage.g.i.cs file, LayoutRoot (and all other controls defined in XAML) are marked "internal", i.e.:
internal System.Windows.Controls.Grid LayoutRoot;
You might want to try creating a public rather than an internal property that does a FindName("LayoutRoot") and returns the appropriate control.
Actually I found the problem.
I was generating the project automatically with a tool built by someone else in the company.
I did some extra tests and added a new UserControl to the project and tried to access the LayoutRoot from a property in the code behind, and it worked.
Then copied the exact same code to the file with the problem (just changing the class name) and it didn't compile.
Then I checked the project file, and found a section like this:
<ItemGroup>
<Compile Include="SilverlightMainPage.xaml.cs">
<DependentUpon>SilverlightMainPage.xaml</DependentUpon>
</Compile>
</ItemGroup>
Which for some reason was causing the compilation to fail.
I removed that section and now everything works fine.
Thanks for your answers though.
I have a simple Window with a reference to a StaticResource in the App.xaml.
App.xaml resource definition:
<!-- Standard Text Box Style -->
<Style x:Key="textBoxStyleStd" TargetType="{x:Type TextBox}">
<Setter Property="FontSize" Value="14" />
</Style>
Window componets using the resource:
<TextBlock Grid.Column="1" Grid.Row="0" Name="stationIdTitle"
Style="{StaticResource textBlockStyleStd}"
VerticalAlignment="Center" HorizontalAlignment="Center"
Text="{LocText Key=Title, Dict={StaticResource Dictionary},
Assembly={StaticResource Assembly}}"/>
When trying to unit test this Window I get the error:
System.Windows.Markup.XamlParseException: Cannot find resource named
'{textBlockStyleStd}'. Resource names are case sensitive. Error at
object 'stationIdTitle' in markup file
'Zpg;component/guicomponenets/screens/enterstationidscreen.xaml' Line
23 Position 71.
Is there any way around this? My unit test code is:
[Test]
public void TestEnterKeyPressedNoText()
{
IPickingBusinessObject pickingBusinessObject = mock.StrictMock<IPickingBusinessObject>();
EnterStationIdScreen objectUnderTest = new EnterStationIdScreen(pickingBusinessObject);
Assert.AreEqual(Visibility.Visible, objectUnderTest.stationIdError.Visibility);
Assert.AreEqual("werwe", "oksdf");
Replay();
objectUnderTest.EnterKeyPressed();
Verify();
}
Thanks Kent,
I looked at your suggestions and in most scenarios I agree models should be used and tested however, there is some code associated with the controls (e.g. TextBox visibility) I still wanted to test. To get round this you can create an instance of your Application (but not initialize it) and add the resources manually. This does lead to duplication in the App.xaml and the base unit test but this allows me to complete the tests I wanted.
if (Application.Current == null)
{
App application = new App();
#region Add Static Resources from the App.xaml
Style textBoxStyle = new Style(typeof(TextBox));
textBoxStyle.Setters.Add(new Setter(TextBox.FontSizeProperty, 14d));
Style textBlockStyle = new Style(typeof(TextBlock));
textBlockStyle.Setters.Add(new Setter(TextBlock.FontSizeProperty, 14d));
application.Resources.Add("TextBoxStyleStd", textBoxStyle);
application.Resources.Add("TextBlockStyleStd", textBlockStyle);
application.Resources.Add("TextBlockStyleError", textBlockStyle);
application.Resources.Add("Assembly", "Zpg");
#endregion
}
In the context of your unit test, there is no WPF application running. Therefore, the Window won't find the resource.
My way around this would be to not unit test your views. Instead, use MVVM and unit test your view models. If you want to test your views, write integration tests instead. Your integration tests can actually kick off the application and therefore much more closely imitate the real running of your app.
When I use fully qualified assembly names in my app.xaml resource entries, I only need to instanciate the App() class. In this example, all resources lies in the Majesty_of_Omega_GUI assembly, which is referred by the UnitTest.DLL
<Application
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="Majesty_of_Omega.GUI.App"
StartupUri="Pages/MainPage.xaml"
>
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/Majesty_of_Omega_GUI;component/Resources/MainScreens.xaml" />
<ResourceDictionary Source="pack://application:,,,/Majesty_of_Omega_GUI;component/Resources/PanelResources.xaml" />
<ResourceDictionary Source="pack://application:,,,/Majesty_of_Omega_GUI;component/Simple Styles.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>
Test function:
[Test]
public void Testfunction()
{
if (Application.Current == null)
{
App application = new App();
}
SomePage page = new SomePage();
Actually, you can use the same Application and if the resources are from the same assembly, you've got to call the InitializeComponents methods to make it works (Source here).
Have a nice day !