Search code examples
wpfvisual-studio-2012stylesresourcedictionary

How should application wide resources be organized?


Here is the scenario:

I have been using VS 2010 to write quite a large WPF project using MEF. There are multiple projects within this solution and I have one ResourceDictionary for all of my styles.

The AppStyles.xaml ResourceDictionary is located in my startup project, it contains all of my styles for the project. And within my App.xaml I have the following:

<Application x:Class="Dionysus.Shell.App"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         DispatcherUnhandledException="UnhandledException" ShutdownMode="OnMainWindowClose">
<Application.Resources>
    <ResourceDictionary>
        <ResourceDictionary.MergedDictionaries>

            <ResourceDictionary Source="AppStyles.xaml"/>

    </ResourceDictionary>
</Application.Resources>

Now, this all works perfectly in design mode of VS 2010 and when I am debugging, the correct styles are being applied to all of my controls. But I recently switched to VS 2012, and the designer is no longer working! When I debug it works perfectly but whenever I use one of my styles as follows:

<Button Name="btnForward" Height="23" Margin="10,0" Style="{StaticResource DionysusButton}" Grid.Row="2" Grid.Column="0"/>

I get the following error in the VS 2012 designer:

The resource "DionysusButton" could not be resolved.

I am clueless as to why this is happening, I have renamed my Style, and even the ResourceDictionary to no avail.

Is there a different way I should be doing this in VS 2012 or is this a problem with VS 2012?

I have found a lot of posts that say that changing the x:Name property of my AppStyles.xaml ResourceDictionary to x:Key will resolve this problem but I cannot do that as it is not a valid option for a ResourceDictionary.

Thanx in advance

-- UPDATE

I am using Marc's answer and all seems to be going well except for default styles. I have the following style:

 <Style TargetType="DataGrid" x:Name="DataGridStyle"/>

And this is my main ResourceDictionary:

<ResourceDictionary x:Name="Main" 
                xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">


<ResourceDictionary.MergedDictionaries>
    <ResourceDictionary Source="Styles/AppStyling.xaml" />
</ResourceDictionary.MergedDictionaries>

<Style TargetType="DataGrid" BasedOn="{StaticResource DataGridStyle}"/>

When I do not add the Style tag in my main ResourceDictionary it works fine, but the style is not being applied to all items obviously. When I do add it, i get the following exception:

Provide value on 'System.Windows.Markup.StaticResourceHolder' threw an exception

Ideas?


Solution

  • Are you trying to use the styles which are located in your app.xaml in your modules which you export into the shell using PRISM? The modules should not have a reference to your application/shell project (the startup project)? Then they maybe just don't 'know' the project, which contains the styles? Though the question is why it works in VS2010 then... Please let me know, if I misunderstood your MEF scenario, I'll adapt my answer then.

    Resources and Prism is abit tricky, and I am a fan of the following approach, which I have posted before, but it might help in your case too. It is much more appropriate for a PRISM solution than placing your resources inside app.xaml:

    I usually create a seperate styling project, which I reference from the projects, which I want to style. The styling project has a fixed structure like this:

    Styling project

    For every control, I create a styling ResourceDictionary. For example for my buttons:

    <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
        <Style x:Key="PrimaryButtonStyle" TargetType="Button">
        </Style>
    
        <Style x:Key="ToolbarButton" TargetType="Button">
            <Setter Property="BorderThickness" Value="0" />
            <Setter Property="Margin" Value="3"/>
            <Setter Property="Background" Value="Transparent"></Setter>
        </Style>
    </ResourceDictionary>
    

    In one main ResourceDictionary, I merge all the other dictionaries, in this case in the file IncaDesign.xaml, which you can see in the picture above:

    <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                        xmlns:controls="clr-namespace:Commons.Controls;assembly=Commons">
    
        <ResourceDictionary.MergedDictionaries>
    
            <ResourceDictionary Source="Converter/Converter.xaml" />
            <ResourceDictionary Source="Styles/Button.xaml" />
            <ResourceDictionary Source="BitmapGraphics/Icons.xaml" />
    
        </ResourceDictionary.MergedDictionaries>
    
        <!-- Default Styles -->
        <Style TargetType="Button" BasedOn="{StaticResource PrimaryButtonStyle}"></Style>
    </ResourceDictionary>
    

    Notice how I defined the default styles, which are applied automatically, unless you specify otherwise. In every window or control, that you want to style, you only need to reference this one ResourceDictionary. Note the definition of the source, which is a reference to the assembly (/Commons.Styling;component...)

    <UserControl.Resources>        
        <ResourceDictionary>            
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="/Commons.Styling;component/IncaDesign.xaml" />
            </ResourceDictionary.MergedDictionaries>  
            <!-- Here you can added resources which belong to the UserControl only -->
            <!-- For example: -->
            <DataTemplate x:Kex="UcTemplate" /> 
        </ResourceDictionary>        
    </UserControl.Resources>
    

    Default styles will be set automatically now, and if you want to access a resource explicitly, you can do this, using StaticResource.

    <Viewbox Height="16" Width="16" Margin="0,0,10,0">
        <ContentControl Content="{StaticResource FileIcon32}" />
    </Viewbox>
    

    This is very nice solution in my opinion, which works for very complex solutions, including modular solutions, for example built with PRISM.