Context:
I have a created a user control library project in Microsoft Visual Studio 2017 with a lot of Styles and ControlTemplates to be used in various future projects. Now I want to reference it in a new solution.
What works:
When I create a new solution with a new WPF-Application I can import the user control library project into the solution and add a reference to it in my new WPF-Application project - everything works fine this way. But this isn't a good solution for me because I don't want to copy the sourcecode everytime I use it.
What doesn't work:
...So I tried to not import it and referencing it as a project but to just reference the compiled dll-file of the user control library. In principle, everything should stay the same from the WPF-Applications point of view. But the Designer doesn't find the referenced resources correctly. At runtime it works perfectly well with no errors, but at designtime the Designer either ignores the referenced resources or crashes entirely.
Its behaviour doesn't seem to be very consistent anyway:
It finds some resources correctly, e.g. It shows referenced ColorBrushes defined in the user control library correctly (e.g.Background="{StaticResource MyBlue}")
Sometimes it seems to find a referenced Style-resource but not the resources nested within that Style (e.g. Style="{StaticResource TextBoxOutput}" shows error "The resource "MarginStandard" can't be found." when MarginStandard is used in TextBoxOutput)
Styles without a key which should apply by default don't apply automatically.
All this works correctly at runtime and when I reference the user control library as project rather than as dll.
What I already checked
User control library and WPF-Application use the same CPU-Architecture (AnyCPU).
User control library and WPF-Application use the same .net Version (4.6.1)
I tried different Build-Actions for compiling the user control library (resource, embedded resource, none).
The dll-file gets copied into the bin/debug-folder of the WPF-project.
All resource dictionaries from the user control library are imported in the App.xaml file of the WPF-project using the following Syntax:
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/UserControlLibraryName;component/RecourceDictionaryName.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
My Question
How can I use resources referenced as dll-file in such a way that the Designer also finds the resources correctly? My Styles and ControlTemplates alter the look of the Controls drastically, so its really necessary to be able to see the results at designtime.
Related
I'm hoping to create a WPF User Control Library with reusable controls for my many projects to save time recreating them every time (something I've found myself doing).
So I:
Created a WPF User Control Library, and created a ThemeDark.xaml file to contain common colour keys and styles.
Created a separate regular WPF project in the same solution so that I can test the controls in an actual window.
Added a project reference to the User Control Library.
Added a ResourceDictionary.MergedDictionaries into the App.xaml which references the theme file from the other project.
The problem: I can access and use the SolidColorBrush keys from the theme file just fine in the testing project, but for some reason IntelliSense can't see the Styles (which all have keys).
I also can't seem to use the pack://application:,,,/ method of referencing the ResourceDictionary in App.xaml; it only accepts the filename. This was originally set through the Properties panel -> Source -> Browse, however I have tried a few different formats, all to no avail. It also doesn't seem to notice that ThemeDark.xaml is in a Themes/ directory within the User Control Library project.
So - How does one properly reference a ResourceDictionary from a WPF User Control Library project and be able to use all resources (including Styles)?
DarkTheme.xaml (inside user control library project):
App.xaml (inside testing project):
Resulting behaviour (very strange - SolidColorBrushes available but Styles aren't):
I found the problem. I had removed the spaces from the project's namespace, but not from the assembly name.
Once that was fixed I was able to use Source="pack://application:,,,/MyControlLibrary;component/Themes/ThemeDark.xaml"
MyControlLibrary isn't the actual name! ;)
I have a Class Library project with default controls and styles in. I created a Nuget package from this project. I then tried to use the Package Manager in Visual Studio to add a reference to another project, but this is where I have the problem.
The reference gets added successfully, but then I cannot access the App.xaml file from the packaged project in a ResourceDictionary within the MergedDictionaries property of the consumer project's App.xaml file:
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/DefaultStyles;component/App.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
I get an inline error on this line (not shown in the Error List) stating:
Assembly 'DefaultStyles' is not referenced by this project
But it is referenced by the project. I can see the reference in the Project References and I have Intellisense for the controls that are in the referenced project.
Also, the project builds successfully, but I have two similar errors displayed in the Error List:
The name "ImageWithSingleTextLine" does not exist in the namespace "clr-namespace:DefaultStyles.ItemTemplates;assembly=DefaultStyles".
But it does exist in there and I get Intellisense for it, so that if I try to write the line again, it auto completes for me... and then gives me the error again. No amount of cleaning, building and rebuilding will fix the issue.
It's not until I try to run the application that I get a further clue. A XamlParseException is thrown from the App.xaml.cs constructor, stating:
The invocation of the constructor on type 'DefaultStyles.App' that matches the specified binding constraints threw an exception.
It also has an inner Exception of type InvalidOperationException:
Cannot create more than one System.Windows.Application instance in the same AppDomain.
So, it seems like that is the root cause of the problem. In the default styles project, I have extensively used the App.xaml file, and added an event handler into the App.xaml.cs code behind file.
My question is how can I make this all work?
As you correctly discovered, you cannot have more than a single instance of the Application class in one AppDomain. The solution is to move the contents of your App.xaml file into a ResourceDictionary and use that instead. You can still use a code behind file for a standard ResourceDictionary, if you need to.
I'm trying to create a project dll file that holds core styles, resources, controls, etc. This will then be referenced into various other projects to use as a graphic base to unify the appearance.
Anyway, I have the graphics project set up and I can use it in other projects and it all seems to display correctly. However, XAML intellisense for my static resources doesn't seem to be working.
Here is my resource dictionary reference:
<ResourceDictionary Source="pack://application:,,,/BIT.GraphicElements;component/BITCoreStyles.xaml" />
As you can see I'm using pack notation to reference it inside it's assembly. I can style elements like I would expect (StButtonAction is in the above dictionary):
Style="{StaticResource StButtonAction}"
However, I have to type it in manually. Intellisense doesn't give it as an option but if I type it manually is applies it successfully. It also works if I add the project to the solution and reference it there, however this is intended to be used by those that don't necessarily have access to the graphics project so that's not a good solution in the long run...
What am I missing? Is there a way to make Intellisense work for these?
Maybe this'll help future searchers even if it doesn't fix your specific problem.
When you have a solution involves external dll then you can occasionally find resources just don't get loaded at design time. At all. Even though you merge in app.xaml the things don't turn up until you run.
It looks like some sort of subtle bug to me.
The way I work round this is to use functionality originally intended for Blend. We are now using the blend designer in visual studio.
In solution explorer, add a resource dictionary to Properties. Yes. Properties. Create it first in your project and drag it up there. This must be called DesignTimeResources. This can then merge any resource dictionaries you will want to use just for design time or when you have an issue makes you think resource dictionaries aren't getting loaded for design time.
Mine is in army editor but uses resources from a referenced dll, UILIb. It looks like:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ArmyEditor">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/UILib;component/Resources/UILibResources.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
This is a resource dictionary in a bit of an odd place but nothing particularly weird so far.
Next is the bit that many developers will never have done - editing your csproj.
Right click your project and choose unload.
Right click it again and edit it.
In there you will have entries for all the stuff makes up your project. And of course breaking this would be bad so maybe you want to back up first.
If you don't use blend then I think the important thing here is to add a node which says ContainsDesignTimeResources True, but here's mine:
<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>
Save your edits.
Reload your project.
When the designer is open, you should find resources from it are loaded. Maybe this will help intellisense find them.
I mentioned this earlier but.. In my experience, such intellisense on resources is often unreliable.
This definitely solved a similar issue I have had in this solution. It is also useful when you want to temporarily overlay resource dictionaries for localisation/branding testing.
My situation is this.
I have multiple projects under a WPF solution which make up a multi-tier application I am developing. Since separate projects will sometimes need to access the same image or xaml resource I'd like to centralize "Public" resources to one project that can be referenced by any other projects requiring these "Public" resources. For example, I have a BMP image that I use in various controls and windows in separate projects that serves as a banner/logo. It seems like I should be able to add this image as a resource to the public resources project and reference it from my other projects instead of adding the image separately to every project that needs it. If this is possible, what would it look like and how should I go about doing it? If my idea just sucks I'm open to suggestions, but the project is going to be quite large so I don't want to be adding resources all over the place.
Thanks!
P.S.
I have searched this topic quite a bit but there are so many garbage answers out there from people that don't know what they are doing. Given that I'm relatively new to WPF I'd rather get a direct answer to my problem.
Well after some tinkering and research I found a solution to my own question. It seems like bits and pieces of my answer were scattered all over the web so I'll try to explain the whole thing at once, in one place. To re-cap I basically wanted to centralize any resources used across projects within my solution. These are the steps I eventually followed to accomplish this.
1.
Create a User Control project in your solution (any project that can host Resource Dictionaries will do). I'll refer to this project as "the resource project".
2.
Add any resource files (images etc...) that you want to share between projects to the resource project. I try to organize files in sub-directories that make sense. You will want to set the build action to "Resource" so that it gets compiled into the output binary.
3.
Now add a resource dictionary to the resource project. In my case I wanted to reference several images so I made one called "ImageDictionary.xaml".
4.
Add references to the image files in the resource dictionary. I am referencing images here but its just a resource dictionary, you can put whatever in there. The string in the middle is just the relative path to the image file you are referring to.
<ImageSource x:Key="SomeImageKey">
Images/SomeImage.bmp
</ImageSource>
5.
Now go to a project that requires the resource dictionary you just made in step 4. It can be another user control or a window, whatever. Right-click on project references and add a reference to the resource project.
6.
Okay now the current project needs to reference the dictionary you made in step 4 that is defined in the resource project. I made a user control first so this is what my code would look like below... Note that I used merged dictionaries. I plan on using more later so I chose this way instead of a single dictionary.
"ResourceProject" below is the name of the project/assembly that you added the resource to.
"component" is a keyword that needs to be there, followed by the path to the dictionary xaml code file.
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/ResourceProjectAssembly;component/Resources/ImageDictionary.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
As you can see, referencing an external resource dictionary requires something called a "Pack URI". I found a short hand way of doing it. The long way is quite a bit uglier and as far as I know, there is no advantage to it. But here is what the long way looks like.
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/ResourceProjectAssembly;component/Resources/ImageDictionary.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
7.
Now that your current project has a reference to the resource dictionary in the resource project, you can use anything from that dictionary. I went ahead and put an image in my user control. Below is the code. Its just like referencing a dictionary locally.
<Image Source="{StaticResource SomeImageKey}" />
8.
Important Note!
This caused me a headache for a while before I found it. Make sure that the startup project in your solution has a reference to the resource project EVEN IF THE STARTUP PROJECT DOES NOT USE THE RESOURCE PROJECT. (I.e. right-click references, add reference etc...)
The resources will not get compiled into the output unless you do this. This part was tricky because the designer was even showing my images and I had no xaml errors but at runtime it would throw set property and cannot find file exceptions. This went away when I referenced my resource project from my startup project.
Hopefully this helps somebody.
Below are some links to a few places (including stackoverflow) that gave me what I needed to put all the pieces together.
http://blogs.msdn.com/b/wpfsldesigner/archive/2010/06/03/creating-and-consuming-resource-dictionaries-in-wpf-and-silverlight.aspx
Load WPF styles or other Static Resources from an external file or assembly
ResourceDictionary in a separate assembly
I'm writing some extension modules for a WPF Composite application supplied by another vendor. The application is themed, and gives users the option to select from a number of themes which will change the appearance of the entire applications, including any custom modules that register themselves with the theme manager appropriately.
I really want my custom extensions to look like an integrated component, so I'm trying to use only styles defined as resources within the main application. As I'm still learning the nuances of XAML, I'm styling more by trial and error.
I'm wondering if there's a way of 'discovering' what styles are available in a different assembly. Here is an example of what I'm currently doing.
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<common:DesignTimeResourceDictionary Source="/Vendor.Desktop.WPFCommon;component/themes/generic.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<Border
Background="{DynamicResource LightGradientBackgroundBrush}"
Margin="0"
>
The LightGradientBackgroundBrush is defined in the vendors assemblies. By including the above ResourceDictionary, the style is present during design time, and the brush isn't underlined in the XAML editor.
Can I find what other styles are defined? I'm only aware of that style because it was mentioned in passing in a sample provided by the vendor.
Edit:
I'm aware of a BAML add-in for reflector, but it doesn't work with reflector 7 unless it has been recompiled/patched. Someone has already done that here though.
I was hoping there was a better solution though. Intellisense for styles would be great.
There used to be a Reflector add-in for viewing BAML resources that worked well for that. But I haven't used Reflector in a while since Redgate crashed the party. It may not work with the current version.
You know where your resource dictionary is so you should be able to load it up in code and enumerate over the available resources. This will at least give you an idea of what's available.
See this link: http://blogs.claritycon.com/leeroth/2009/05/20/load-xaml-resource-dictionaries-at-runtime/
Enumerate over the Keys property and use the item property to access the resources. You can get as detailed as you want, but at the very least you should be able to spit out the resource names.