Implementacja mechanizmu skórek w WPF

2013-02-20

W WPF przy wykorzystaniu stylów oraz szablonów w prosty sposób możemy stworzyć mechanizm skórek, dzięki któremu możliwa będzie zmiana wyglądu naszej aplikacji w trakcie jej działania. W dzisiejszym wpisie pokażę jak taki mechanizm stworzyć. Zacznijmy od prostego okna składającego się z kontenera Grid oraz trzech przycisków. Po naciśnięciu każdego z nich załadowana zostanie odpowiednia skórka zmieniająca wygląd aplikacji:

<Window x:Class="Skin.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="194" Width="536">
    <Grid>
        <Button Name="GraySkinButton" Content="SZARY"
                HorizontalAlignment="Left" VerticalAlignment="Top"
                Margin="27,53,0,0" Width="140" Height="50" FontSize="14"
                Click="GraySkinButton_Click" />
        <Button Name="BlackSkinButton" Content="CZARNY"
                HorizontalAlignment="Left" VerticalAlignment="Top"
                Margin="189,53,0,0" Width="140" Height="50" FontSize="14"
                Click="BlackSkinButton_Click" />
        <Button Name="BlueSkinButton" Content="NIEBIESKI"
                HorizontalAlignment="Left" VerticalAlignment="Top"
                Margin="351,53,0,0" Width="140" Height="50" FontSize="14"
                Click="BlueSkinButton_Click" />
    </Grid>
</Window>

Teraz stwórzmy definicje naszych skórek: szarej, czarnej oraz niebieskiej. Z reguły poszczególne elementy umieszcza się w oddzielnych plikach (np. style i szablony) ale na potrzeby tego wpisu kod każdej skórki umieszczę w jednym pliku.

Szara skórka (plik GraySkin.xaml) składa się jedynie ze stylu zmieniającego tło grida:

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

    <Style TargetType="Grid">
        <Setter Property="Background" Value="#FFF3F3F9" />
    </Style>

</ResourceDictionary>

Czarna skórka (plik BlackSkin.xaml) posiada definicję szablonu przycisku oraz style dla przycisku i grida:

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

    <ControlTemplate x:Key="button" TargetType="Button">
        <Border x:Name="border"
				Background="{TemplateBinding Background}"
				BorderBrush="{TemplateBinding BorderBrush}"
				BorderThickness="{TemplateBinding BorderThickness}">
            <ContentPresenter x:Name="content" HorizontalAlignment="Center"
                              VerticalAlignment="Center" />
        </Border>
        <ControlTemplate.Triggers>
            <Trigger Property="IsMouseOver" Value="True">
                <Setter Property="BorderThickness" Value="3" TargetName="border" />
            </Trigger>
            <Trigger Property="IsEnabled" Value="False">
                <Setter Property="TextBlock.Foreground" Value="LightGray"
                        TargetName="content" />
            </Trigger>
            <Trigger Property="IsPressed" Value="True">
                <Setter TargetName="content" Property="RenderTransform">
                    <Setter.Value>
                        <TranslateTransform X="1" Y="1" />
                    </Setter.Value>
                </Setter>
            </Trigger>
        </ControlTemplate.Triggers>
    </ControlTemplate>

    <Style TargetType="Button">
        <Setter Property="Template" Value="{StaticResource button}" />
        <Setter Property="Foreground" Value="White" />
        <Setter Property="Background" Value="#FF15A015" />
        <Setter Property="BorderBrush" Value="#FF97F85C" />
        <Setter Property="BorderThickness" Value="0" />
    </Style>

    <Style TargetType="Grid">
        <Setter Property="Background" Value="Black" />
    </Style>

</ResourceDictionary>

Niebieska skórka (plik BlueSkin.xaml) podobnie jak czarna zawiera szablon przycisku oraz style przycisku i grida:

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

    <LinearGradientBrush x:Key="buttonBackground" StartPoint="0.5,0" EndPoint="0.5,1">
        <GradientStop Color="#FF00255A" Offset="0.0" />
        <GradientStop Color="#FF016CC5" Offset="1.0" />
    </LinearGradientBrush>

    <LinearGradientBrush x:Key="activeButtonBackground" StartPoint="0.5,0" EndPoint="0.5,1">
        <GradientStop Color="#FF7FCBED" Offset="0.0" />
        <GradientStop Color="#FFD3ECF9" Offset="1.0" />
    </LinearGradientBrush>

    <SolidColorBrush x:Key="buttonBorderBrush" Color="White" />
    <SolidColorBrush x:Key="buttonForeground" Color="White" />
    <SolidColorBrush x:Key="activeButtonForeground" Color="#FF004990" />

    <DropShadowEffect x:Key="shadow" Color="Gray" ShadowDepth="0" Opacity="0.9" />

    <ControlTemplate x:Key="button" TargetType="Button">
        <Border x:Name="border"
				Background="{StaticResource buttonBackground}"
				BorderBrush="{StaticResource buttonBorderBrush}"
				BorderThickness="3"
				CornerRadius="6">
            <TextBlock x:Name="content" Text="{TemplateBinding Content}"
                       HorizontalAlignment="Center" VerticalAlignment="Center"
                       Foreground="{StaticResource buttonForeground}"
                       TextWrapping="Wrap" TextAlignment="Center" />
        </Border>
        <ControlTemplate.Triggers>
            <Trigger Property="IsMouseOver" Value="True">
                <Setter TargetName="border" Property="Background"
                        Value="{StaticResource activeButtonBackground}" />
                <Setter TargetName="content" Property="Foreground"
                        Value="{StaticResource activeButtonForeground}" />
                <Setter TargetName="content" Property="FontWeight"
                        Value="SemiBold" />
            </Trigger>
            <Trigger Property="IsEnabled" Value="False">
                <Setter Property="Background" Value="Gray" TargetName="border" />
                <Setter Property="BorderBrush" Value="Gray" TargetName="border" />
            </Trigger>
            <Trigger Property="IsPressed" Value="True">
                <Setter TargetName="content" Property="RenderTransform">
                    <Setter.Value>
                        <TranslateTransform X="1" Y="1" />
                    </Setter.Value>
                </Setter>
            </Trigger>
        </ControlTemplate.Triggers>
    </ControlTemplate>

    <Style TargetType="Button">
        <Setter Property="Template" Value="{StaticResource button}" />
        <Setter Property="Effect" Value="{StaticResource shadow}" />
    </Style>

    <Style TargetType="Grid">
        <Setter Property="Background" Value="#FFE3ECFD" />
    </Style>

</ResourceDictionary>

Dysponując definicjami skórek możemy przejść do obsługi ich przełączania. Cały mechanizm polega na dynamicznym ładowaniu plików ze skórkami do zasobów aplikacji:

public partial class MainWindow : Window
{
	private ResourceDictionary currentSkin;
	private Collection<ResourceDictionary> appResources;

	public MainWindow()
	{
		InitializeComponent();
		appResources = App.Current.Resources.MergedDictionaries;
		SetSkin(LoadResourceDictionary("GraySkin.xaml"));
	}

	private ResourceDictionary LoadResourceDictionary(string dictionaryName)
	{
		return (ResourceDictionary)App.LoadComponent(
			new Uri(dictionaryName, UriKind.Relative));
	}

	private void SetSkin(ResourceDictionary newSkin)
	{
		if (newSkin == null)
			return;

		if (currentSkin != null)
			appResources.Remove(currentSkin);

		appResources.Add(newSkin);
		currentSkin = newSkin;
	}

	private void GraySkinButton_Click(object sender, RoutedEventArgs e)
	{
		SetSkin(LoadResourceDictionary("GraySkin.xaml"));
	}

	private void BlackSkinButton_Click(object sender, RoutedEventArgs e)
	{
		SetSkin(LoadResourceDictionary("BlackSkin.xaml"));
	}

	private void BlueSkinButton_Click(object sender, RoutedEventArgs e)
	{
		SetSkin(LoadResourceDictionary("BlueSkin.xaml"));
	}
}

Oto efekt działania aplikacji:

GraySkin

BlackSkin

BlueSkin

Reklamy

Posted on 2013-02-20, in .NET/C# and tagged , , , . Bookmark the permalink. 1 komentarz.

  1. Kapitalny wpis 😉 Tego szukałem 😉

Skomentuj

Wprowadź swoje dane lub kliknij jedną z tych ikon, aby się zalogować:

Logo WordPress.com

Komentujesz korzystając z konta WordPress.com. Log Out / Zmień )

Zdjęcie z Twittera

Komentujesz korzystając z konta Twitter. Log Out / Zmień )

Facebook photo

Komentujesz korzystając z konta Facebook. Log Out / Zmień )

Google+ photo

Komentujesz korzystając z konta Google+. Log Out / Zmień )

Connecting to %s

%d blogerów lubi to: