WPF - Styles

A style in WPF is the collection of (dependency) properties that affect the appearance and behavior of a control. For example, the text color inside a TextBox and background of a Button. For our examples, say we’re trying to affect the styles of two textBoxes,

<Grid x:Name="MyGrid">
    <Grid.ColumnDefinitions>
        <ColumnDefinition />
        <ColumnDefinition />
    </Grid.ColumnDefinitions>
                
    <TextBox Grid.Column="0" />
    <TextBox Grid.Column="1" />
</Grid>

Since the textBoxes don’t have a height and width, lets use a named style to set them.
<Grid.Resources>
    <Style x:Key="TextBoxBaseStyle"> <!—- Named style (because we defined an x:Key) -–>
        <!—- Property=”[ClassName].[DependencyProperty]” -–> 
        <Setter Property="Control.Height" Value="30" />
        <Setter Property="Control.Width" Value="60" />
    </Style>
</Grid.Resources>

We need to explicitly apply this style to all the textBoxes,
<!—- Both textboxes need to explicitly set the Style properties --> 
<TextBox Grid.Column="0" Style="{StaticResource TextBoxBaseStyle}" />
<TextBox Grid.Column="1" Style="{StaticResource TextBoxBaseStyle}" />

Now all the textboxes have the same height and width. We can override a style by declaring the property on the control.
<TextBox Grid.Column="0" Style="{StaticResource TextBoxBaseStyle}" />
<!—- Override the Height property -->
<TextBox Grid.Column="1" Height="60" Style="{StaticResource TextBoxBaseStyle}" />

The style we defined (“TextBoxBaseStyle”) can be applied to any type, for example Button,
<Button Grid.Column="2" Style="{StaticResource TextBoxBaseStyle}" />

We can do this because the style implicitly targets Control types. If we want to target a particular type, we can use the TargetType property.
<!—- This style will only target TextBox and TextBox’s derived types -->
<Style x:Key="TextBoxBaseStyle" TargetType="TextBox">
    <Setter Property="Control.Height" Value="30" />
    <Setter Property="Control.Width" Value="60" />
</Style>

If we do this, we’ll no longer be able to apply this style to a Button, but we can apply the style to TextBox derived types. Since the style’s target type is TextBox we can set properties that are specific to a TextBox,
<Style x:Key="TextBoxBaseStyle" TargetType="TextBox">
    <Setter Property="Control.Height" Value="30" />
    <Setter Property="Control.Width" Value="60" />
    <!—- TextBox specific property -–>
    <Setter Property="TextBox.IsReadOnly" Value="True" />
</Style>

Also, we could hook into events via an EventSetter.
<Style x:Key="TextBoxBaseStyle" TargetType="TextBox">
    <Setter Property="Control.Height" Value="30" />
    <Setter Property="Control.Width" Value="60" />
    <Setter Property="TextBox.IsReadOnly" Value="True" />
    <!—- Write any custom code in the handler: void OnMouseEnter(object sender, RoutedEventArgs args) -–>
    <EventSetter Event="MouseEnter" Handler="OnMouseEnter" />
</Style>

We can create a typed style and give all the TextBoxes (in the scope of the style) the same properties. This is the whole point of having styles – to define a style once and have multiple controls share it. To create a typed style we need to get rid of the x:Key,
<Grid.Resources>
    <Style TargetType="TextBox"> <!—- This typed style applies to all textboxes within its scope -–>
                                 <!—- but not TextBox derived types -–> 
        <Setter Property="Control.Height" Value="30" />
        <Setter Property="Control.Width" Value="60" />
        <Setter Property="TextBox.IsReadOnly" Value="True" />
        <EventSetter Event="MouseEnter" Handler="OnMouseEnter" /> 
    </Style>
</Grid.Resources>
<TextBox Grid.Column="0" />
<TextBox Grid.Column="1" Height="60" />

But a typed style only applies to the declared type and not derived types (note the difference with a named type). Styles can also derive from other (named) styles,
<Grid.Resources>
    <Style x:Key="TextBoxBaseStyle">
        <Setter Property="Control.Height" Value="30" />
        <Setter Property="Control.Width" Value="60" />
    </Style>
    <Style x:Key="TextBoxDerivedStyle" TargetType="TextBox" BasedOn="{StaticResource TextBoxBaseStyle}">
        <Setter Property="TextBox.IsReadOnly" Value="True" />
    </Style>
</Grid.Resources>
        
<TextBox Grid.Column="0" Style="{StaticResource TextBoxBaseStyle}"/>
<TextBox Grid.Column="1" Height="60" Style="{StaticResource TextBoxDerivedStyle}"/>

And styles can have their own resources,
<Style x:Key="TextBoxDerivedStyle" TargetType="TextBox" BasedOn="{StaticResource TextBoxBaseStyle}">
    <Style.Resources>
        <SolidColorBrush x:Key="TextBoxBackgroundColor" Color="LightGray" />
    </Style.Resources>
    <Setter Property="TextBox.IsReadOnly" Value="True" />
    <Setter Property="TextBox.Background" Value="{StaticResource TextBoxBackgroundColor}" />
</Style>

Of course we can do all this in code programmatically as shown below,
Style textboxStyle = new Style();
textboxStyle.TargetType = typeof(TextBox);

Setter backgroundSetter = new Setter(TextBox.BackgroundProperty, Brushes.LightGray);            
textboxStyle.Setters.Add(backgroundSetter);

this.MyTextBox.Style = textboxStyle;

OR if the style is already defined in XAML,we can do like this,
this.MyTextBox.Style = (Style)this.MyGrid.FindResource("TextBoxDerivedStyle");

Comments

Popular posts from this blog

Facade Design Pattern

Auto Scroll in Common Controls

Convert typed library (.tlb) to .net assembly?