четверг, 30 июня 2011 г.

Стили и ресурсы в WPF


Стили – это одна из наиболее мощных и полезных особенностей WPF и XAML. Стили позволяют практически до неузнаваемости изменять внешний вид (и поведение) элементов управления и форм.
Предположим, что мы хотим иметь возможность менять внешний вид интерфейса на лету. В таком случае мы можем просто создать несколько различных стилей и менять их в ответ на какие-либо действия пользователя.

Стили также очень полезны из-за того, что они позволяют избегать чрезмерного разрастания XAML кода страницы, так как стили могут быть вынесены в отдельный файл.
Каждый элемент управления имеет множество свойств, каждое из которых может быть задано при помощи стиля.

В этой статье мы рассмотрим небольшой пример работы  со стилями в WPF.
Итак, пуская мы хотим задать цвет фона и размер шрифта для кнопки. Самый простой и очевидный способ сделать это – это просто задать все нужные стили прямо в теге кнопки:


        <Button Name="button1" Width="150" FontSize="12" Background="AliceBlue" 


Content="Click Me!" Margin="3" Click="OnResources" />


Есть еще один способ задать стиль элемента прямо в самом элементе:


<Button Width="150" Content="Click Me!" Margin="3">
<Button.Style>
<Style TargetType="Button">
<Setter Property="Background" Value="Yellow" />
<Setter Property="FontSize" Value="14" />
<Setter Property="FontWeight" Value="Bold" />
</Style>
</Button.Style>
</Button>


Понятно, что использование такого способа задания стиля не является оптимальным. Например, часто нужно применить один и тот же стиль к нескольким элементам управления, в этом случае нам пришлось бы просто скопировать код.

К счастью, стили можно создавать отдельно от самих элементов управления и хранить их в коллекциях ресурсов. Например, вот так:

<Window.Resources>
<Style TargetType="{x:Type Button}">
<Setter Property="Background" Value="LemonChiffon" />
<Setter Property="FontSize" Value="18" />
</Style>
</Window.Resources>

Такой стили станет стилем по умолчанию для кнопок на текущей странице.

 
        <Button Width="200" Content="Uses default style" Margin="3" />
 
Давайте зададим еще несколько стилей.


<Window.Resources>
<Style TargetType="{x:Type Button}">
<Setter Property="Background" Value="LemonChiffon" />
<Setter Property="FontSize" Value="18" />
</Style>
<Style x:Key="ButtonStyle">
<Setter Property="Button.Background" Value="Red" />
<Setter Property="Button.Foreground" Value="White" />
<Setter Property="Button.FontSize" Value="18" />
</Style>

<Style x:Key="FancyButtonStyle">
<Setter Property="Button.FontSize" Value="22" />
<Setter Property="Button.Foreground" Value="White" />
<Setter Property="Button.Background">
<Setter.Value>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
<GradientStop Offset="0.0" Color="LightCyan" />
<GradientStop Offset="0.14" Color="Cyan" />
<GradientStop Offset="0.7" Color="DarkCyan" />
</LinearGradientBrush>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>


 Применим их к нескольким новым кнопкам.
        
         <Button Width="200" Content="Uses named style" Style="{StaticResource ButtonStyle}" Margin="3" />

        <Button Width="200" Content="Fancy button style" Style="{StaticResource FancyButtonStyle}" Margin="3" />
 

Еще одной особенностью стилей в WPF является то, что стили могут наследовать свойства других стилей. Вот пример:
<Window.Resources>
<Style TargetType="{x:Type Button}">
<Setter Property="Background" Value="LemonChiffon" />
<Setter Property="FontSize" Value="18" />
</Style>
<Style x:Key="ButtonStyle">
<Setter Property="Button.Background" Value="Red" />
<Setter Property="Button.Foreground" Value="White" />
<Setter Property="Button.FontSize" Value="18" />
</Style>

<Style x:Key="FancyButtonStyle">
<Setter Property="Button.FontSize" Value="22" />
<Setter Property="Button.Foreground" Value="White" />
<Setter Property="Button.Background">
<Setter.Value>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
<GradientStop Offset="0.0" Color="LightCyan" />
<GradientStop Offset="0.14" Color="Cyan" />
<GradientStop Offset="0.7" Color="DarkCyan" />
</LinearGradientBrush>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="AnotherButtonStyle" BasedOn="{StaticResource FancyButtonStyle}" TargetType="Button">
<Setter Property="Foreground">
<Setter.Value>
<LinearGradientBrush>
<GradientStop Offset="0.2" Color="White" />
<GradientStop Offset="0.5" Color="LightYellow" />
<GradientStop Offset="0.9" Color="Orange" />
</LinearGradientBrush>
</Setter.Value>
</Setter>
</Style>


</Window.Resources>

Стиль AnotherButtonStyle наследует свойства от FancyButtonStyle и переопределяет лишь свойство ForeGround. Можно убедиться, что цвет фона и размер шрифта идентичен для двух этих стилей.

        <Button Width="200" Content="Style inheritance" Style="{StaticResource AnotherButtonStyle}" Margin="3" />

Так будет выглядеть весь код страницы:

<Window x:Class="StylesAndResources.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="240" Width="500">
    <Window.Resources>
        <Style TargetType="{x:Type Button}">
            <Setter Property="Background" Value="LemonChiffon" />
            <Setter Property="FontSize" Value="18" />
        </Style>
        <Style x:Key="ButtonStyle">
            <Setter Property="Button.Background" Value="Red" />
            <Setter Property="Button.Foreground" Value="White" />
            <Setter Property="Button.FontSize" Value="18" />
        </Style>

        <Style x:Key="FancyButtonStyle">
            <Setter Property="Button.FontSize" Value="22" />
            <Setter Property="Button.Foreground" Value="White" />
            <Setter Property="Button.Background">
                <Setter.Value>
                    <LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
                        <GradientStop Offset="0.0" Color="LightCyan" />
                        <GradientStop Offset="0.14" Color="Cyan" />
                        <GradientStop Offset="0.7" Color="DarkCyan" />
                    </LinearGradientBrush>
                </Setter.Value>
            </Setter>
        </Style>
       
        <Style x:Key="AnotherButtonStyle" BasedOn="{StaticResource FancyButtonStyle}" TargetType="Button">
            <Setter Property="Foreground">
                <Setter.Value>
                    <LinearGradientBrush>
                        <GradientStop Offset="0.2" Color="White" />
                        <GradientStop Offset="0.5" Color="LightYellow" />
                        <GradientStop Offset="0.9" Color="Orange" />
                    </LinearGradientBrush>
                </Setter.Value>
            </Setter>
        </Style>


    </Window.Resources>
    <StackPanel>
        <Button Name="button1" Width="150" FontSize="12" Background="AliceBlue" Content="Click Me!" Margin="3" Click="OnResources" />
        <Button Width="150" Content="Click Me!" Margin="3">
            <Button.Style>
                <Style TargetType="Button">
                    <Setter Property="Background" Value="Yellow" />
                    <Setter Property="FontSize" Value="14" />
                    <Setter Property="FontWeight" Value="Bold" />
                </Style>
            </Button.Style>
        </Button>
        <Button Width="200" Content="Uses default style" Margin="3" />
        <Button Width="200" Content="Uses named style" Style="{StaticResource ButtonStyle}" Margin="3" />
        <Button Width="200" Content="Fancy button style" Style="{StaticResource FancyButtonStyle}" Margin="3" />
        <Button Width="200" Content="Style inheritance" Style="{StaticResource AnotherButtonStyle}" Margin="3" />

    </StackPanel>
</Window>

В результате должно получиться вот такое окошко с шестью кнопками.

Стили также можно создавать и применять динамически в коде приложения. Давайте посмотрим на простой пример создания такого стиля.
using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Windows;

using System.Windows.Controls;

using System.Windows.Data;

using System.Windows.Documents;

using System.Windows.Input;

using System.Windows.Media;

using System.Windows.Media.Imaging;

using System.Windows.Navigation;

using System.Windows.Shapes;

 

namespace StylesAndResources

{

    /// <summary>

    /// Interaction logic for MainWindow.xaml

    /// </summary>

    public partial class MainWindow : Window

    {

        public MainWindow()

        {

            InitializeComponent();

        }

 

        private void OnResources(object sender, RoutedEventArgs e)

        {

            new ResourceDemo().Show();

        }

    }

}


<Window x:Class="StylesAndResources.ResourceDemo"

        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

        Title="Resource Demo" Height="300" Width="300">

    <StackPanel x:Name="myContainer">

        <StackPanel.Resources>

            <LinearGradientBrush x:Key="MyGradientBrush" StartPoint="0,0" EndPoint="0.3,1">

                <GradientStop Offset="0.0" Color="LightCyan" />

                <GradientStop Offset="0.14" Color="Cyan" />

                <GradientStop Offset="0.7" Color="DarkCyan" />

            </LinearGradientBrush>

        </StackPanel.Resources>

        <Button Width="200" Height="50" Foreground="White" Margin="5" 
Background="{StaticResource MyGradientBrush}" Content="Click Me!" />

 

        <Button Name="button1" Width="220" Height="50" Margin="5" Click="button1_Click">

            Apply Resource Programmatically

        </Button>

 

        <Button Name="button2" Width="200" Height="50" Foreground

="White" 
Margin="5" Background="{DynamicResource MyGradientBrush}" Content="Change Resource" 
Click="button2_Click" />

 

        

 

    </StackPanel>

 

</Window>



Создадим еще одну страницу и реализуем обработчик первой кнопки так, чтобы при нажатии на нее открывалась новая страница:


using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Windows;

using System.Windows.Controls;

using System.Windows.Data;

using System.Windows.Documents;

using System.Windows.Input;

using System.Windows.Media;

using System.Windows.Media.Imaging;

using System.Windows.Navigation;

using System.Windows.Shapes;

 

namespace StylesAndResources

{

    /// <summary>

    /// Interaction logic for MainWindow.xaml

    /// </summary>

    public partial class MainWindow : Window

    {

        public MainWindow()

        {

            InitializeComponent();

        }

 

        private void OnResources(object sender, RoutedEventArgs e)

        {

            new ResourceDemo().Show();

        }

    }

}

<Window x:Class="StylesAndResources.ResourceDemo"

        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

        Title="Resource Demo" Height="300" Width="300">

    <StackPanel x:Name="myContainer">

        <StackPanel.Resources>

            <LinearGradientBrush x:Key="MyGradientBrush" StartPoint="0,0" EndPoint="0.3,1">

                <GradientStop Offset="0.0" Color="LightCyan" />

                <GradientStop Offset="0.14" Color="Cyan" />

                <GradientStop Offset="0.7" Color="DarkCyan" />

            </LinearGradientBrush>

        </StackPanel.Resources>

        <Button Width="200" Height="50" Foreground="White" Margin="5" Background="{StaticResource MyGradientBrush}" Content="Click Me!" />

 

        <Button Name="button1" Width="220" Height="50" Margin="5" Click="button1_Click">

            Apply Resource Programmatically

        </Button>

 

        <Button Name="button2" Width="200" Height="50" Foreground="White" Margin="5" Background="{DynamicResource MyGradientBrush}" Content="Change Resource" Click="button2_Click" />

 

        

 

    </StackPanel>

 

</Window>

Тут мы задали стиль MyGradientBrush и у нас есть три кнопки. Для первой мы просто используем этот стиль. А для остальных будем менять его динамически.
В обработчике нажатия на первую кнопку нам нужно найти стиль MyGradientBrush и применить его к текущей кнопке.

        private void button1_Click(object sender, RoutedEventArgs e)

        {

            Control ctrl = sender as Control;

            ctrl.Background = ctrl.FindResource("MyGradientBrush"as Brush;

        }

Для второй кнопки мы создадим несколько другой алгоритм. Сначала мы очистим всю коллекцию стилей для данной страницы. После этого мы создадим кисть и добавим ее в коллекцию стилей сстриницы.

        private void button2_Click(object sender, RoutedEventArgs e)

        {

            myContainer.Resources.Clear();

            var brush = new LinearGradientBrush { StartPoint = new Point(0, 0), EndPoint = new Point(0, 1) };

 

            brush.GradientStops = new GradientStopCollection()

            {

                new GradientStop(Colors.White, 0.0),

                new GradientStop(Colors.Yellow, 0.14),

                new GradientStop(Colors.YellowGreen, 0.7)

            };

            myContainer.Resources.Add("MyGradientBrush", brush);

 

 

        }

Обратите внимание на то, что теперь новый стиль будет применяться и к кнопке button1, в то время как кпопка со статическим стилем не изменится.
Весь код этой страницы выглядит вот так:

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Windows;

using System.Windows.Controls;

using System.Windows.Data;

using System.Windows.Documents;

using System.Windows.Input;

using System.Windows.Media;

using System.Windows.Media.Imaging;

using System.Windows.Shapes;

 

namespace StylesAndResources

{

    /// <summary>

    /// Interaction logic for ResourceDemo.xaml

    /// </summary>

    public partial class ResourceDemo : Window

    {

        public ResourceDemo()

        {

            InitializeComponent();

        }

 

        private void button1_Click(object sender, RoutedEventArgs e)

        {

            Control ctrl = sender as Control;

            ctrl.Background = ctrl.FindResource("MyGradientBrush"as Brush;

        }

 

        private void button2_Click(object sender, RoutedEventArgs e)

        {

            myContainer.Resources.Clear();

            var brush = new LinearGradientBrush { StartPoint = new Point(0, 0), EndPoint = new Point(0, 1) };

 

            brush.GradientStops = new GradientStopCollection()

            {

                new GradientStop(Colors.White, 0.0),

                new GradientStop(Colors.Yellow, 0.14),

                new GradientStop(Colors.YellowGreen, 0.7)

            };

            myContainer.Resources.Add("MyGradientBrush", brush);

 

 

        }

    }

}

Стили – это очень важный инструмент при работе с XAML, Windows Presentation Foundation и Silverlight. Так что надеюсь, что это простая вводная статья будет полезной.

1 комментарий:

  1. А есть стать про то как вынести стили в отдельный файл?

    ОтветитьУдалить