WPF Separate Style Resource

Posted in software by Christopher R. Wirz on Wed Oct 14 2015



If you want to apply global styles to your WPF application, this can be accomplished using the application level resource dictionary. The resource dictionary can combine multiple resource dictionaries through the MergedDictionaries declaration.

Note: With merged dictionaries, the last dictionary supersedes the first.

The procedure is simple and can be done as follows:

We will need an ObjectToTypeStringConverter for certain styles to work.

public class ObjectToTypeStringConverter : IValueConverter
{
	/// <summary>
	///     Converts a value.
	/// </summary>
	/// <param name="value">The value produced by the binding source.</param>
	/// <param name="targetType">The type of the binding target property.</param>
	/// <param name="parameter">The converter parameter to use.</param>
	/// <param name="culture">The culture to use in the converter.</param>
	/// <returns>
	///     A converted value. If the method returns null, the valid null value is used.
	/// </returns>
	public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
	{
		return value?.GetType().Name ?? "";
	}

	/// <summary>
	///     Converts a value.
	/// </summary>
	/// <param name="value">The value that is produced by the binding target.</param>
	/// <param name="targetType">The type to convert to.</param>
	/// <param name="parameter">The converter parameter to use.</param>
	/// <param name="culture">The culture to use in the converter.</param>
	/// <returns>
	///     A converted value. If the method returns null, the valid null value is used.
	/// </returns>
	public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
	{
		return Type.GetType((string) value);
	}
}

Create a Styles.xaml

<ResourceDictionary 
					xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:local="clr-namespace:DemoApplication">
	<!-- Add a converter as a resource to be available for subsequent styles -->
    <local:ObjectToTypeStringConverter x:Key="ObjectToTypeStringConverter"/>
    
    <!-- Button Style -->
    <Style TargetType="Button">
        <Style.Triggers>
            <Trigger Property="IsEnabled" Value="False">
                <Setter Property="Opacity" Value="0.62" />
            </Trigger>
            <DataTrigger Binding="{Binding  
                    RelativeSource={RelativeSource Self},
                    Path=Content, 
                    Converter={StaticResource ObjectToTypeStringConverter} 
                }"  Value="String">
                <Setter Property="ContentTemplate">
                    <Setter.Value>
                        <DataTemplate>
                            <TextBlock Text="{TemplateBinding Content}" 
                                       TextAlignment="Center" 
                                       TextWrapping="Wrap"/>
                        </DataTemplate>
                    </Setter.Value>
                </Setter>
            </DataTrigger>
        </Style.Triggers>
        <Style.Resources>
            <!--    An image will be styled if within the content of a button 
                    Everywhere else, images are unaffected -->
            <Style TargetType="Image">
                <Setter Property="Opacity" Value="0.2" />
            </Style>
        </Style.Resources>
        <Setter Property="Cursor" Value="Hand"/>
        <Setter Property="FontSize" Value="14"/>
        <Setter Property="Margin" Value="5"/>
        <Setter Property="Padding" Value="5"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="Button">
                    <!-- Add a CornerRadius to the Button -->
                    <Border CornerRadius="3"  
                            BorderBrush="#0060A9" 
                            BorderThickness="1"
                            Background="{TemplateBinding Background}"
                            Opacity="{TemplateBinding Opacity}">
                        <ContentPresenter ContentTemplate="{TemplateBinding ContentTemplate}" 
                                          Content="{TemplateBinding Content}" 
                                          HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" 
                                          Margin="{TemplateBinding Padding}"
                                          VerticalAlignment="{TemplateBinding VerticalContentAlignment}" />
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

In App.xaml

<Application x:Class="DemoApplication.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:DemoApplication"
             StartupUri="MainWindow.xaml">
    <Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary 
                Source="/DemoApplication;component/Styles.xaml"/>
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Application.Resources>
</Application>

Now, every button in the DemoApplication will allow for text wrapping.