Category Archives: User Control

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

How to Bind to ListBox.ItemsSource inside UserControl

Summary

The post describes two ways of WPF data binding to elements inside WPF user control. Example shows how to establish WPF user control data binding by:

Complete source code is available for download here.

Problem

While working on my project, I needed identical controls and functionality on several windows. The solution was to create WPF user control and use it in all windows.

Control I created contains ListBox , button, textbox and few other controls (panel, border). Creating this user control was easy – define the layout and add standard WPF controls. Step two is using this custom control in windows and other user controls.

Adding user control to other window or user controls is simple:

  • add a reference to a namespace (and assembly if the user control is not in the same assembly as Window where it will be used)
  • add a control to XAML

So, I added my new user control to the window, and next step is to define binding. Type ItemsSource=”{Binding myList}” and the red squiggly line immediately appeared, notifying me that there is no  ItemsSource in my user control.

Binding to Dependency Property in User Control

My user control is derived from UserControl , which does not have ItemsSource property. The first idea is to create a property on user control that exposes ItemsSource property.  The user control will have dependency property of type IEnumerable for ItemsSource, and listbox will have data binding that binds to this property. Consumers of this user control will then be able to bind to this property in XAML, just like standard listbox.

Binding to DataContext of User Control

While reading through UserControl properties, I noticed that I forgot about DataContext property. Second way can be to assign DataContext to my control and then bind ListBox inside my user control. Consumer will bind list to user control DataContext and inside user control, listbox will bind to that DataContext

User Control Example

I decided to test both of solutions to see which one feels better. For test, I created simple user control that contains two listboxes, one will be bound to DataContext, other to dependency property. Here is the code:


<UserControl x:Class="ListboxInUserControlDemo.ListboxInUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Name="myUserControl"
Height="300" Width="500">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="10"/>
<ColumnDefinition />
</Grid.ColumnDefinitions>
<ListBox ItemsSource="{Binding Path=.}" Grid.Column="0"/>
<ListBox x:Name="listBox2"
ItemsSource="{Binding ElementName=myUserControl, Path=MyItemsSource}"
Grid.Column="2"/>
</Grid>
</UserControl>

In above code, first listbox have ItemsSource bound to current DataContext. Specification Path=. is used to bind to current source (see Binding.Path Property for details). Specifying Path=. is not mandatory, it could be written like this: ItemsSource="{Binding}". I put it here just to remind myself what Path=. actually means.
The second listbox gets ItemsSource from dependency property MyItemsSource. Since ItemsSource is bound to property of this user control, there must be specified element whose property is bound to listbox, so there is: ElementName=myUserControl.

Most of the code behind is code needed to define dependency property:


using System.Collections;
using System.Windows;
using System.Windows.Controls;

namespace ListboxInUserControlDemo
{
/// <summary>
/// Interaction logic for ListboxInUserControl.xaml
/// </summary>
public partial class ListboxInUserControl : UserControl
{
public static readonly DependencyProperty MyItemsSourceProperty;

static ListboxInUserControl()
{
ListboxInUserControl.MyItemsSourceProperty =
DependencyProperty.Register("MyItemsSource",
typeof(IEnumerable), typeof(ListboxInUserControl));
}

public IEnumerable MyItemsSource
{
get
{
return (IEnumerable)GetValue(ListboxInUserControl.MyItemsSourceProperty);
}
set
{
SetValue(ListboxInUserControl.MyItemsSourceProperty, value);
}
}

public ListboxInUserControl()
{
InitializeComponent();
}
}
}

Consuming User Control in Window

Now we have user control defined. Next step is to put it into window and test behavior. To see difference between binding methods, example will contain two copies of the user control. One will bind to DataContext, while second will bind to dependency property. On each copy of user control, one listbox will show data, while other will be empty.


<Window x:Class="ListboxInUserControlDemo.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:lbUserControl="clr-namespace:ListboxInUserControlDemo"
Title="Window1" Height="400" Width="550"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Grid>
<TabControl>
<TabItem Header="DataContext binding" >
<lbUserControl:ListboxInUserControl DataContext="{Binding TestList}"/>
</TabItem>
<TabItem Header="Dependency Property Binding">
<lbUserControl:ListboxInUserControl MyItemsSource="{Binding TestList}"/>
</TabItem>
</TabControl>
</Grid>
</Window>

Window contains TabControl with two TabItems. Each TabItem have one ListboxInUserControl.

There is nothing special in code behind of this window, standard constructor and initialization for TestList property:


using System.Collections.Generic;
using System.Windows;

namespace ListboxInUserControlDemo
{
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
}

List<string> _testList;
public List<string> TestList
{
get
{
if (_testList == null)
InitList();
return _testList;
}
}

private void InitList()
{
_testList = new List<string>();
_testList.Add("Lorem ipsum dolor sit amet,");
_testList.Add("consectetur adipiscing elit. ");
_testList.Add("Duis mollis egestas ornare. ");
_testList.Add("Cras diam est, sollicitudin ");
_testList.Add("quis vulputate sed, lacinia.");
_testList.Add("Phasellus lorem nunc, fringilla eget posuere");
_testList.Add("sed, tincidunt nec felis. Ut non dui ut");
_testList.Add("metus auctor scelerisque quis ornare leo.");
_testList.Add("Integer eget purus tellus. Nullam ut urna dui.");
_testList.Add("Quisque sed enim magna, sed.");
}
}
}


So, let’s see what happens when I run this code.
The first user control on first TabItem binds to DataContext to TestList the property of a window. Left listbox in user control binds to that DataContext and it will show contents of TestList. Right listbox will be empty since there is no binding to the dependency property MyItemsSource.
Second user control binds dependency property MyItemsSource to TestList property of the window but does not bind DataContext. On this tab left listbox will be empty, and right listbox will show contents of TestList.

There it is. Two ways to databind to ListBox inside the user control. This, of course, works for all types of controls.
I prefer databinding with DataContext. Defining DependencyProperty adds lots of code in codebehind but does not save anything when you consume UserControl.

The complete code is available for download here.