Monday, 12 March 2012

Dependency Properties in User Control

In order to be able to expose custom properties that take advantage of xaml data binding, you need to use dependency properties.

In this simple example we create a user control exposing four dependency properties, which will allow our developers to be able to create the following instance:

player-control

Our User Control

class-diagram

For each property you’ll need a dependency property field which gets exposed through a normal .NET property wrapper.  This is to enable Xaml to make use of the data binding system:

PlayerControl.xaml.cs

using System.ComponentModel;
using System.Windows;
using System.Windows.Media;

namespace WpfCustomUserControl
{
public partial class PlayerControl
{
#region Dependency Properties

public static readonly DependencyProperty ShirtNumberProperty =
DependencyProperty.Register("ShirtNumber",
typeof(int),
typeof(PlayerControl),
new PropertyMetadata(0));

[Bindable(true)]
public int ShirtNumber
{
get { return (int)this.GetValue(ShirtNumberProperty); }
set { this.SetValue(ShirtNumberProperty, value); }
}

public static readonly DependencyProperty KitColorProperty =
DependencyProperty.Register("KitColor",
typeof(Brush),
typeof(PlayerControl),
new PropertyMetadata(new SolidColorBrush(Color.FromArgb(0, 0, 0, 0))));

[Bindable(true)]
public Brush KitColor
{
get { return (Brush)this.GetValue(KitColorProperty); }
set { this.SetValue(KitColorProperty, value); }
}

public static readonly DependencyProperty SurnameProperty =
DependencyProperty.Register("Surname",
typeof(string),
typeof(PlayerControl),
new PropertyMetadata("Not set"));

[Bindable(true)]
public string Surname
{
get { return (string)this.GetValue(SurnameProperty); }
set { this.SetValue(SurnameProperty, value); }
}

public static readonly DependencyProperty PositionProperty =
DependencyProperty.Register("Position",
typeof(string),
typeof(PlayerControl),
new PropertyMetadata("Not set"));

[Bindable(true)]
public string Position
{
get { return (string)this.GetValue(PositionProperty); }
set { this.SetValue(PositionProperty, value); }
}

#endregion

public PlayerControl()
{
InitializeComponent();
}
}
}

Note: I've included a bindable attribute to the dependency properties that is not required but I add as a best practice.


The xaml for our user control can then hook up the data binding.


PlayerControl.xaml

<UserControl x:Class="WpfCustomUserControl.PlayerControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
x:Name="CustomPlayer"
Width="120"
Height="90"
mc:Ignorable="d">
<Grid>
<Button>
<StackPanel>
<Ellipse Width="30"
Height="30"
Fill="{Binding Path=KitColor,
ElementName=CustomPlayer,
Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}" />
<TextBlock Margin="0,-25"
HorizontalAlignment="Center"
FontSize="16"
Foreground="White"
Text="{Binding Path=ShirtNumber,
ElementName=CustomPlayer,
Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}" />
<TextBlock Margin="0,10,0,5"
HorizontalAlignment="Center"
FontSize="15"
Text="{Binding Path=Surname,
ElementName=CustomPlayer,
Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}" />

<TextBlock HorizontalAlignment="Center"
Text="{Binding Path=Position,
ElementName=CustomPlayer,
Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}" />
</StackPanel>
</Button>
</Grid>
</UserControl>


Note: No data binding is set on the user control as this will be set at runtime by the developer using the usercontrol. The element name within the usercontrols controls points to itself to enable data binding to work.


We can now easily reuse our user control setting the properties for each instance.


England-team


MainWindow.xaml


<Window x:Class="WpfCustomUserControl.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfCustomUserControl"
Title="England Footy Team"
Icon="Football.ico"
Height="500" Width="600">
<Grid x:Name="mainGrid">
<Grid.RowDefinitions>
<RowDefinition Height="10"/>
<RowDefinition Height="100"/>
<RowDefinition Height="120"/>
<RowDefinition Height="120"/>
<RowDefinition Height="120"/>
<RowDefinition Height="10"/>
</Grid.RowDefinitions>

<Grid.Resources>
<Style TargetType="WrapPanel">
<Setter Property="HorizontalAlignment" Value="Center" />
</Style>
</Grid.Resources>

<WrapPanel Name="panelGoalKeeper" Grid.Column="0" Grid.Row="1">
<local:PlayerControl x:Name="btnGK"
ShirtNumber="1"
KitColor="Green"
Surname="Hart"
Position="Keeper" />
</WrapPanel>

<WrapPanel Name="panelDefenders" Grid.Column="0" Grid.Row="2">
<local:PlayerControl x:Name="btnDF1"
ShirtNumber="2"
KitColor="Crimson"
Surname="Richards"
Position="Strong as an Ox"
Margin="0,25,25,0"/>
<local:PlayerControl x:Name="btnDF2"
ShirtNumber="5"
KitColor="Crimson"
Surname="Cahill"
Position="Ball playing stopper"
Margin="0,0,5,0"/>
<local:PlayerControl x:Name="btnDF3"
ShirtNumber="6"
KitColor="Crimson"
Surname="Jones"
Position="Marauding Sweeper"
Margin="5,0,0,0"/>
<local:PlayerControl x:Name="btnDF4"
ShirtNumber="3"
KitColor="Crimson"
Surname="Baines"
Position="Freekick specialist"
Margin="25,25,0,0"/>

</WrapPanel>

<WrapPanel Name="panelMidfielders" Grid.Column="0" Grid.Row="3">

<local:PlayerControl x:Name="btnMD1"
ShirtNumber="7"
KitColor="Crimson"
Surname="Wilshire"
Position="Playmaker"
Margin="0,25,25,0"/>
<local:PlayerControl x:Name="btnMD2"
ShirtNumber="4"
KitColor="Crimson"
Surname="Parker"
Position="Battler" />
<local:PlayerControl x:Name="btnMD3"
ShirtNumber="8"
KitColor="Crimson"
Surname="Gerrard"
Position="Attacking Midfielder"
Margin="25,25,0,0"/>
</WrapPanel>

<WrapPanel Name="panelStrikers" Grid.Column="0" Grid.Row="4">
<local:PlayerControl x:Name="btnST1"
ShirtNumber="9"
KitColor="Crimson"
Surname="Sturridge"
Position="The Future is bright"
Margin="0,25,25,0"/>
<local:PlayerControl x:Name="btnST2"
ShirtNumber="10"
KitColor="Crimson"
Surname="Rooney"
Position="Number 10" />
<local:PlayerControl x:Name="btnST3"
ShirtNumber="11"
KitColor="Crimson"
Surname="Welbeck"
Position="Striker"
Margin="25,25,0,0"/>
</WrapPanel>
</Grid>
</Window>

Source code:  http://stevenhollidge.com/blog-source-code/WpfCustomUserControl.zip

No comments:

Post a Comment