I have an WPF solution and this solution consist of 3 project:
1-A project that has several WPF user control inside
2-Another project that has several WPF user control inside
3-A project which has Resources for 2 WPF projects above.
As you know, if you have common settings for you views like that
-Using Same FontFamily.
-Using same FontSize
-Using same FontWeight
-Using same BackroundBrush for all your User Controls etc.. You need to declare this setters in you all usercontrol tags like below:
<UserControl ....
FontFamily="{DynamicResource MyFontFamily}"
FontSize="{DynamicResource MyFontSize}"
FontWeight="{DynamicResource MyFontWeight}"
Background="{DynamicResource MyAppBgBrush2}"
Width="250" d:DesignHeight="350">
<Grid/>......
But I dont want to write same setters in all my UserControls. For thi reason, I decided to move this property setting in to a new c# file and locate it in Resource Project.
using System.Windows.Controls;
namespace Resources
{
public class PageBase : UserControl
{
public PageBase()
{
SetResourceReference(FontFamilyProperty, "MyFontFamily");
SetResourceReference(FontSizeProperty, "MyFontSize");
SetResourceReference(FontWeightProperty, "MyFontWeight");
SetResourceReference(BackgroundProperty, "MyAppBgBrush2");
}
}
}
So, In my Resource project, I adited AssemlyInfo.cs file like this:
[assembly: System.Windows.Markup.XmlnsDefinition("http://schemas.sat.com/winfx/2010/xaml/internalresources", "Resources")]
This edit gives me ability to declare/create a user control like below:
<internalresources:PageBase
xmlns:internalresources="http://schemas.sat.com/winfx/2010/xaml/internalresources">
<Grid>DoWhatEver<Grid/>
<internalresources:PageBase/>
From now, I do not have to create a usercontrol view which its tags start with
<UserControl...., I can start with <internalresources:PageBase......
My Question is that, VisualStudio 2010 can show me Design of all my user control bu Expression blend can not. Interesting part is that both in VS and Blend, my project compiling without any error But when I try to open my views in blend it says:
-The namespace 'PageBase' does not exist in namespace "http://schemas.sat.com/winfx/2010/xaml/internalresources"
P.S: References are added properly to my Project and My project was suitable to open with blend.
Inheriting your own UserControl makes sense when you are adding new abilities or properties to it. In your case you just want to override the design.
You can achieve this very simply by creating a Resource XAML (BasePageResources.xaml) and define your UI properties there with Setter tags.
<Style TargetType="Button">
<Setter Property="MinWidth" Value="75"/>
<Setter Property="MinHeight" Value="23"/>
<Setter Property="Margin" Value="11,11,0,0"/>
</Style>
You can dump all your setters in a single file, give your setters keys, just like CSS classes.
Then, in your App.xaml, you can include these files to make them Application-wide accessable.
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="BasePageResources.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
All your buttons in the application should now apply your styles. It's much better than inheriting, and WPF binding feels more natural than your work-around. Blend should cause you less problems if you design the way it expects you to.
I have found the problem. The problem is just about Expression blend. If your project setting does not have any PropertyGroup for Debug|AnyCPU you will get this problem. You should add this propertygroup to your csproj file like below via text editor :
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>x86</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
Related
I'm creating a WPF Revit Addin. For this I'd like to use some 3rd party controls for extra UI functionality and for their styles (see ModernWpf])
Within a 'normal' WPF application it all works fine: I add the library (nuget) and added the themes resouces to the app.xaml's ResourceDictionary.MergedDictionaries.
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ui:ThemeResources />
<ui:XamlControlsResources />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
Adding (for example) a DropDownButton control to a page and running the WPF app results in a working and styled DropDownButton.
DropDownButton with styling
In my Revit Addin this doesn't work somehow. Since there is no app.xaml, I tried to add the resources at different places (Window, Page, UserControl, my theme resource MainTheme.xaml, ... The control itself is shown and the dropdown action works, but there is no styling.
DropDownButton without styling
The ModernWpf.dll and ModernWpf.Controls.dll are copied to the Revit Addins folder, and these dll's hold the resources (as seen with DotPeek), so these should be available somehow.
DotPeek on ModernWpf.dll
What am I missing / how can I fix this?
Thanks in advance, Michel
As you pointed out, in Revit add in there is no app.xaml so no assembly wide resources either, this is because Revit add in is a class library, not a proper WPF application. You can check out this question and take one of the approaches. In my add-ins, I fixed this issue like this:
Create xaml file with resource dictionary, for example "Resources.xaml" (to easily create WPF things inside class library, check out this question)
Create your global resources in "Resources.xaml"
Create this class. Replace *name of your project* with name of your project.
public class SingletonResources : ResourceDictionary
{
private static ResourceDictionary? inst;
public SingletonResources()
{
if (inst is null)
{
var uri = new Uri("/*name of your project*;component/Themes/Resources.xaml", UriKind.Relative);
inst = (ResourceDictionary)System.Windows.Application.LoadComponent(uri);
}
MergedDictionaries.Add(inst);
}
}
Then in every control where you want to be able to access "Resources.xaml", inside of control resources include SingletonResources. Like this:
<Window.Resources>
<revitPluginUi:SingletonResources>
</revitPluginUi:SingletonResources>
</Window.Resources>
Now you can access all resources from "Resource.xaml" inside window.
IDE autocompletion for resource keys will not work.
Important note: if you need to create local resources for control, and you defined SingletonResources you need to define local resources inside SingletonResources tag. Like this:
<Window.Resources>
<revitPluginUi:SingletonResources>
<SolidColorBrush x:Key="MyLocalBrush"/>
</revitPluginUi:SingletonResources>
</Window.Resources>
I have a class library where I'm defining (basically extending) some controls such as TextBox, Button etc. I'm also using MaterialDesignInXamlToolkit which is used to stylize controls. So my class library will essentially have controls with my own extended functionality and they will look like styles defined in MaterialDesignInXamlToolkit.
Now my question is, since I don't have App.xaml in class library project, where should I write the XAML code to import the styles of MaterialDesignInXamlToolkit, so that they will be applied to my extended controls? What is the place in class library where you can specify styles which are globally accessible and are applied to all the controls?
I searched about this but didn't find what I want. Please help.
Update: Here is my code (not working).
MaterialTextBox.cs
using System.Windows.Controls;
namespace MaterialControls
{
public class MaterialTextBox : TextBox
{
... some extra features here (no XAML file for this class, just this .cs)...
}
}
Themes.xaml (this will contain all the global styles)
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MaterialControls">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.Light.xaml" />
<ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.Defaults.xaml" />
<ResourceDictionary>
<Style TargetType="local:MaterialTextBox">
<Setter Property="FontWeight" Value="Bold"/>
<Setter Property="Height" Value="100"/>
</Style>
</ResourceDictionary>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
Now I want these styles to apply to MaterialTextBox so that wherever I use it, it should come with this look and featues out of the box.
What is the place in class library where you can specify styles which are globally accessible and are applied to all the controls?
There is none really. In a single resource dictionary, you could use <ResourceDictionary.MergedDictionaries> to import resources that the resources that you define in the resource Dictionary itself are based on, e.g.:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication8">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.Light.xaml" />
</ResourceDictionary.MergedDictionaries>
<Style TargetType="...">
<!-- style based on MaterialDesignTheme -->
</Style>
</ResourceDictionary>
But there is no concept of an App.xaml or some kind of "global resource cache" in a class library.
Found the solution.
I was using Class Library project where I actually should have used WPF Custom Control Library project. Here project type is important otherwise you will have to play with .csproj file to make it work.
So now created a new WPF Custom Control Library project (New Project > Windows > Classic Desktop > WPF Custom Control Library template). This project has Themes\Generic.xaml file which will be used as a default location for styles.
There is no concept for a dictionary in the assembly which is automaticaly merged into app.xaml. But for a default control style there is one.
To assign a default style set the DefaultStyleKeyProperty for the control.
static MaterialTextBox() {
DefaultStyleKeyProperty.OverrideMetadata(typeof(MaterialTextBox), new FrameworkPropertyMetadata(typeof(MaterialTextBox)));
}
and in Themes\Generic.xaml add the style:
<Style TargetType="{x:Type local:MaterialTextBox}">
...
</Style>
Do not merge Themes\Generic.xaml in your App.xaml
do only add default styles for controls created in this assembly.
The resources in Themes\Generic.xaml are not globaly available, but through the DefaultStyleKeyProperty the resource is found and assigned to the control.
I am working on a WPF application that uses the Wizard window from the Extended WPF Toolkit. I need to change the color of the footer of the wizard and unfortunately the developers didn't expose any property to do it, so I need to edit the style.
The Toolkit is imported as NuGet package, so I cannot just edit the source code. I found the default style of the control (Generic.xaml) on Codeplex, copied it in a file in my project so now I have something like this:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Xceed.Wpf.Toolkit"
xmlns:conv="clr-namespace:Xceed.Wpf.Toolkit.Core.Converters">
<conv:WizardPageButtonVisibilityConverter x:Key="WizardPageButtonVisibilityConverter" />
<Style TargetType="{x:Type local:Wizard}">
...
Here I get two errors:
The type 'conv:WizardPageButtonVisibilityConverter' was not found.
Verify that you are not missing as assembly reference and that all
referenced assemblies have been built.
and
The name "Wizard" does not exist in the namespace
"clr-namespace:Xceed.Wpf.Toolkit".
Then I tried to change the line
xmlns:local="clr-namespace:Xceed.Wpf.Toolkit"
to
xmlns:local="http://schemas.xceed.com/wpf/xaml/toolkit"
and the second error disappeared, but I don't know how to deal with the first one.
Do you have any idea? Is it the right way to change the default style?
Thanks!
The XAML namespace mapping should also specify the name of the assembly in which the WizardPageButtonVisibilityConverter class is defined:
xmlns:conv="clr-namespace:Xceed.Wpf.Toolkit.Core.Converters;assembly=Xceed.Wpf.Toolkit"
I am looking for a way to share ResourceDictionary between projects.
Adding new item to shared project doesn't offer resource dictionary. It can be created in other (main) project and dragged. But then I can't change its build options to Page:
The idea is to load resource dictionary like this
var dictionary = new ResourceDictionary();
dictionary.Source = new Uri("/WpfApplication91;component/Dictionary2.xaml", UriKind.Relative);
This is obviously fails currently with
An exception of type 'System.IO.IOException' occurred in PresentationFramework.dll but was not handled in user code
Additional information: Cannot locate resource 'dictionary2.xaml'.
Any ideas?
It's possible to manually edit shared project to set build action for resource dictionary.
Shared project consists of Project.shproj and Project.projitems files, open second and locate dictionary there:
<ItemGroup>
<None Include="$(MSBuildThisFileDirectory)Dictionary.xaml" />
</ItemGroup>
Add after that
<ItemGroup>
<Page Include="$(MSBuildThisFileDirectory)Dictionary.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
</ItemGroup>
it's a copy/paste thing from normal csproj for WPF project containing dictionary.
Seems to work, though this build action is not visible when project is loaded into Visual Studio. Adding files to shared project doesn't affect this manual change.
Now I can have shared project containing resource dictionary, yay!
Resource dictionary can be merged into application dictionaries like if it's located in the root of the project (to use as static/dynamic resource in xaml designer):
<Application.Resources>
<ResourceDictionary >
<ResourceDictionary.MergedDictionaries>
<!-- doesn't really exists in project -->
<ResourceDictionary Source="Dictionary.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
and/or loaded manually, e.g. using this pack Uri :
var dictionary = new ResourceDictionary()
{
Source = new Uri("pack://application:,,,/FlexProperty.xaml"),
};
I was having the same problem. There's a solution for including Xaml in shared projects which doesn't require editing the .projitems file directly.
You just have to add Xamarin to your Visual Studio installation. (I did it with VS Community 2015.)
You can now add xaml types via the usual Visual Studio dialog:
And the correct build action is available:
Xaml in shared projects now compiles and runs as expected.
(Presumably this is there to support Xamarin Forms, but it works for any xaml document.)
This issue is that you are putting the application name. You need the project name. Below is how to do it in both XAML and code
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/SharedProject1;Component/Dictionary2.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
Or
var dictionary = new ResourceDictionary();
dictionary.Source = new Uri("/SharedProject1;component/Dictionary2.xaml", UriKind.Relative);
I have a WPF Control Library that is being added to a windows forms application. We want to allow the controls to be localizable, however I am not sure how to FULLY accomplish this without duplicating code. This is what I am doing now.
Basically, in the windows forms app, before the main application kicks off, I am instantiating an App.xaml that live within the forms app (containing my links to my resources that also live within the forms app). This works perfectly for runtime.
However, my user controls all have Content="{StaticResource SomeVariableName}", which end up being blank. I can fix this by having an app.xaml and appropriate resource dictionaries in my control library that match those in my windows forms app. However, this is duplicated code.
Things I have already tried to no avail:
Instantiate the App.xaml that lives within the user control library from within my forms app. This does not work because the URIs to my resources is looking for an embedded resource, not my local resource dictionary (I could then simply copy the resource files from the control to an appropriate location within my forms app on build). Could I leverage DeferrableContent here? There is not much online as far as I could find on this attribute and how it should be used, though.
I would like to use post builds for both App and dictionaries, however, the App instantiation is a static reference to a compiled App.xaml as far as I can tell. So, App.xaml must live within the form at least
I did try to have a duplicated App.xaml with a post build moving the resourcedictionary.xaml. I figured that a duplicated app.xaml is ok since that is the driving force and you might not want to rely on one from the control anyway (which circles back and makes you wonder if you should then have the App.xaml in the control at all? Unless you want to allow a default that uses embedded resources....) That too failed saying it could not find the resource even though it was placed where the URI should have been pointing to. The decompiled code points to Uri resourceLocater = new Uri("/WindowsFormsApplication3;component/app.xaml", UriKind.Relative);
So, Is there any way to allow for this to work AND have design time viewing of the component defaults AND avoid duplication? Or, is the duplication OK in this case? If my 2nd bullet's sub-item seems ok (duplicated App.xaml with build copied resourcedictionaries), how do I make it not look for a component level item, but instead a file level one?
Last question (and I can post this separately if necessary) that I just paid attention to. My App.xaml is being built into the code, so that does not allow me to create new ResourceDictionaries on the fly anyway. Is there any way to do this?
Final option...possibly the best one?
- I plan on using Andre van Heerwaarde's code anyway, so should I just check for the existence of a file and add it as a merged resource on the fly? Basically, have one App.xaml in my user control that links to a default embedded ResourceDictionary. And, then have the code look for the appropriate localized resources on the fly, which can be relative file paths? The only downside I see here is that the default cannot be changed on the fly...which I could probably even have that look in a specified place (using some sort of convention) and have that preferred over the built-in one?
Oh, and my reason for not wanting embedded resources is so that end users can add/modify new localized resources after the build is deployed.
I can add code if it will help you visualize this better, just let me know.
UPDATE
I am now running into a further problem with styling and not just localizing.
Here is an example of one of the internal buttons on one of the controls:
<Button Style="{StaticResource GrayButton}"
Some more things I tried/thought:
I cannot create an app.xaml (that would never be used) with the ResourceDictionary set up as ApplicationDefinitions are not allowed in library projects. I could embed this in the control's resources, but then that would always take precedence over any application level resources and I lose customizability.
Here is a connect case that actually sounds like what I am looking for, however it does not provide any real solution to this
The solution (beyond the top..which does not work) that I can think of that might work (and have yet to try) also seems like a lot of work for something that I would think should be simple. But, I might be able to create some dependency properties in the control that I can Bind to and then allow those to be overriden by the project that will be using the control. As I said, that seems like a lot of work for a pretty simple request :). Would this even work? And more importantly, is there a better, simpler solution that I am missing?
I've run into this problem once, and I resolved it by dropping the whole "Resources are objects indexed by key in canonical dictionaries" thing.
I mean, the simple fact of defining a resource in one project and referencing it in another by it's "key" should give goosebumps to any sane person. I wanted strong references.
My solution to this problem was to create a custom tool that converts my resource xaml files to static classes with a property for each resource:
So MyResources.xaml:
<ResourceDictionary>
<SolidColorBrush x:Key="LightBrush" ... />
<SolidColorBrush x:Key="DarkBrush" ... />
</ResourceDictionary>
Becomes MyResources.xaml.cs
public static class MyResources {
static MyResources() {
// load the xaml file and assign values to static properties
}
public static SolidColorBrush LightBrush { get; set; }
public static SolidColorBrush DarkBrush { get; set; }
}
For referencing a resource, you can use the x:Static instead of StaticResource:
<Border
Fill="{x:Static MyResources.LightBrush}"
BorderBrush="{x:Static MyResources.DarkBrush}"
... />
Now you got strong references, autocompletion and compile time check of resources.
I too had a problem dealing with Styling Themes and available static resources. So, I created a stand-alone library that basically had nothing but the themes to be used all nested like your MERGED resources of your prior linked question.
Then, in the Windows form (.xaml), I just put reference to that library, something like
<Window x:Class="MyAppNamespace.MyView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" ... />
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<!-- Common base theme -->
<ResourceDictionary Source="pack://application:,,,/MyLibrary;component/Themes/MyMainThemeWrapper.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<Rest of XAML for the WPF window>
</Window>
The "component" appears to refer to the root of the given "MyLibrary" project. In the actual project, I created a subfolder called "Themes", hence the source includes... ;component/Themes/...
The "MyMainThemeWrapper.xaml" is very much like your nested Merged Resource dictionaries, and it sees everything perfectly from other libraries.
Here's my partial solution to your problem. I haven't tried to handle loose resources, but I have some success with sharing resources between WinForms and WPF.
Create a class library to contain your resources in .ResX files (e.g. Resources.resx, Resources.fr.resx, etc)
Create your WPF controls in a WPF user control library
Create your WinForms host
Reference the resources in your resource library from WPF using the Infralution.Localization.Wpf markup extension and culture manager, e.g.
<TextBlock Text="{Resx ResxName=ResourceLib.Resources, Key=Test}"/>
Put the content of your WPF user controls into one or more resource dictionaries as control templates,e,g
<ControlTemplate x:Key="TestTemplate">
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<TextBlock Text="{Resx ResxName=ResourceLib.Resources, Key=Test}"/>
</Grid>
</ControlTemplate>
Use the resource template in your user controls
<UserControl x:Class="WpfControls.UserControl1"
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:DesignHeight="300" d:DesignWidth="300" >
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="ResourceDictionary.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<ContentControl Template="{StaticResource TestTemplate}" />
</UserControl>
Add a couple of lines of code to make things work
public partial class UserControl1 : UserControl
{
// we require a reference to the resource library to ensure it's loaded into memory
private Class1 _class1 = new Class1();
public UserControl1()
{
// Use the CultureManager to switch to the current culture
CultureManager.UICulture = Thread.CurrentThread.CurrentCulture;
InitializeComponent();
}
}
Here's a simple demo app called WindowsFormsHost.7z