вторник, 27 августа 2013 г.

WPF. Skinning and Theming.

У WPF використовуються 4 основні механізми зміни зовнішнього вигляду аплікації:
Styles (Стилі ) – представляють простий механізм стилізації шляхом застосування набору значень пропертей для більш ніж одного елемента.   Функціонал подібний до  використання CSS в HTML;
- Templates (Темплейти) – використовуються для зміни вигляду контролу, визначають візуальне представлення контролу;
- Skins (Скіни)  -  представляють собою специфічні  колекції стилів  і / або  темплейтів , зазвичай з можливістю бути зміненими  динамічно.
- Themes  (Теми) -  механізм, який дозволяє використовувати візуальні  характеристики операційної системи, з  можливістю змін налаштування юзером.
В дану пості будуть розглядатись Скіни і Теми.


Skinning

Skinning полягає в механізмі динамічної зміни зовнішнього вигляду аплікації (або скіна ).  Skin являє собою  специфічні колекції стилів і / або темплейтів , зазвичай з можливістю бути зміненими динамічно.
В WPF немає чіткої концепції яка називається skin, і немає формального понняття skin. 
Можна легко написати аплікацію   або компонент, який підтримує skinning за допомогою механізму Dynamic Resource в WPF
Давайте розглянемо кроки для скіннінгу аплікації.
  1. Створення логічно повязаних стилів і темплейтів для UI елементів і розміщення їх в ResourceDictionary.
В заатаченому семплі створено 3 кастомні скіни , які по суті являють собою ResourceDictionary в яких містяться логічно повязані стилі і темплейти.
skinning
 
 
   1: <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

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

   3:     <!--Brushes-->

   4:     <LinearGradientBrush x:Key="BlueBackground"

   5:                          StartPoint="0,0"

   6:                          EndPoint="0,1">

   7:         <GradientStop Offset="0"

   8:                       Color="#c1dbe8" />

   9:         <GradientStop Offset="0.5"

  10:                       Color="#008fc7" />

  11:         <GradientStop Offset="1"

  12:                       Color="#066caa" />

  13:     </LinearGradientBrush>

  14:  

  15:     <SolidColorBrush x:Key="BlueStroke"

  16:                      Color="#0075b0" />

  17:  

  18:     <ImageBrush x:Key="BluePattern"

  19:                 ImageSource="/Images/line6.png"

  20:                 TileMode="Tile"

  21:                 Viewport="0,0,100,100"

  22:                 ViewportUnits="Absolute" />

  23:     

  24:     <!--Control Templates-->

  25:     <ControlTemplate x:Key="LabelTemplate"

  26:                      TargetType="Label">

  27:         <Border BorderBrush="{StaticResource BlueStroke}"

  28:                 BorderThickness="1"

  29:                 Background="White"

  30:                 CornerRadius="3,15,15,3"

  31:                 Padding="1">

  32:             <Border Background="{StaticResource BlueBackground}"

  33:                     CornerRadius="3,15,15,3"

  34:                     Padding="3">

  35:                 <ContentPresenter />

  36:             </Border>

  37:         </Border>

  38:     </ControlTemplate>

  39:  

  40:     <ControlTemplate x:Key="ButtonTemplate"

  41:                      TargetType="Button">

  42:         <Grid>

  43:             <Border x:Name="Border"

  44:                     Background="{StaticResource BlueBackground}"

  45:                     BorderThickness="1"

  46:                     CornerRadius="3"

  47:                     BorderBrush="{StaticResource BlueStroke}"

  48:                     RenderTransformOrigin="0.5,0.5" />

  49:             <ContentPresenter VerticalAlignment="Center"

  50:                               HorizontalAlignment="Center"

  51:                               TextElement.Foreground="White" />

  52:         </Grid>

  53:  

  54:         <ControlTemplate.Triggers>

  55:             <Trigger Property="IsPressed"

  56:                      Value="True">

  57:                 <Setter Property="RenderTransform"

  58:                         TargetName="Border">

  59:                     <Setter.Value>

  60:                         <ScaleTransform ScaleX="1"

  61:                                         ScaleY="-1" />

  62:                     </Setter.Value>

  63:                 </Setter>

  64:             </Trigger>

  65:             <Trigger Property="IsMouseOver"

  66:                      Value="True">

  67:                 <Setter Property="BorderBrush"

  68:                         TargetName="Border"

  69:                         Value="#a1bbc8" />

  70:             </Trigger>

  71:         </ControlTemplate.Triggers>

  72:     </ControlTemplate>

  73:     

  74:     <!--Styles-->

  75:     <Style x:Key="LabelStyle"

  76:            TargetType="Label">

  77:         <Setter Property="Foreground"

  78:                 Value="White" />

  79:         <Setter Property="Template"

  80:                 Value="{StaticResource LabelTemplate}" />

  81:         <Setter Property="Width"

  82:                 Value="100" />

  83:         <Setter Property="VerticalAlignment"

  84:                 Value="Top" />

  85:         <Setter Property="Margin"

  86:                 Value="0,0,8,0" />

  87:     </Style>

  88:  

  89:     <Style x:Key="TextBoxStyle"

  90:            TargetType="TextBox">

  91:         <Setter Property="BorderBrush"

  92:                 Value="{StaticResource BlueStroke}" />

  93:         <Setter Property="Foreground"

  94:                 Value="{StaticResource BlueStroke}" />

  95:         <Setter Property="FontSize"

  96:                 Value="13" />

  97:     </Style>

  98:     

  99:     <Style x:Key="PasswordBoxStyle"

 100:            TargetType="PasswordBox">

 101:         <Setter Property="BorderBrush"

 102:                 Value="{StaticResource BlueStroke}" />

 103:         <Setter Property="Foreground"

 104:                 Value="{StaticResource BlueStroke}" />

 105:         <Setter Property="FontSize"

 106:                 Value="13" />

 107:     </Style>

 108:  

 109:     <Style x:Key="ButtonStyle"

 110:            TargetType="Button">

 111:         <Setter Property="Template"

 112:                 Value="{StaticResource ButtonTemplate}" />

 113:         <Setter Property="HorizontalAlignment"

 114:                 Value="Right" />

 115:     </Style>

 116:  

 117:     <Style x:Key="BorderStyle"

 118:            TargetType="Border">

 119:         <Setter Property="Padding"

 120:                 Value="10" />

 121:         <Setter Property="Background"

 122:                 Value="Blue" />

 123:     </Style>

 124:  

 125:     <Style TargetType="{x:Type RadioButton}" >

 126:         <Setter Property="Template">

 127:             <Setter.Value>

 128:                 <ControlTemplate TargetType="{x:Type RadioButton}">

 129:                     <BulletDecorator Background="White" Cursor="Hand">

 130:                         <BulletDecorator.Bullet>

 131:                             <Grid Height="16" Width="16">

 132:                                 <!--Define size of the Bullet-->

 133:  

 134:                                 <!--The two borders-->

 135:                                 <Border Name="RadioOuter" Background="Transparent" BorderBrush="Gainsboro" BorderThickness="2" CornerRadius="2" />

 136:                                 <Border CornerRadius="0" Margin="4" Name="RadioMark" Background="#FFADADAD" Visibility="Hidden" />

 137:  

 138:                             </Grid>

 139:                         </BulletDecorator.Bullet>

 140:  

 141:                         <!--Text element-->

 142:                         <TextBlock Margin="3,1,0,0" Foreground="#FF3E3E3E" FontFamily="Calibri" FontSize="12">

 143:        <ContentPresenter />

 144:                         </TextBlock>

 145:                     </BulletDecorator>

 146:  

 147:                     <!--If item is checked, trigger the visibility of the mark-->

 148:                     <ControlTemplate.Triggers>

 149:                         <Trigger Property="IsChecked" Value="true">

 150:  

 151:                             <!--If item is checked, trigger the visibility of the mark

 152: and change the color of the selected bullet into a darker gray for better highlighting-->

 153:                             <Setter TargetName="RadioMark" Property="Visibility" Value="Visible"/>

 154:                             <Setter TargetName="RadioOuter" Property="BorderBrush" Value="#FFADADAD" />

 155:  

 156:                         </Trigger>

 157:  

 158:                     </ControlTemplate.Triggers>

 159:                 </ControlTemplate>

 160:             </Setter.Value>

 161:         </Setter>

 162:     </Style>

 163:  

 164: </ResourceDictionary>

    2.  Встановлення стилів для UI елементів через ссилку Dynamic Resource .
<TabItem Name="TabDR" Header="Skinning using Dynamic Resource">
               <Grid Grid.Column="1">
                   <Border  Style="{DynamicResource BorderStyle}" >
                       <Grid>
                           <Grid.RowDefinitions>
                               <RowDefinition></RowDefinition>
                               <RowDefinition></RowDefinition>
                               <RowDefinition></RowDefinition>
                               <RowDefinition></RowDefinition>
                               <RowDefinition></RowDefinition>
                               <RowDefinition></RowDefinition>
                           </Grid.RowDefinitions>

                           <Border Grid.Row="0">
                               <Grid>
                                   <Grid.ColumnDefinitions>
                                       <ColumnDefinition></ColumnDefinition>
                                       <ColumnDefinition></ColumnDefinition>
                                   </Grid.ColumnDefinitions>

                                   <Label Grid.Column="0" Content="UserId"  Style="{DynamicResource LabelStyle}" />
                                   <TextBox Grid.Column="1"  Style="{DynamicResource TextBoxStyle}"/>

                               </Grid>

                           </Border>

                           <Border Grid.Row="1">
                               <Grid>
                                   <Grid.ColumnDefinitions>
                                       <ColumnDefinition></ColumnDefinition>
                                       <ColumnDefinition></ColumnDefinition>
                                   </Grid.ColumnDefinitions>

                                   <Label  Grid.Column="0" Content="Password"  Style="{DynamicResource LabelStyle}"/>
                                   <PasswordBox  Grid.Column="1" Style="{DynamicResource PasswordBoxStyle}"></PasswordBox>

                               </Grid>

                           </Border>

                           <Border Grid.Row="2">
                               <Grid>
                                   <Grid.ColumnDefinitions>
                                       <ColumnDefinition></ColumnDefinition>
                                       <ColumnDefinition></ColumnDefinition>
                                   </Grid.ColumnDefinitions>

                                   <Label  Grid.Column="0" Content="Email"   Style="{DynamicResource LabelStyle}"/>
                                   <TextBox  Grid.Column="1" Style="{DynamicResource TextBoxStyle}"  />

                               </Grid>

                           </Border>

                           <Border Grid.Row="3">
                               <Grid>
                                   <Grid.ColumnDefinitions>
                                       <ColumnDefinition></ColumnDefinition>
                                       <ColumnDefinition></ColumnDefinition>
                                   </Grid.ColumnDefinitions>

                                   <Label  Grid.Column="0" Content="First Name"  Style="{DynamicResource LabelStyle}"/>
                                   <TextBox   Grid.Column="1"   Style="{DynamicResource TextBoxStyle}" />

                               </Grid>
                           </Border>

                           <Border Grid.Row="4">
                               <Grid>
                                   <Grid.ColumnDefinitions>
                                       <ColumnDefinition></ColumnDefinition>
                                       <ColumnDefinition></ColumnDefinition>
                                   </Grid.ColumnDefinitions>

                                   <Label  Grid.Column="0" Content="Last Name"  Style="{DynamicResource LabelStyle}"/>
                                   <TextBox  Grid.Column="1"   Style="{DynamicResource TextBoxStyle}"  />
                               </Grid>
                           </Border>

                           <Border Grid.Row="5">
                               <Button Content="Submit" Grid.Row="4"  Style="{DynamicResource ButtonStyle}"  />
                           </Border>

                       </Grid>
                   </Border>
               </Grid>
           </TabItem>



3. Аплаїння нового скіну шляхом загрузки певної ResourceDictionary

private void OnSkinChanged(object sender, RoutedEventArgs e)
        {
            App currentApp = Application.Current as App;
            string name = (e.OriginalSource as RadioButton).Name;
            currentApp.Resources.Clear();
            if (name == "None") return;

            ResourceDictionary skin =  Application.LoadComponent(new Uri("Skins/" + name + ".xaml", UriKind.Relative)) as ResourceDictionary;
            currentApp.Resources.MergedDictionaries.Add(skin);

            e.Handled = true;
        }



                                           Використання готових скінів !!!!
Нещодавно я знайшов опенсорсний проект WPF Themes на CodePlex,
Даний проект містить готовий набір таких скінів


  • Expression Dark (From Silverlight Toolkit)

  • Expression Light (From Silverlight Toolkit)

  • Rainier Purple (From Silverlight Toolkit)

  • Rainier Orange (From Silverlight Toolkit)

  • Rainier Radial Blue

  • Shiny Dark Green

  • Shiny Dark Teal

  • Shiny Dark Purple

  • Shiny Blue (From Silverlight Toolkit)

  • Shiny Red (From Silverlight Toolkit)

  • Bureau Black

  • Bureau Blue

  • Whistler Blue

  • UX Musing Red (From CorrinaB)

  • UX Musing Green (From CorrinaB)

  • UX Musing Rough

  • UX Musing Rough Green (From CorrinaB)

  • UX Musing Rough Red (From CorrinaB)

  • UX Musing Bubbly

  • UX Musing Bubbly Blue (From CorrinaB)

Список контролів , які підтримують ці теми:

  • Button
  • ToggleButton
  • RadioButton
  • CheckBox
  • TextBox
  • ComboBox
  • ListBox
  • ProgressBar
  • Slider
  • TreeView
  • Expander
  • Planned

Скачати цей проект можна тут CodePlex
Детальнішу інформацію про  WPF.Themes.dll можна прочитати тут http://jiezhu0815.blogspot.com/2011/10/apply-themes-to-net-40-wpf-applications.html
 

Theming




Theming - механізм, який дозволяє використовувати візуальні характеристики операційної системи, з можливістю змін налаштування юзером.

На приклад, зміна Windows теми на Windows Classic надає баттонам і скролбарам плоский вигляд.  Або у Windows XP  переключення
з дефолтної кольорової схеми на Blue, Olive Green, або Silver впливає на колір і вигляд стандартних контролів.
Також важливо зрозуміти як працює темінг  при створені власних кастомних контролів для гармонізації  з темою операційної системи
В даному пості наведу 2 приклади темінгу  :

  •   1 спосіб   -  проcтий, але не потужний

  •   2 спосіб  - трохи складніший, але більш гнучкіший

1 спосіб  - використання  System Colors, Fonts, і Parameters


- Клас SystemColors надає доступ до налаштувань кольору.

- Клас SystemFonts забезпечує доступ до налаштувань шрифтів.

- Клас SystemParameters охоплює величезний список налаштувань, які описують стандартний розмір різних екранних елементів, параметри клавіатури і миші, розмір екрану, а також активні графічні ефекти (на зразок відкидання тіней і відображення вмісту вікон при перетягуванні).

Проперті, які надаються класами  SystemColors, SystemFonts , and SystemParameters автоматично апдейтаються , коли міняється Windows тема.  Таким чином включення їх в Стилі і темплейти являється легким способом використовувати візуальні характеристики операційної системи.


<Style x:Key="ButtonStyle" TargetType="Button" >

              <Style.Resources>

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

                      

                      <GradientStop Offset="0"   Color="{DynamicResource {x:Static SystemColors.InactiveCaptionColorKey}}"/>

                      <GradientStop Offset="0.5" Color="{DynamicResource {x:Static SystemColors.InactiveCaptionColorKey}}"/>

                      <GradientStop Offset="1"   Color="{DynamicResource {x:Static SystemColors.ActiveCaptionColorKey}}"/>

                  </LinearGradientBrush>

              </Style.Resources>

              <Setter Property="Foreground" Value="{StaticResource foregroundBrush}"/>

              <Setter Property="Background" Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"/>

          </Style>




2 спосіб – написання стилів і темплейтів для кожної Windows  теми.


Багато вбудованих елементів WPF маю різний вигляд в залежності від оброної Windows теми  Наприклад, вони зазвичай
блискучі в Windows 7 Aero і менш привабливі  в Windows Classic. Це досягається за допомогою  окремих шаблонів елемента  для кожної теми.
2 спосіб теммінгу полягає в написанні власних стилів і темплейтів, логічно згрупованих в теми , і   використання їх при зміні Windows теми.
Давайте розглянемо по кроках


  1. Перший крок полягає в :



  • розміщенні темо-повязаних ресурсів в різні resource dictionary (це і є наші теми).

  • далі необхідно розмістити ці теми в папку Themes (ця папочка обовязково  має бути в руті проекту).

  • назвати  теми  -  ThemeName.ThemeColor.xaml  (Слід зауважити, що URI теми Windows Classic не має ThemeColor )

  • вказати резервну тему  Themes \ Generic.xaml, яка буде використовуватись, якщо не має теми, яка б відповідала поточній темі Windows.

theming

Тема загружається  і аплається автоматично при зміні Windows теми.
Нище наведено існуючі теми Windows :


  • The Aero theme (Windows Vista and Windows 7): themes\Aero.NormalColor.xam

  • The default Windows XP theme: themes\Luna.NormalColor.xaml

  • The olive green Windows XP theme: themes\Luna.Homestead.xaml

  • The silver Windows XP theme: themes\Luna.Metallic.xaml

  • The Windows XP Media Center Edition 2005 and Windows XP Tablet PC Edition 2005 theme: themes\Royale.NormalColor.xaml

  • The Windows Classic theme: themes\Classic.xaml

  • The Zune Windows XP theme: themes\Zune.NormalColor.xaml

 

2. Другий крок – настройка ThemeInfo


Аттрибут ThemeInfo вказує де автоматичний механізм темінгу повинен шукати тему і generic dictionary.
Можна проставити наступні значення:

  • None (default): – не шукає resource dictionary.
  • SourceAssembly: Resource dictionary знаходиться в поточній ассемблі.
  • ExternalAssembly: Resource dictionary знаходиться в іншій ассемблі,  яка названа <AssemblyName>.<ThemeName>.dll, де  <AssemblyName> назва ассемблі.

 

 

[assembly: ThemeInfo(

    ResourceDictionaryLocation.SourceAssembly, //where theme specific resource dictionaries are located

    //(used if a resource is not found in the page, 

    // or application resource dictionaries)

    ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located

    //(used if a resource is not found in the page, 

    // app, or any theme specific resource dictionaries)

)]


В зааатаченом прикладі показано використання темінгу , в т.ч. для кастомного контролу MyCustomButton



Скачати приклад























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

  1. Ну що ж, почитав я трохи про те чому застосуввання динамічних ресурсів пришвидшує завантаження аплікації, діло в тому, що ці ресурси підвантажуються при необхідності а статичні грузяться одразу всі при старті аплікухи.

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