Simply adding a reference to the style assembly will be insufficient; you'll have to do something to make WPF merge the resources in. But we can do this in such a way that you'll only need to add a single line of C# (or a few lines of XAML) to your application assembly.
The most straightforward solution is probably to create a strongly-typed ResourceDictionary
in your shared styles assembly, and add it into your app-level ResourceDictionary
at start-up.
For example, create a CustomStyles.xaml
in your shared styles assembly, and pull all of your style resources into that file (either directly or via MergedDictionaries
). Make sure the Build Action is set to "Page", and add an x:Class
directive to the ResourceDictionary
element like so:
<ResourceDictionary x:Class="YourNamespace.CustomStyles"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<!-- Your styles declared or imported here -->
</ResourceDictionary>
For styles meant to replace built-in or third-party control styles, you can declare the styles as implicit, i.e., leave the x:Key
off entirely, or use the control's type as the key, e.g., x:Key="{x:Type ComboBox}"
.
Adding the x:Class
directive probably won't be enough to make Visual Studio generate a CustomStyles()
constructor that actually loads the XAML content, so you'll probably need to add a CustomStyles.xaml.cs
file manually and give it a constructor that calls InitializeComponent()
(VS should still generate this):
namespace YourNamespace
{
partial class CustomStyles
{
public CustomStyles()
{
InitializeComponent();
}
}
}
In your application, you need to get this dictionary merged into your Application.Resources
dictionary. You can do this from the App.xaml
file if you like:
<Application x:Class="YourNamespace.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:cs="clr-namespace:YourNamespace;assembly=YourCustomStylesAssembly">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<cs:CustomStyles />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>
...or you can do it on the C# side:
public partial class App
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
this.Resources.MergedDictionaries.Add(new CustomStyles());
}
}
Now, the tricky part is going to be getting these styles to work in the XAML Designer. One solution that comes to mind is to add a custom attached property that you can set on all your views, and which is only applied if you're running in the designer:
partial class CustomStyles
{
public static readonly DependencyProperty EnableDesignTimeStylesProperty =
DependencyProperty.RegisterAttached(
"EnableDesignTimeStyles",
typeof(bool),
typeof(CustomStyles),
new PropertyMetadata(
default(bool),
OnEnableDesignTimeStylesChanged));
private static CustomStyles DesignTimeResources;
private static void OnEnableDesignTimeStylesChanged(
DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
if (!DesignerProperties.GetIsInDesignMode(d))
return;
var element = d as FrameworkElement;
if (element == null)
return;
if (DesignTimeResources == null)
DesignTimeResources = new CustomStyles();
if ((bool)e.NewValue)
element.Resources.MergedDictionaries.Add(DesignTimeResources);
else
element.Resources.MergedDictionaries.Remove(DesignTimeResources);
}
public static void SetEnableDesignTimeStyles(
DependencyObject element,
bool value)
{
element.SetValue(EnableDesignTimeStylesProperty, value);
}
public static bool GetEnableDesignTimeStyles(DependencyObject element)
{
return (bool)element.GetValue(EnableDesignTimeStylesProperty);
}
}
Then, on your views, just set CustomStyles.EnableDesignTimeStyles="True"
to force the designer to merge in the style resources. At runtime, DesignerProperties.GetIsInDesignMode(d)
will evaluate to false
, and you won't end up loading a new copy of your styles in every view; you'll just inherit them from the app-level resources.