Tag Archives: Style

WPF Style Inheritance and Horizontal Alignment of WPF Controls in StackPanel

Summary

The post shows how WPF style inheritance can be used to simplify XAML. Code sample examines different ways to align horizontally several different WPF controls in StackPanel using:

  • no style
  • embedding style in controls
  • creating and applying the style to controls
  • using style inheritance

Problem: Aligning controls in StackPanel

Sample control contains a label, several radio buttons and one text box in one line. StackPanel seems like the perfect choice for this. Controls can be stacked vertically or horizontally, one next to the other. In my case, I want StackPanel to arrange the controls horizontally. Quick typing:



<StackPanel Orientation="Horizontal" VerticalAlignment="Top">
<Label>Select delimiter:</Label>
<RadioButton GroupName="Delimiter" Content="Comma (,)" />
<RadioButton GroupName="Delimiter" Content="Semicolon (;)" />
<RadioButton GroupName="Delimiter" Content="Colon (:)" />
<RadioButton GroupName="Delimiter" Content="Other" />
<TextBox Width="20"/>
</StackPanel>


Run and the result was this mess:

WPF controls default vertical alignment.

Adding Vertical Alignment and Margins

By default, all controls are vertically aligned to the top and result is that all controls have a top at the same position. Since heights and baselines are different, aligning control bottoms would not work either. Aligning centers is the only option that can put control contents in line. Vertical alignment can be set directly to each control in XAML by adding:  VerticalAlignment="Center"

Controls are crammed one to the another because default margin is zero. To create space between the controls, the Margin property must be greater than zero.

For more information see this MSDN article: Alignment, Margins, and Padding Overview.

Creating a Style

Vertical aligning to center will be default alignment for the controls in my application, so I would like to have one style definition that will be available to the all controls. First step is creating a style. It can be done in several ways:

Create default style for each type of the controls:


<Window.Resources>
<Style TargetType="RadioButton">
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="Margin" Value="5,0,0,0" />
</Style>
<Style TargetType="Label">
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="Margin" Value="5,0,0,0" />
</Style>
<Style TargetType="TextBox">
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="Margin" Value="5,0,0,0" />
</Style>
</Window.Resources>
<StackPanel Orientation="Horizontal" VerticalAlignment="Top">
<Label>Select delimiter:</Label>
<RadioButton GroupName="Delimiter" Content="Comma (,)" />
<RadioButton GroupName="Delimiter" Content="Semicolon (;)" />
<RadioButton GroupName="Delimiter" Content="Colon (:)" />
<RadioButton GroupName="Delimiter" Content="Other" />
<TextBox Width="20"/>
</StackPanel>

Result:
All WPF controls vertically aligned by center.
This works – controls are now aligned, but it is to much code – same properties are set for each type of controls.

Create named style and assign it to controls

Next step is to create style, give him a name and then assign it to the controls:


<Window.Resources>
<Style x:Key="BaseStyle" TargetType="FrameworkElement">
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="Margin" Value="5,0,0,0" />
</Style>
</Window.Resources>

<Grid>
<StackPanel Margin="10" Orientation="Horizontal" VerticalAlignment="Top">
<Label Style="{StaticResource BaseStyle}">Select delimiter:</Label>
<RadioButton Style="{StaticResource BaseStyle}" GroupName="Delimiter"
Content="Comma (,)" />
<RadioButton Style="{StaticResource BaseStyle}" GroupName="Delimiter"
Content="Semicolon (;)" />
<RadioButton Style="{StaticResource BaseStyle}" GroupName="Delimiter"
Content="Colon (:)" />
<RadioButton Style="{StaticResource BaseStyle}" GroupName="Delimiter"
Content="Other" />
<TextBox Style="{StaticResource BaseStyle}" Width="20"/>
</StackPanel>
</Grid>

This one is worse than the first solution. It has less lines, but assigning style to every control is making code hard to read and adding style to each and every control is pain in the lower back.

Note the target type property: TargetType="FrameworkElement" It is set to FrameworkElement which is the ancestor of all the controls I have on this form. But, be careful because there are WPF controls that are not derived from the FrameworkElement and this code will not work for them.

WPF Style inheritance

Fortunately, WPF styles can be derived from other existing styles. Procedure goes like this:

  • create base style and give it a name
  • create new style and add property: BasedOn="{StaticResource SomeBaseStyle}"

Now, I’m going to create base style for FrameworkElement and then I’ll derive styles for each type of control I have on my form:


<Window.Resources>
<Style x:Key="BaseStyle" TargetType="FrameworkElement">
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="Margin" Value="5,0,0,0" />
</Style>
<Style TargetType="Label" BasedOn="{StaticResource BaseStyle}" />
<Style TargetType="RadioButton" BasedOn="{StaticResource BaseStyle}">
<Setter Property="GroupName" Value="Delimiter" />
</Style>
<Style TargetType="TextBox" BasedOn="{StaticResource BaseStyle}" />
</Window.Resources>

<Grid>
<StackPanel Margin="10" Orientation="Horizontal" VerticalAlignment="Top">
<Label>Select delimiter:</Label>
<RadioButton Content="Comma (,)" />
<RadioButton Content="Semicolon (;)" />
<RadioButton Content="Colon (:)" />
<RadioButton Content="Other" />
<TextBox Width="20"/>
</StackPanel>
</Grid>
</Window>


It works like this:

  • base style defines VerticalAlignment and Margin properties for FrameworkElement (which is ancestor control for other controls in this example)
  • Label and textbox styles inherit from the base style and leave it as is. They are defined here so that we have default styles for the labels and textboxes, so there is no need for assigning style in the controls. This can be done because label and textbox are derived from FrameworkElement
  • Radio button style inherits from the base style, but also sets GroupName property, since it is the same for all RadioButtons on my form

Although this is bit more code compared to solution with one named style, I like it more because XAML looks less clutered to me. Even better, base styles can be put into Application.Resources and reused throughout whole application. More details on using Application.Resources can be found in this MSDN article: How to: Use Application Resources