Intersoft ClientUI 8 > ClientUI Fundamentals > Styles and Templates Overview |
Styling and templating refer to a suite of features (styles, templates, and storyboards) that allow developers and designers to create visually compelling effects and to create a consistent appearance for their product.
Although developers and or designers can customize the appearance extensively on an application-by-application basis, a strong styling and templating model is necessary to allow maintenance and sharing of the appearance within and among applications.
With this styling model there are clear separation of presentation and logic. This means that designers can work on the appearance of an application by using only XAML at the same time that developers work on the programming logic by using C# or Visual Basic.This overview focuses on the styling and templating aspects of the application and does not discuss any data binding concepts. For more information about data binding, see Data Binding Overview.
This topic contains the following sections:
You can think of a Style as a convenient way to apply a set of property values to more than one element. For example, consider the following GlassButton elements and their default appearance:
XAML |
Copy Code
|
---|---|
<StackPanel HorizontalAlignment="Center" Orientation="Horizontal" VerticalAlignment="Center"> <Intersoft:GlassButton Content="Previous"/> <Intersoft:GlassButton Content="Next"/> </StackPanel> |
You can change the default appearance by setting properties, such as FontSize, Width, Padding, Margin on each GlassButton element directly. However, if you want your GlassButton elements to share some properties, you can create a Style in the Resources section of your XAML file, as shown here:
XAML |
Copy Code
|
---|---|
<UserControl.Resources> <Style TargetType="Intersoft:GlassButton"> <Setter Property="Margin" Value="8"/> <Setter Property="Padding" Value="8,3"/> <Setter Property="Width" Value="80"/> <Setter Property="FontSize" Value="13"/> </Style> </UserControl.Resources> <Grid x:Name="LayoutRoot" Background="White"> <StackPanel HorizontalAlignment="Center" Orientation="Horizontal" VerticalAlignment="Center"> <Intersoft:GlassButton Content="Previous"/> <Intersoft:GlassButton Content="Next"/> </StackPanel> </Grid> |
When you set the TargetType of your style to the GlassButton type, the style is applied to all the GlassButton elements in the user control.
Now the GlassButton elements appear as follows:
Perhaps you want your two GlassButton elements to share some property values, such as the FontSize and Width, but you also want the "Next" Button to have some additional properties. You can do that by creating a new style that is based on the first style, as shown here:
XAML |
Copy Code
|
---|---|
<UserControl.Resources> <Style x:Key="BaseStyle" TargetType="Intersoft:GlassButton"> <Setter Property="Margin" Value="8"/> <Setter Property="Padding" Value="8,3"/> <Setter Property="Width" Value="80"/> <Setter Property="FontSize" Value="13"/> </Style> <Style TargetType="Intersoft:GlassButton" BasedOn="{StaticResource BaseStyle}"/> <Style x:Key="ExtendedStyle" TargetType="Intersoft:GlassButton" BasedOn="{StaticResource BaseStyle}"> <Setter Property="Foreground" Value="Orange"></Setter> </Style> </UserControl.Resources> |
Notice that the previous style is given an x:Key. To apply the style, you set the Style property on your GlassButton to the x:Key value, as shown here:
XAML |
Copy Code
|
---|---|
<Grid x:Name="LayoutRoot" Background="White"> <StackPanel HorizontalAlignment="Center" Orientation="Horizontal" VerticalAlignment="Center"> <Intersoft:GlassButton Content="Previous"/> <Intersoft:GlassButton Content="Next" Style="{StaticResource ExtendedStyle}"/> </StackPanel> </Grid> |
The GlassButton now has different Foreground but share the same other properties as the other button.
The following shows what the GlassButton elements now look like:
As shown in the first example, setting the TargetType property to GlassButton without assigning the style an x:Key causes the style to be applied to all GlassButton elements. In this case, the x:Key is implicitly set to {x:Type GlassButton}. This means that if you explicitly set the x:Key value to anything other than {x:Type GlassButton}, the Style is not applied to all GlassButton elements automatically. Instead, you must apply the style (by using the x:Key value) to the GlassButton elements explicitly. If your style is in the resources section and you do not set the TargetType property on your style, then you must provide an x:Key.
In addition to providing a default value for the x:Key, the TargetType property specifies the type to which setter properties apply. If you do not specify a TargetType, you must qualify the properties in your Setter objects with a class name by using the syntax Property="ClassName.Property". For example, instead of setting Property="FontSize", you must set Property to "GlassButton.FontSize" or "Control.FontSize".
Also note that many ClientUI controls consist of a combination of other ClientUI controls. If you create a style that applies to all controls of a type, you might get unexpected results. For example, if you create a style that targets the StylishLabel type in your application, the style is applied to all StylishLabel controls in the your application, even if the StylishLabel is part of another control, such as a UXListBoxItem.
You can use a style on any element that derives from FrameworkElement or. The most common way to declare a style is as a resource in the Resources section in a XAML file, as shown in the previous examples. Because styles are resources, they obey the same scoping rules that apply to all resources; where you declare a style affects where the style can be applied. For example, if you declare the style in the root element of your application definition XAML file, the style can be used anywhere in your application. If you create a navigation application and declare the style in one of the application's XAML files, the style can be used only in that XAML file.
To assign a named style to an element programmatically, get the style from the resources collection and assign it to the element's Style property. Note that the items in a resources collection are of type Object. Therefore, you must cast the retrieved style to a Style before assigning it to the Style property. For example, to set the defined Extended style on a GlassButton named nextButton, do the following:
C# |
Copy Code
|
---|---|
this.nextButton.Style = this.Resources["ExtendedStyle"] as Style; |
Most of ClientUI controls has visual states embedded to provides nice user experience. These visual states can also be customized by modifying the Control Template.
The following shows GlassButton's control template which contains some visual states.
XAML |
Copy Code
|
---|---|
<Style x:Key="GlassButtonStyle1" TargetType="Intersoft:GlassButton"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="Intersoft:GlassButton"> <Grid x:Name="RootElement"> <VisualStateManager.VisualStateGroups> <VisualStateGroup x:Name="CommonStates"> <VisualStateGroup.Transitions> <VisualTransition From="*" GeneratedDuration="00:00:00.2000000" To="MouseOver"/> <VisualTransition From="MouseOver" GeneratedDuration="00:00:00.2000000" To="Normal"/> <VisualTransition From="Pressed" GeneratedDuration="00:00:00.2000000" To="Normal"/> <VisualTransition From="Pressed" GeneratedDuration="00:00:00.1000000" To="MouseOver"/> </VisualStateGroup.Transitions> <VisualState x:Name="Normal"/> <VisualState x:Name="MouseOver"> <Storyboard> <ColorAnimation Duration="0" To="#FFB8B8B8" Storyboard.TargetProperty="(Border.Background).(GradientBrush.GradientStops)[1].(GradientStop.Color)" Storyboard.TargetName="BaseColor"/> </Storyboard> </VisualState> <VisualState x:Name="Pressed"> <Storyboard> <ColorAnimation Duration="0" To="#FF6C6C6C" Storyboard.TargetProperty="(Border.Background).(GradientBrush.GradientStops)[1].(GradientStop.Color)" Storyboard.TargetName="BaseColor"/> <ColorAnimation Duration="0" To="#FF343434" Storyboard.TargetProperty="(Border.Background).(GradientBrush.GradientStops)[2].(GradientStop.Color)" Storyboard.TargetName="BaseColor"/> </Storyboard> </VisualState> <VisualState x:Name="Checked"> <Storyboard> <ColorAnimation Duration="0" To="#FF6C6C6C" Storyboard.TargetProperty="(Border.Background).(GradientBrush.GradientStops)[1].(GradientStop.Color)" Storyboard.TargetName="BaseColor"/> <ColorAnimation Duration="0" To="#FF343434" Storyboard.TargetProperty="(Border.Background).(GradientBrush.GradientStops)[2].(GradientStop.Color)" Storyboard.TargetName="BaseColor"/> </Storyboard> </VisualState> <VisualState x:Name="Disabled"> <Storyboard> <DoubleAnimation Duration="0" To=".55" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="DisabledVisualElement"/> </Storyboard> </VisualState> </VisualStateGroup> <VisualStateGroup x:Name="FocusStates"> <VisualState x:Name="Focused"/> <VisualState x:Name="Unfocused"/> </VisualStateGroup> </VisualStateManager.VisualStateGroups> <Grid x:Name="GlassElement"> <Grid.RowDefinitions> <RowDefinition/> <RowDefinition/> </Grid.RowDefinitions> <Border x:Name="BaseColor" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" CornerRadius="{TemplateBinding CornerRadius}" Grid.RowSpan="2"/> <Border x:Name="SpreadColor" BorderThickness="0.5" CornerRadius="{TemplateBinding CornerRadius}" Opacity="0.5" Grid.RowSpan="2"> <Border.BorderBrush> <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0"> <GradientStop Color="#FF686868" Offset="0.121"/> <GradientStop Color="#FFC1C1C1" Offset="0.496"/> <GradientStop Color="#FF707070" Offset="0.892"/> <GradientStop Color="#FFD1D1D1" Offset="0.595"/> </LinearGradientBrush> </Border.BorderBrush> <Border.Background> <RadialGradientBrush GradientOrigin="0.49,2.253" RadiusY="1.015" RadiusX="1.01"> <GradientStop Color="#A5FFFFFF" Offset="0.451"/> <GradientStop Offset="0.774"/> </RadialGradientBrush> </Border.Background> </Border> <Border x:Name="GlassColor" CornerRadius="{TemplateBinding CornerRadius}" Opacity="0.5" Grid.Row="1"> <Border.Background> <RadialGradientBrush GradientOrigin="0.5,-0.355" RadiusY="0.723" RadiusX="0.846"> <GradientStop Color="Black" Offset="0.341"/> <GradientStop Color="Transparent" Offset="0.78"/> <GradientStop Color="#31000000" Offset="0.565"/> </RadialGradientBrush> </Border.Background> </Border> <Border x:Name="ShineGlow" BorderBrush="Black" BorderThickness="0" CornerRadius="{TemplateBinding CornerRadius}" Grid.RowSpan="2"> <Border.Background> <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0"> <GradientStop Color="Transparent"/> <GradientStop Color="Transparent" Offset="1"/> </LinearGradientBrush> </Border.Background> </Border> </Grid> <ContentPresenter x:Name="ContentPresenter" ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/> <Border x:Name="DisabledVisualElement" Background="#FFFFFFFF" CornerRadius="3" IsHitTestVisible="false" Opacity="0"/> </Grid> </ControlTemplate> </Setter.Value> </Setter> </Style> |
As you can see, there are some visual states defined in the control templates such as MouseOver, Pressed, Checked, Normal, etc. Each of this visual states contain storyboard that you can modify so that when the visual state is triggered it will use the modified settings.
For controls that have collection of items the styling consist of two parts, styling the parent control and styling the items collection. Styling the parent control is the same as styling any other control as discusses in previous section, while styling the items collection requires different approach.
Each ItemsControl type has a corresponding item container type. These item container types indicate the control type used as the item of each ItemsControl that you can easily customize using ItemsContainerStyle. To learn more about item container types, see ItemsControl Overview.
Styling using ItemsContainerStyle property is the basic way to style the items of ItemsControl. As discussed above each ItemsControl has an item container type, so basically you just need to provide the style of the item container type and assign it to ItemsContainerStyle property.
The following example shows how to style the item of UXListBox using ItemsContainerStyle property.
XAML |
Copy Code
|
---|---|
<UserControl.Resources> <Style x:Key="UXListBoxItemStyle1" TargetType="Intersoft:UXListBoxItem"> <Setter Property="Background" Value="Transparent"/> <Setter Property="BorderThickness" Value="0"/> <Setter Property="CornerRadius" Value="0"/> <Setter Property="IsTabStop" Value="False"/> <Setter Property="Padding" Value="3"/> <Setter Property="HorizontalContentAlignment" Value="Left"/> <Setter Property="VerticalContentAlignment" Value="Center"/> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="Intersoft:UXListBoxItem"> <Grid x:Name="RootElement" Background="LightBlue"> <VisualStateManager.VisualStateGroups> <VisualStateGroup x:Name="CommonStates"> <VisualState x:Name="Normal"/> <VisualState x:Name="MouseOver"> <Storyboard> <DoubleAnimation Duration="0" To=".35" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="FillColor"/> </Storyboard> </VisualState> <VisualState x:Name="Disabled"> <Storyboard> <DoubleAnimation Duration="0" To=".55" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="ContentPresenter"/> </Storyboard> </VisualState> </VisualStateGroup> <VisualStateGroup x:Name="SelectionStates"> <VisualState x:Name="Unselected"/> <VisualState x:Name="Selected"> <Storyboard> <DoubleAnimation Duration="0" To=".75" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="FillColor2"/> </Storyboard> </VisualState> </VisualStateGroup> <VisualStateGroup x:Name="FocusStates"> <VisualState x:Name="Focused"> <Storyboard> <ObjectAnimationUsingKeyFrames Duration="0" Storyboard.TargetProperty="Visibility" Storyboard.TargetName="FocusVisualElement"> <DiscreteObjectKeyFrame KeyTime="0"> <DiscreteObjectKeyFrame.Value> <Visibility>Visible</Visibility> </DiscreteObjectKeyFrame.Value> </DiscreteObjectKeyFrame> </ObjectAnimationUsingKeyFrames> </Storyboard> </VisualState> <VisualState x:Name="Unfocused"/> </VisualStateGroup> </VisualStateManager.VisualStateGroups> <Border x:Name="FillColor" Background="Magenta" CornerRadius="1" IsHitTestVisible="False" Opacity="0"/> <Border x:Name="FillColor2" Background="Magenta" CornerRadius="1" IsHitTestVisible="False" Opacity="0"/> <Intersoft:StylishLabel x:Name="ContentPresenter" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" ContentType="{TemplateBinding ContentType}" ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" ContentEffect="{x:Null}" CornerRadius="{TemplateBinding CornerRadius}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" HorizontalContentAlignment="Stretch" IsTabStop="False" ImageMargin="{TemplateBinding ImageMargin}" ImageWidth="{TemplateBinding ImageWidth}" ImageStretch="{TemplateBinding ImageStretch}" ImageSource="{TemplateBinding Icon}" ImageHeight="{TemplateBinding ImageHeight}" Padding="{TemplateBinding Padding}" TextImageRelation="{TemplateBinding TextImageRelation}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" VerticalContentAlignment="Stretch"/> <Border x:Name="FocusVisualElement" BorderBrush="#FF6DBDD1" BorderThickness="1" CornerRadius="1" Visibility="Collapsed"/> </Grid> </ControlTemplate> </Setter.Value> </Setter> </Style> </UserControl.Resources> <Grid x:Name="LayoutRoot" Background="White"> <Intersoft:UXListBox x:Name="ListBox1" HorizontalAlignment="Center" VerticalAlignment="Center" ItemContainerStyle="{StaticResource UXListBoxItemStyle1}"> <Intersoft:UXListBoxItem Content="Item #1"/> <Intersoft:UXListBoxItem Content="Item #2"/> <Intersoft:UXListBoxItem Content="Item #3"/> <Intersoft:UXListBoxItem Content="Item #4"/> </Intersoft:UXListBox> </Grid> |
To cover advanced styling such as creating alternating style between items or highlighting certain items based on certain scenario ClientUI provides ItemContaierStyleSelector property at ItemsControl level.
This property requires an object that inherit from StyleSelector class that will do the custom logic to determine which style will be applied to the specific item.
The following example shows how to use StyleSelector to create alternate item style in UXListBox.
XAML |
Copy Code
|
---|---|
<UserControl xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:Intersoft="http://intersoft.clientui.com/schemas" xmlns:local="clr-namespace:ClientUI.Sample" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" x:Class="ClientUI.Sample.MainPage" Width="800" Height="600"> <UserControl.Resources> <Style x:Key="ItemStyle1" TargetType="Intersoft:UXListBoxItem"> <Setter Property="Background" Value="Gold"/> </Style> <Style x:Key="ItemStyle2" TargetType="Intersoft:UXListBoxItem"> <Setter Property="Background" Value="Silver"/> </Style> <local:AlternateStyleSelector x:Key="AlternateStyleSelector" ItemStyle="{StaticResource ItemStyle1}" AlternateItemStyle="{StaticResource ItemStyle2}"/> </UserControl.Resources> <Grid> <Intersoft:UXListBox x:Name="ListBox1" HorizontalAlignment="Center" VerticalAlignment="Center" ItemContainerStyleSelector="{StaticResource AlternateStyleSelector}"> <Intersoft:UXListBoxItem Content="Item #1"/> <Intersoft:UXListBoxItem Content="Item #2"/> <Intersoft:UXListBoxItem Content="Item #3"/> <Intersoft:UXListBoxItem Content="Item #4"/> </Intersoft:UXListBox> </Grid> </UserControl> |
C# |
Copy Code
|
---|---|
using System.Windows; using Intersoft.Client.Framework; using Intersoft.Client.UI.Aqua.UXCollection; namespace ClientUI.Sample { public class AlternateStyleSelector: StyleSelector { public Style ItemStyle { get; set; } public Style AlternateItemStyle { get; set; } public override Style SelectStyle(object item, DependencyObject container) { UXListBoxItem control = container as UXListBoxItem; if (control != null) { UXListBox owner = control.Owner as UXListBox; if (owner.Items.IndexOf(control) % 2 == 0) return this.ItemStyle; } return this.AlternateItemStyle; } } } |
When the ItemsControl is bound to a collection of object, you can use ItemTemplate to control the look and feel of the content.
The following example shows how to perform templating using ItemTemplate property.
XAML |
Copy Code
|
---|---|
<UserControl.Resources> <DataTemplate x:Key="ItemTemplate1"> <Intersoft:DockPanel FillChildMode="Custom"> <Image Source="{Binding Picture}" Height="64" Width="64" Intersoft:DockPanel.Dock="Left"/> <Grid Intersoft:DockPanel.IsFillElement="True"> <StackPanel VerticalAlignment="Center" Margin="8"> <TextBlock Text="{Binding ContactName}" /> <TextBlock Text="{Binding EmailAddress}" /> </StackPanel> </Grid> </Intersoft:DockPanel> </DataTemplate> </UserControl.Resources> <Grid x:Name="LayoutRoot" Background="White" DataContext="{Binding Source={StaticResource SampleDataSource}}"> <Intersoft:UXListBox HorizontalAlignment="Center" VerticalAlignment="Center" ItemTemplate="{StaticResource ItemTemplate1}" ItemsSource="{Binding Collection}"/> </Grid> |
Similiar with ItemContaierStyleSelector, you can also create a template selector by creating a class that inherit from DataTemplateSelector. This template selector allows you to apply different template on different item based on the logic describes in the template selector.
The following example shows how to use DataTemplateSelector to create alternate item template in UXListBox.
XAML |
Copy Code
|
---|---|
<UserControl xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:Intersoft="http://intersoft.clientui.com/schemas" xmlns:local="clr-namespace:ClientUI.Sample" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" x:Class="ClientUI.Sample" Width="800" Height="600"> <UserControl.Resources> <DataTemplate x:Key="ItemTemplate1"> <Intersoft:DockPanel FillChildMode="Custom" Width="250"> <Image Source="{Binding Picture}" Height="64" Width="64" Intersoft:DockPanel.Dock="Left"/> <Grid Intersoft:DockPanel.IsFillElement="True"> <StackPanel VerticalAlignment="Center" Margin="8"> <TextBlock Text="{Binding ContactName}" /> <TextBlock Text="{Binding EmailAddress}" /> </StackPanel> </Grid> </Intersoft:DockPanel> </DataTemplate> <DataTemplate x:Key="ItemTemplate2"> <Intersoft:DockPanel FillChildMode="Custom" Width="250"> <Image Source="{Binding Picture}" Height="64" Width="64" Intersoft:DockPanel.Dock="Right"/> <Grid Intersoft:DockPanel.IsFillElement="True"> <StackPanel VerticalAlignment="Center" Margin="8"> <TextBlock Text="{Binding ContactName}" /> <TextBlock Text="{Binding EmailAddress}" /> </StackPanel> </Grid> </Intersoft:DockPanel> </DataTemplate> <local:AlternateTemplateSelector x:Key="AlternateTemplateSelector" ItemTemplate="{StaticResource ItemTemplate1}" AlternateItemTemplate="{StaticResource ItemTemplate2}"/> </UserControl.Resources> <Grid x:Name="LayoutRoot" Background="White" DataContext="{Binding Source={StaticResource SampleDataSource}}"> <Intersoft:UXListBox HorizontalAlignment="Center" VerticalAlignment="Center" ItemTemplateSelector="{StaticResource AlternateTemplateSelector}" ItemsSource="{Binding Collection}"/> </Grid> </UserControl> |
C# |
Copy Code
|
---|---|
using System.Windows; using Intersoft.Client.Framework; using Intersoft.Client.UI.Aqua.UXCollection; namespace ClientUI.Sample { public class AlternateTemplateSelector: DataTemplateSelector { public DataTemplate ItemTemplate { get; set; } public DataTemplate AlternateItemTemplate { get; set; } public override DataTemplate SelectTemplate(object item, DependencyObject container) { UXListBoxItem control = container as UXListBoxItem; if (control != null) { UXListBox owner = control.Owner as UXListBox; if (owner.Items.IndexOf(item) % 2 == 0) return this.ItemTemplate; } return this.AlternateItemTemplate; } } } |