How to use resource dictionary in prism modules at design time? - c#

I am using prism framework in a silverlight app with multiple modules in separate XAPs.
I have a resource dictionary defined in my in my shell project. In my modules I can use the resources fine, but since the modules are decoupled from the shell until they are loaded at runtime the designer does not show them or recognize them.
Is there a way to make the modules aware of my resources at design time without merging my resource file in every view xaml?
My resource files are in a "common" project.

I think I have definitely solution for design-time resources.
Benefits:
It works in any module based (MEF, UNITY..) application.
It works in any designer (Visual Studio, Blend..)
It does not create multiple instances of the same ResourceDictionary
Let's consider following solution:
MyApp.Shell (.exe)
MyApp.Module1 (.dll) - loaded at runtime using MEF
MyApp.Module2 (.dll) - loaded at runtime using MEF
MyApp.Common (.dll) - referenced by all projects
you can define brushes, implicit styles, templates etc in MyApp.Common.
use my SharedResourceDictionary to include the ResourceDictionary in all projects. At design-time it will load the ResourceDictionary for each designer, at runtime the ResourceDictionary will be loaded only when necessary.
Usage example:
include SharedResourceDictionary in App.xaml
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<common:SharedResourceDictionary SharedSource="MyApp.Common;component/CommonResources.xaml" />
</ResourceDictionary>
</Application.Resources>
include SharedResourceDictionary everywhere designer fails to find
some share resource, e.g. in MyApp.Module1/UserControl1.xaml
<UserControl.Resources>
<common:SharedResourceDictionary SharedSource="MyApp.Common;component/CommonResources.xaml" />
</UserControl.Resources>
Source:
/// <summary>
/// Loads singleton instance of ResourceDictionary to current scope;
/// </summary>
public class SharedResourceDictionary : ResourceDictionary
{
/// <summary>
/// store weak references to loaded ResourceDictionary, to ensure that ResourceDictionary won't be instanciated multiple times
/// </summary>
protected static Dictionary<string, WeakReference> SharedResources = new Dictionary<string, WeakReference>();
public string SharedSource
{
get { return _SharedSource; }
set
{
if (_SharedSource != value)
{
_SharedSource = value;
sharedSourceChanged();
}
}
}
private string _SharedSource;
private void sharedSourceChanged()
{
//ResourceDictionary will be instanciated only once
ResourceDictionary sharedResourceDictionary;
lock (SharedResources)
{
WeakReference weakResourceDictionary = null;
if (SharedResources.ContainsKey(_SharedSource))
{
weakResourceDictionary = SharedResources[_SharedSource];
}
else
{
SharedResources.Add(_SharedSource, null);
}
if (weakResourceDictionary == null || !weakResourceDictionary.IsAlive) //load ResourceDictionary or get reference to exiting
{
sharedResourceDictionary = (ResourceDictionary)Application.LoadComponent(new Uri(_SharedSource, UriKind.Relative));
weakResourceDictionary = new WeakReference(sharedResourceDictionary);
}
else
{
sharedResourceDictionary = (ResourceDictionary)weakResourceDictionary.Target;
}
SharedResources[_SharedSource] = weakResourceDictionary;
}
if (Application.Current != null)
{
//if sharedResourceDictionary is defined in application scope do not add it to again to current scope
if (containsResourceDictionary(Application.Current.Resources, sharedResourceDictionary))
{
return;
}
}
this.MergedDictionaries.Add(sharedResourceDictionary);
}
private bool containsResourceDictionary(ResourceDictionary scope, ResourceDictionary rs)
{
foreach (var subScope in scope.MergedDictionaries)
{
if (subScope == rs) return true;
if (containsResourceDictionary(subScope, rs)) return true;
}
return false;
}
}

I have found there are a couple of solutions to this:
1) When you create a module project, leave the App.xaml in the project instead of deleting it and instantiate your resources in there just as if it were its own application by itself (you can also add a new Application class to the project if you have already deleted it). When your module is loaded into the shell that file will be ignored so it's essentially only valid during design time. This works well in visual studio and blend although if you have many modules, memory footprint may become a problem.
2) Using design time resources. Some info about setting this up here: http://adamkinney.com/blog/2010/05/04/design-time-resources-in-expression-blend-4-rc/. This offers only blend support and your views will be stripped of all styles and formatting in visual studio. This was not ideal for me because I like working on certain aspects of the UI in visual studio. There also doesn't seem to be a documented way of manually setting up design time resources.

Small own-experience guide for migrating resources from Shell to shared essembly and making designer work just fine
Some thoughts based on reading such questions and searching internet on the same/similar problem. I'm writing this primarily because of problem 2 (below), which is related to this issue, IMHO.
So, we had the same design, all styles and resources were in Shell. This produced 2 problems:
Context help in XAML-Editor was not available (<- resources not
found)
Designer wouldn't show up properly (<- resources not
found)
So we migrated all styles to shared assembly (Resources).
To solve the first problem you would need sth like Liero proposed, i.e. add resource dictionary to each UserControl. I didn't try his SharedDictionary, but normal ResourceDictionary definitely brings context help back and removes blue-underscore lines. Designer however still didn't show up properly.
So the second problem. There is a small trick to bring styles to designer at design time only described in this article. Basically you add a resource dictionary named DesignTimeResources.xaml to your project that contains reference to your resources:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/Resources;component/Themes/Generic.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
Than move it to Properties folder. Than edit manually project file and change the item for this file to this:
<Page Include="Properties\DesignTimeResources.xaml" Condition="'$(DesignTime)'=='true' OR ('$(SolutionPath)'!='' AND Exists('$(SolutionPath)') AND '$(BuildingInsideVisualStudio)'!='true' AND '$(BuildingInsideExpressionBlend)'!='true')">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
<ContainsDesignTimeResources>true</ContainsDesignTimeResources>
</Page>
Basically it's a file that Blend would generate if you add design time resources. VS cannot create it, although can read it just fine. The editing of project file says that you don't want basically this file in release.
Two minor gotchas here also, perhaps it will help somebody.
When migrating resources from Shell to Resources, our Resources project won't build with weird errors that it cannot find UserControls referenced from style files (all problematic controls were defined in the Resources project as well). They were working just fine when referenced from Shell before. The problem was that some tools (like Resharper) automatically reference these controls in namespace like "clr-namespace:XXX;assembly=Resources". The ";assembly=Resources"-part you should delete, as it is the same assembly now.
We already head some local resources in our UserControls, like this:
<UserControl.Resources>
<PresentationHelpers:BoolToVisibilityConverter x:Key="boolToVisibilityConverter" />
</UserControl.Resources>
So at first I just added new ResourceDictionary into this block, which asked me to provide an x:Key. I was so used to add resources directly to UserControl.Resources, that I didn't first realise that in order to merge another dictionary you would need <ResourceDictionary> tag that normally you could skip. So it will look like this:
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<Helpers:RedbexResourceDictionary Source="pack://application:,,,/Resources;component/Themes/Generic.xaml" />
</ResourceDictionary.MergedDictionaries>
<PresentationHelpers:BoolToVisibilityConverter x:Key="boolToVisibilityConverter" />
</ResourceDictionary>
</UserControl.Resources>

If you're looking to provide design time data for your views may I suggest reading this article. It shows how to use Blend to create design time data within your project which is not included in the release builds of the application.
Hope it helps.

Related

Opening new window in a MVVM WPF Class Libary

Basically I am looking for this Answer, but for Class Libraries.
I am currently maintaining a larger WPF Solution where each part is loaded as a Add-In.
I'd like to create a service for all of these Add-Ins to implement which opens a Window without using UI elements in the ViewModel, basically looking like this:
class WindowService:IWindowService
{
public void ShowWindow(object viewModel)
{
var win = new Window();
win.Content = viewModel;
win.Show();
}
}
To simplify it, let's say we have 4 Projects:
1. Client
Client.exe - MainWindow and App.xaml is in here
Services.dll - Services like WindowService and Reflection are in here
Interfaces.dll - Contains Interfaces for above assemblies
2. Add-Ins
AddIn.dll - WPF Class Library, Contains WPF View/ViewModel, References to Interfaces
The Answer I linked defines DataTemplates in the App.Xaml, but I can't define DataTemplates for Classes I don't know at that point, since Add-Ins are loaded using Reflection at Runtime.
Where would I put my DataTemplates in order to get the linked Answer to work for Class Libraries without an App.Xaml?
What I do is have a xaml resource file in each of my class libraries, and programatically merge these into the main application resources.
Say the class library project assembly name is "MyProject.Client" and it contains the resource file \Views\Resources\LibraryStyles.xaml. The code to merge this would look like:
Application.Current.Resources.MergedDictionaries.Add(
new ResourceDictionary
{
Source = new Uri("pack://application:,,,/MyProject.Client;component/Views/Resources/LibraryStyles.xaml",
UriKind.RelativeOrAbsolute)
});

WPF ToastNotifications from wpf class library does not load Resources

I am developing a plugin and I want to use this notification toast library from nuget: Toast Notifications
One of the steps I have to do to setup the Toast Notifications library is this:
2 Import ToastNotifications.Messages theme in App.xaml
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/ToastNotifications.Messages;component/Themes/Default.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
However, I don't have an Application file since I am developing a class library. I have added the resourcer Dictionary to the UserControl that is creating the notifications. Like this:
<UserControl.Resources>
<ResourceDictionary Source="pack://application:,,,/ToastNotifications.Messages;component/Themes/Default.xaml" />
</UserControl.Resources>
However, when I run the App I've got this exception:
{System.Windows.Markup.XamlParseException: Provide value on 'System.Windows.StaticResourceExtension' threw an exception. ---> System.Exception: Cannot find resource named 'InformationIcon'. Resource names are case sensitive.
But InformationIcon is an image inside the ToastNotifications library. What am I doing wrong?
Your problem is the main application that uses your plugin does not have a reference to the nuget package. So, you have two methods to solve this:
If you have access to the main application's project add the nuget lib to it.
If you do not. You have to load the assembly dynamically at runtime. Here is a good solution How to reference a DLL on runtime?
It searches through dlls in a given directory and finds classes that implement a particular interface. Below is the class I used to do this:
public class PlugInFactory<T>
{
public T CreatePlugin(string path)
{
foreach (string file in Directory.GetFiles(path, "*.dll"))
{
foreach (Type assemblyType in Assembly.LoadFrom(file).GetTypes())
{
Type interfaceType = assemblyType.GetInterface(typeof(T).FullName);
if (interfaceType != null)
{
return (T)Activator.CreateInstance(assemblyType);
}
}
}
return default(T);
}
}
All you have to do is initialize this class with something like this:
PlugInFactory<InterfaceToSearchFor> loader = new PlugInFactory<InterfaceToSearchFor>();
InterfaceToSearchFor instanceOfInterface = loader.CreatePlugin(AppDomain.CurrentDomain.BaseDirectory);

Using Custom Resource in DevExpress WPF Theme

Objective:
I have a WPF project which shall be themed using DevExpress Themes.
There is a Login-UserControl that shall have a themable background image.
Implementation
I made a custom Theme. In that theme I have a Folder "CustomResources" in which there is an Image, let's call it "Background.png" and a "Brushes.xaml" that defines an ImageBrush like this:
<ResourceDictionary ...>
<ImageBrush x:Key="{CustomThemeKeyAssembly:CustomThemeResourcesThemeKey ResourceKey=LoginBackgroundImageBrush, ThemeName=CustomTheme}" ImageSource="Background.png" />
</ResourceDictionary>
Accordingly, I have a shared Assembly CustomThemeKeyAssembly that derives a Custom ResourceThemeKey.
In the Project, I register and set the Theme using ApplicationThemeHelper
var theme = new Theme("CustomTheme")
{
AssemblyName = "DevExpress.Xpf.Themes.CustomTheme.v17.2"
};
Theme.RegisterTheme(theme);
ApplicationThemeHelper.ApplicationThemeName = "CustomTheme";
and I reference the Resource through
Background="{dxci:ThemeResource ThemeKey={CustomThemeKeyAssembly:CustomThemeResourcesThemeKey ResourceKey=LoginBackgroundImageBrush}}"
As advised by DevExpress Knowledgebase / Support.
Problem
The Resource is only found and displayed, if I add a Merged Resource Dictionary like this:
ResourceDictionary loginBackgroundDictionary = new ResourceDictionary
{
Source = new Uri($"pack://application:,,,/{MyProject.Properties.Settings.Default.ThemeAssembly};Component/CustomResources/Brushes.xaml", UriKind.Absolute)
};
//Add LoginBackgroundImageBrush Dictionary
Resources.MergedDictionaries.Add(loginBackgroundDictionary);
No article or example mentions having to do this, though. So my impression is that I either am doing something wrong or I am missing some simple step like merging the Brushes.xaml into some ResourceDictionary.
Without that snippet I get a warning that the resource could not be found.
Question
Has anybody an idea where I am going wrong or what I am missing to get this working without that last snippet?
FYI: I am using DevExpress 17.2.3 and the ResourceKey Assembly is targeted to .net Framework 4.0
EDIT
Meanwhile, I tried adding the Brushes.xaml to Themes/Generic.xaml in the theme assembly like this:
<ResourceDictionary.MergedDictionaries>
<dxt:ResourceDictionaryEx Source="/DevExpress.Xpf.Themes.Office2016WhiteSE.v17.2;component/Themes/ControlStyles.xaml" />
<dxt:ResourceDictionaryEx Source="/DevExpress.Xpf.Themes.Office2016WhiteSE.v17.2;component/CustomResources/Brushes.xaml" />
</ResourceDictionary.MergedDictionaries>
It didn't make any difference. Same behavior as before.
Problem solved!
The problem was in the CustomThemeKeyAssembly
The wrong implementation was
public class CustomThemeResourcesThemeKey : ThemeKeyExtensionBase
{
public override Assembly Assembly => TypeInTargetAssembly != null ? TypeInTargetAssembly.Assembly : GetType().Assembly;
}
The working implementation is
public class CustomThemeResourcesThemeKey : ThemeKeyExtensionBase<ThemeResourcesThemeKeys> { }
The breaking difference is the override of the Assembly property. The default implementation makes it work. I did that because it was done so in an example. Support told me to stick with the default implementation and it worked.

View doesn't find ViewModel in different Assembly

I am starting a new project and oriented my projectstructure on the structure recommended in this question.
Now I am seeing strange behaviour. When I am setting the datacontext in the View-XAML, it isn't found at runtime (getting a XamlParseException). When I set it in the constructor in the codebehind-file, everything is working just fine.
Is this official (documented) behaviour when using different assemblies, or am I doing something wrong?
The code:
Not Working:
MainView.xaml:
<UserControl x:Class="ViewsRoot.Views.MainView"
xmlns:baseControls="clr-namespace:BaseControls;assembly=BaseControls"
xmlns:viewModels="clr-namespace:ViewModelsRoot;assembly=ViewModelsRoot">
<UserControl.DataContext>
<viewModels:ShellViewModel />
</UserControl.DataContext>
MainView.xaml.cs
public MainView()
{
InitializeComponent();
// No DataContext set in codebehind-file
}
Working:
MainView.xaml:
<UserControl x:Class="ViewsRoot.Views.MainView"
xmlns:baseControls="clr-namespace:BaseControls;assembly=BaseControls"
xmlns:viewModels="clr-namespace:ViewModelsRoot;assembly=ViewModelsRoot">
<!--<UserControl.DataContext>
<viewModels:ShellViewModel />
</UserControl.DataContext> -->
MainView.xaml.cs:
public MainView()
{
InitializeComponent();
DataContext = new ViewModelsRoot.ShellViewModel();
}
Update:
The Exception-Text is:
{"The file or assembly \" ViewModelsRoot, PublicKeyToken = null \ "or one of its dependencies was not found. The system can not find the file specified."}
And the only inner Exception I can see is a System.IO.FileNotFoundException.
Update 2:
Thanks for the comments, but I haven't forgotten a namespace. I shortened it here for showing the code, but I double- and triplechecked (again). The DataContexts namespace is also filled in by intellisense. The whole <viewModels:ShellViewModel /> is written by intelli-sense. So it is found at designtime... ...so any more ideas?
Update 3:
The xaml is "correctly" parsed as I am able to bind the DataContext to a class in the same assembly.
I have reproduced this error using a three project solution, with the specified dependencies between them:
StartupProject → ViewsRoot
ViewsRoot → ViewModelsRoot
ViewModelsRoot
"StartupProject" has "exe" output type, while the other two have "dll".
In my case, I solved the problem by adding "ViewModelsRoot" to the References list of "StartupProject". It is not a coding problem, but rather a runtime problem, because "ViewModelsRoot.dll" is not copied to "StartupProject" output folder.
When you specify the DataContext in code-behind, Visual Studio notices the need for that "dll" and adds it to the output after compilation. This doesn't happen when setting the DataContext from XAML. It is tricky because "ViewModelsRoot" code is used from XAML with Reflection. Adding it to References list forces Visual Studio to copy the "dll" in both cases.
You can also copy "ViewModelsRoot.dll" to the output folder directly, but it will not be updated when you change the code.
I've often found this error when the project target framework was set to "Client Profile" (this was set by default on VS2010, IIRC), if this is the case, try changing it to 3.5 or 4.0.

The component does not have a resource identified by the uri

I want to create a Generic DataGrid to use on all my Views/UserControls.
This is my structure:
Class Library called "Core":
Class called "ViewBase":
public class ViewBase : UserControl
{
public ViewBase()
{
}
//Rest of Methods and Properties
}
Class Library called "Controls":
UserControl Called "GridView":
XAML:
<vb:ViewBase x:Class="Controls.GridView"
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:vb="clr-namespace:Core;assembly=Core">
<Grid>
<DataGrid></DataGrid>
</Grid>
</vb:ViewBase>
Code Behind:
using Core;
public partial class GridView : ViewBase
{
public GridView ()
{
InitializeComponent();
}
}
Then is the WPF Aplication called "WPFApp":
Class called "View":
using Controls;
public class View : GridView
{
public View()
{
InitializeComponent();
}
}
My whole idea is to use GridView where i need a DataGrid.
When i run the application i get this error:
"The component 'WpfApp.View' does not have a resource identified by the URI '/Controls;component/GridView.xaml'."
What am i doing wrong?
Is this the correct approach or am i way off?
Frustratingly, I had exactly this error and spent forever trying to work out the cause. For me, it was once working but then I made some very minor changes to the XAML of the derived control, and the compiler started giving that error message.
Short solution, cutting out many hours of trying to figure it out: shut down Visual Studio and re-opened it, recompiled, problem magically went away! (This is VS2012 Pro)
Just added this in case anyone reading is going round in circles trying to find a non-existent problem with their code. Might be worth trying the "IT Crowd solution" first.
This gave me headaches for 3 days! I have a XAML UserControl in a class library and a class (only C#) that derives from the UserControl in my .exe project.
In xaml designer of my MainWindow.xaml and when starting the application, I got the error "component does not have a resource identified by the uri".
The answer of "Juan Carlos Girón" finally lead me to the solution:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Navigation;
using System.Reflection;
using System.IO.Packaging;
using System.Windows.Markup;
namespace ClassLibrary1
{
static class Extension
{
public static void LoadViewFromUri(this UserControl userControl, string baseUri)
{
try
{
var resourceLocater = new Uri(baseUri, UriKind.Relative);
var exprCa = (PackagePart)typeof(Application).GetMethod("GetResourceOrContentPart", BindingFlags.NonPublic | BindingFlags.Static).Invoke(null, new object[] { resourceLocater });
var stream = exprCa.GetStream();
var uri = new Uri((Uri)typeof(BaseUriHelper).GetProperty("PackAppBaseUri", BindingFlags.Static | BindingFlags.NonPublic).GetValue(null, null), resourceLocater);
var parserContext = new ParserContext
{
BaseUri = uri
};
typeof(XamlReader).GetMethod("LoadBaml", BindingFlags.NonPublic | BindingFlags.Static).Invoke(null, new object[] { stream, parserContext, userControl, true });
}
catch (Exception)
{
//log
}
}
}
}
and called that from by UserControl's .cs file:
namespace ClassLibrary1
{
public partial class MyUserControl : UserControl
{
public MyUserControl()
{
//InitializeComponent();
this.LoadViewFromUri("/ClassLibrary1;component/myusercontrol.xaml");
}
}
}
Thanks again to "Juan Carlos Girón"!
The reason you are getting this error is because the way InitializeComponent that is implemented (in VS 2010) will always search in the derived class's assembly.
Here is InitializeComponent:
/// <summary>
/// InitializeComponent
/// </summary>
[System.Diagnostics.DebuggerNonUserCodeAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("PresentationBuildTasks", "4.0.0.0")]
public void InitializeComponent() {
if (_contentLoaded) {
return;
}
_contentLoaded = true;
System.Uri resourceLocater = new System.Uri("/WpfApplication1;component/mainwindow.xaml", System.UriKind.Relative);
#line 1 "..\..\..\MainWindow.xaml"
System.Windows.Application.LoadComponent(this, resourceLocater);
#line default
#line hidden
}
The line where it looks up your XAML resource is System.Windows.Application.LoadComponent(this, resourceLocator). And this most probably fails because equivalent of 'this.GetType().Assembly' is used to determine which assembly to search for the resource identified by the relative Uri. And 'this.GetType()' does get the derived type of the object, not the type of the class where the code is implemented.
PS. Is this a bug? I do not know...
You can try this approach
I created my own InitializeComponent() and I called this way
this.LoadViewFromUri("/NameOfProject;component/mainwindow.xaml");
public static void LoadViewFromUri(this Window window, string baseUri)
{
try
{
var resourceLocater = new Uri(baseUri, UriKind.Relative);
var exprCa = (PackagePart)typeof(Application).GetMethod("GetResourceOrContentPart", BindingFlags.NonPublic | BindingFlags.Static).Invoke(null, new object[] { resourceLocater });
var stream = exprCa.GetStream();
var uri = new Uri((Uri)typeof(BaseUriHelper).GetProperty("PackAppBaseUri", BindingFlags.Static | BindingFlags.NonPublic).GetValue(null, null), resourceLocater);
var parserContext = new ParserContext
{
BaseUri = uri
};
typeof(XamlReader).GetMethod("LoadBaml", BindingFlags.NonPublic | BindingFlags.Static).Invoke(null, new object[] { stream, parserContext, window, true });
}
catch (Exception)
{
//log
}
}
I was doing something very similar with the same result. I had one C# class library that contained a WPF control called UsageControl (xaml with accompanying xaml.cs file). In a separate C# project(i.e. separate dll) I created a C# class CPUUsageControl which inherited from UsageControl, but put its own spin on it. When I tried to use the CpuUsageControl on one of my views I got the same error you did.
What I did to fix that was in my seperate assembly, instead of creating a class that inherited from the base control, i created a new WPF Control that contained the base control. I then put all of the logic that was contained in the CpuUsage class into the WpfCpuUsageControl's code behind. I was able to use this object is all of my other controls just fine.
For your Control "GridView" i would create a new WPF user control, call it GridView and make it contain a "ViewBase" as the content of the Grid control.Inside of the ViewBase's content put in your DataGrid, like this:
<UserControl....>
<Grid>
<ViewBase name="vBase">
<DataGrid name="dGrid" />
</ViewBase>
</Grid>
</UserControl>
It is also not apparent to me that you need ViewBase to inherit from UserControl directly. If all you want are for your controls to have certain properties and method why not just make a BaseControl class (that does not inherit from anyone but object) and have future controls inherit from it. Perhaps an abstract base class or interface is what you're after.
For MVVM WPF projects, I typically have a BaseViewModel which implements INotifyPropertyChanged for me so I don't have to do that same code everywhere.
Best of luck, I know this problem was a huge pain to figure out. The exception message and google are most unhelpful!
Same problem here.
Short version:
Copy Local has to be set to False!
Long version:
We developed a WPF solution (MVVM, 20 projects) and implemented a plug-in system. Our /bin/Debug directory contains the executable, some dll files and a plugin directory that contains the plugins.
There is one project "DialogLib" (Class library, kind of dialog) that defines a window (the view), the ViewModel, Model and some interfaces. One of the plugins used one of the interfaces of DialogLib. The window itself is opened by the main application.
To use the interface of the 'DialogLib' library in the plugin we had to add a project reference of DialogLib to the plugins project references. When the application was started, the plugins were loaded. If the user then selects a menu item, the window should open. At this point the error "... component does not have a resource identified by the URI ..." occured when the windows code behind tried to execute its InitializeComponent().
Where's the problem?
The problem is, that, when we built the solution VS has created the DialogLib.dll correctly and copied it to /bin/Debug/. This is because the main application file wants to open the window. But DialogLib.dll was also copied to /bin/Debug/plugins because one of the plugins referenced it to use one of the interfaces defined in DialogLib.dll. So what?
When the plugin is loaded at runtime it uses the interface defined in /bin/Debug/plugins/DialogLib.dll. and the main application file tries to open the window defined in /bin/Debug/DialogLib.dll. Although the files are identical, VS runs into trouble. Setting the value of Copy Local of the DialogLib reference properties of the plugins references avoids copying DialogLib.dll to /bin/Debug/plugins and thus solves the problem.
We had a similar same problem (but different error) in another project where we wanted to use a type TypeA, that was defined in a dll file, in a plugin and in the main application. Copy Local was set to true which caused a copy of the dll file to be located in ../bin/Debug/plugins and in ../bin/Debug/. It turned out that, even though it was the same dll file, the TypeA in the main app file and TypeA in the plugin were treated as different types respectively as types which could not be exchanged.
Delete obj folder
Delete bin folder
Rebuild solution
Worked for me!
Also if you are loading assemblies using Assembly.LoadFile, check out AppDomain.CurrentDomain.GetAssemblies() for duplicate assemblies in the current AppDomain. Because in auto-generated code of WPF UserControl, the component will be loaded using its relative URI. And since there are duplicate assemblies in the current AppDomain, application doesn't know which one to use.
I resolved this by placing
myusercontrol = Activator.CreateInstance<myusercontrol>();
in the constructor of the window containing the usercontrol before the InitializeComponent(); line
I received the same error when using Visual Studio 2013.
The component does not have a resource identified by the uri
Tried:
Cleaning and rebuilding the solution - did not work.
Closing and opening Visual Studio - did not work.
Solution:
Went into the projects bin directory and cleared out all files.
Ran the project again and worked fine.
Open the Package Manager Console which will open in the root directory of your Solution and run the following powershell command:
Get-ChildItem -inc bin,obj -recurse | Remove-Item -recurse -force -EA SilentlyContinue
#Willem, this seems perfectly OK to me. In fact I tried this and it worked in my case. I used ListBox instead of DataGrid (but that shouldnt matter).
All my namespaces were in one assembly. So I used a common parent namespace for all e.g.
MyWpfApplication.Controls
MyWpfApplciation.GridView
MyWpfApplciation.ViewBase
Coz all these Controls, GridView, ViewBase are clashing with existing System or System.Windows.Controls based namespace and class declarations. So I made sure I referred correct ones MyWpfApplication.* in my project.
I just ran into this problem as well without any inheritance issues. I was just referencing a DLL that contained a dialog and trying to create and display that dialog.
I have assembly resolver that loads assemblies from a specific folder and it turns out that I had added the reference in VS and had not turned off Copy Local. Long story short: my process had loaded two versions of that same DLL. This seems to confuse WPF (or the runtime). Once I cleared the Copy Local and deleted the extra DLL copies, it worked fine again.
I got this error after renaming a xaml file. Reversing the renaming solved the problem.
Furthermore, I found that a reference to the xaml file name in App.xaml was not updated (the StartupUri), but renaming that to the current name didn't resolve the problem (but maybe it does for you). Basically, I can't rename the xaml file.
Fyi, for me, the component 'complaining' in the error was SplitComboBox.
Happend to me when I had the same project opened in two solutions. Modifying the base-control in one project cause the other project to have this problem. If closing and opening doesn't work, then delete all the folders in "C:\Users...\AppData\Local\Microsoft\VisualStudio\12.0\Designer\ShadowCache"
This can happen also when closing and reopening a window. So it could also have nothing to do with packages and/or dlls.
I solved the problem thanks to the solution posted by PainElemental, which is IMHO underrated:
namespace MyNamespace
{
public partial class MyDialog : Window
{
public MyDialog(ExcelReference sheetReference)
{
this.LoadViewFromUri("/MyApp;component/mynamespace/mydialog.xaml");
}
}
}
LoadViewFromUri is implemented as an extension, as PainElemental wrote.
The craziest is that I also wrote in the same project other windows without encountering any problem.
Thank you PainElemental, you ended my protracted pain!
I started consistently seeing a "the component does not have a resource identified by the uri" error when I clicked a particular menu choice from an installed product that was working on other computers. I tried uninstalling the product, making sure its files really were gone, rebooting, and reinstalling the product. The problem remained. I deleted the contents of my %TEMP% directory, and the problem ceased.
Thanks for all the tips in this thread. I think my own variation of this error was for a slightly different reason again, so I'll post here in case it's of use.
In my situation, the error occurred when invoking window.ShowDialog(). More specifically, my window is defined in a separate class library assembly (let's call it AssemblyA.dll).
I have multiple versions of AssemblyA which are used in various products, some of which are plugins and some aren't. In short, the consequence is that the process might end up loading several different strong-named versions of AssemblyA. So there are duplicate assemblies in the app domain as #VahidN pointed out, but they're strictly differently versioned assemblies which are meant to be there, and merely share the same AssemblyShortName.
WPF's auto-generated code for InitializeComponent() looks like this:
public void InitializeComponent() {
if (_contentLoaded) {
return;
}
_contentLoaded = true;
System.Uri resourceLocater = new System.Uri("/AssemblyA;component/forms/mywindow.xaml", System.UriKind.Relative);
#line 1 "..\..\..\Forms\MyWindow.xaml"
System.Windows.Application.LoadComponent(this, resourceLocater);
#line default
#line hidden
}
It's only referring to the short name of AssemblyA, and not to the specific version or public key token of AssemblyA in which the InitializeComponent() method is running. The consequence is that the code just seems to find the first AssemblyA assembly loaded into the process, searches for the XAML, can't find it (because it's found an older version of the assembly first), and then throws an exception. Or perhaps it finds something but maybe it's pulled a different XAML resource than what it's meant to have, from either an older or newer version of the assembly that happens to also be loaded.
It's not perfect, but I've consulted the Pack URI specification, and worked around this by writing my own extension method that makes sure the assembly is found with the appropriate version and public key token, rather than simply the AssemblyShortName.
In case it's of use for others, here's a simplified version of what I've ended up with.
public static void AssemblySensitive_InitializeComponent(this ContentControl contentControl, string componentString)
{
// Strictly speaking this check from the generated code should also be
// implemented, but it doesn't fit directly into an extension method.
//if (_contentLoaded)
//{
// return;
//}
//_contentLoaded = true;
var asm = System.Reflection.Assembly.GetExecutingAssembly();
var shortName = asm.GetName().Name;
var publicKeyToken = GetPublicKeyTokenFromAssembly(asm);
var version = asm.GetName().Version.ToString();
System.Uri resourceLocater = new System.Uri($"/{shortName};V{version};{publicKeyToken};{componentString}", System.UriKind.Relative);
System.Windows.Application.LoadComponent(contentControl, resourceLocater);
}
/// <summary>
/// Gets a public key token from a provided assembly, and returns it as a string.
/// </summary>
/// <param name="assembly"></param>
/// <returns></returns>
/// <remarks>Adapted from https://stackoverflow.com/questions/3045033/getting-the-publickeytoken-of-net-assemblies</remarks>
private static string GetPublicKeyTokenFromAssembly(System.Reflection.Assembly assembly)
{
var bytes = assembly.GetName().GetPublicKeyToken();
if (bytes == null || bytes.Length == 0)
return "None";
var publicKeyToken = string.Empty;
for (int i = 0; i < bytes.GetLength(0); i++)
publicKeyToken += string.Format("{0:x2}", bytes[i]);
return publicKeyToken;
}
The _contentLoaded bit could probably be done with extension properties, but I need the code for this library to compile in C# 7.3 so I have a much longer workaround which I removed so as not to distract.
Then I call it from the constructor like this:
public MyWindow()
{
// Don't use the auto-generated initialize, because if multiple different versions
// are loaded into the process, it can try to load the resource from the wrong one.
//InitializeComponent();
AssemblySensitive_InitializeComponent("component/forms/mywindow.xaml");
// ... do more constructor stuff ...
}
I spent ages getting frustrated trying to figure out what was going on, so I hope this helps someone else out there.
As others have pointed out in their answers, this will happen if you have a base control class with an associated XAML resource, and then define a class in a separate assembly that inherits from the base control. This happens because of a limitation in WPF.
WPF is open source now, so you can see the source code that we need to work around that is called in IntializeComponent() (though it's a bit difficult to follow). In summary, this method get a stream for the control's XAML resource and then loads it with XamlReader.LoadBaml(). The issue is that the framework code does not load the XAML resource file correctly when the derived class is in a different assembly than the XAML resource file.
To work around this issue we need to load the XAML resource stream correctly and then call XamlReader.LoadBaml() manually. There are a few other answers here already that do exactly this, but here's my take on it. The following extension method is a bit more concise than the other answers, accesses only one private method via reflection, and also guards against multiple calls.
private static MethodInfo? _loadBamlMethod;
public static void InitializeComponent(this ContentControl control, string xamlResourceUri, ref bool contentLoaded)
{
// Ensure the control is only initialized once
if (contentLoaded) return;
contentLoaded = true;
// Use reflection to get the private XamlReader.LoadBaml() method and cache the result
_loadBamlMethod ??= typeof(XamlReader).GetMethod("LoadBaml", BindingFlags.NonPublic | BindingFlags.Static)
?? throw new InvalidOperationException("Could not find XamlReader.LoadBaml() via reflection");
// Load the XAML resource for the control
var stream = Application.GetResourceStream(new Uri(xamlResourceUri, UriKind.Relative)).Stream;
var parserContext = new ParserContext { BaseUri = PackUriHelper.Create(new Uri("application://")) };
_loadBamlMethod.Invoke(null, new object[] { stream, parserContext, control, true });
}
Which can then be used like this. Controls in other assemblies may now inherit from BaseControl and not see this issue.
public partial class BaseControl : UserControl
{
protected BaseControl()
{
// The resource URI here can be coped from the generated partial class
// Note that we are also re-using the _contentLoaded field defined in the generated partial class
this.InitializeComponent("/Senti.Common.PrismModules.Hmi;component/controls/basecontrol.xaml", ref _contentLoaded);
}
}
It should definitely be noted that this workaround (as well as the ones in other answers) work by accessing a private method within the WPF framework, which is obviously not a supported use case. That said, I have developed and tested this approach with the .NET 5 version of WPF and not seen any issues. Microsoft has also said that very little development is planned for the WPF framework other than bugfixes etc, so this workaround should be fairly stable.
Quicker than closing all of Visual Studio is just to kill XDescProc.exe in your task manager.
XDescProc is the designer. The moment the process is closed you'll see a Reload the designer link in visual studio. Click that and XDes will be started again and your 'no resource' error should be gone.
Here's the link visual studio shows after you kill the designer process:
I had accidently deleted a user control via a rename/copy action. When I reinstated the project file and the xaml file and .cs from version control this error started happening in the design studio for that control which had mistakenly been deleted/renamed.
That suggested some type of cache on the file in question....so closing Visual Studio, deleting the bin directory and rebuilding worked.
Followed PainElemental's solution (to clarify, for his code the ClassLibrary1 for me was the .dll name without the .dll extension), here's my scenario in case it helps anyone link their specific error messages to the problem:
I use dll's to load and run usercontrols into a main program as their own popup windows. PainElemental's solution was mostly working , but 1 of the 3 classes in my "popup .dll" wouldn't load properly. I would get an exception with 2 inner exceptions, like:
mscorlib InvokeMethod...;
WpfXamlLoader.Load...Provide value on...StaticResourceExtension...;
ResolveBamlType....method or operation is not implemented.
In my case, I confirmed it would load the new URI and work in testing, but when I tried to run it over in my Live environment it would error in LoadViewFromUri().
As I tested further, I narrowed down the issue to not being able to load a separate "library .dll" file I was using which contained a Converter I was using in the .xaml file of the class which was failing, and on further research the issue there was that the Live environment was using a different "library .dll" version than I was using in my test environment, even though the exception message from my "popup .dll" did not make any mention of that.
For reference, I use Copy Local=True and that didn't give me issues. To best debug these kinds of issues, an understanding of the locations where .dll files are searched for by the .exe is helpful. As I understand it, when you are running projects in VS, when Copy Local=True the .dlls get copied to the same folder as the .exe when it is Built. When the .exe is run the standard location it will search for .dlls is the same folder as the .exe. Additional locations that the .exe can look for .dlls can be set in the .exe.config file, in the probing element. In the below example, it can also search in a 'MyDLLs' and the 'MyDLLs\Core' directory relative to the .exe's location. Note that it will not naturally search any subfolders, you have to specify them explicitly. I believe it also searches the GAC, but I currently have minimal knowledge concerning GAC.
<configuration>
...
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<probing privatePath="MyDLLs;MyDLLs\Core;"/>
</assemblyBinding>
</runtime>
</configuration>
Hi the way solve this problem was to rename the xaml usercontrol to all smallcaps on InitializeComponent()...
enter image description here
enter image description here
For me, when trying to launch a window dialog (window.ShowDialog()) in my application during startup, the exception was thrown in the InitializeComponent method in the window's class constructor.
After much head scratching I had discovered that the issue was that an app.publish folder was getting created in the debug directory, which contained the application exe only. Deleting the app.publish folder resolved this exception. See the following article to prevent this folder from getting created:
What creates the directory "app.publish" in visual studio 2013?

Categories

Resources