Perhaps you have an application
where you need to display many short text strings with borders around
them. You decide you want to derive from UserControl to create a control named BorderedText that you
can use like so:
<petzold:BorderedText Text="Ta Da!"
FontFamily="Times New Roman"
FontSize="96"
FontStyle="Italic"
FontWeight="Bold"
TextDecorations="Underline"
Foreground="Red"
Background="Lime"
BorderBrush="Blue"
BorderThickness="8"
CornerRadius="36"
Padding="16 4"
HorizontalAlignment="Center"
VerticalAlignment="Center" />
As you can see from the XML
namespace prefix, this class is already in the Petzold.Phone.Silverlight
library.
BorderedText derives
from UserControl, and UserControl derives from Control, so we know that BorderedText will
already have some of these properties through class inheritance. The
properties that BorderedText needs to
define on its own are Text, TextDecorations,
CornerRadius, and perhaps a couple
more to make it more flexible.
It seems likely that the
BorderedText.xaml file will have a visual tree consisting of a TextBlock in a Border. Various properties of the TextBlock
and this Border must
be set from the BorderedText
properties.
In the previous chapter, you saw
one way to do this: The ColorColumn class defined properties named Label and Value and it used property-changed handlers in code
to set the new
values of these properties on elements in the visual tree. A rather
simpler way is through data bindings.
The BorderedText code-behind file simply defines all the
properties not available by virtue of descending from Control:
Example 1. Silverlight
Project: Petzold.Phone.Silverlight File: BorderedText.xaml.cs
using System; using System.Windows; using System.Windows.Controls;
namespace Petzold.Phone.Silverlight { public partial class BorderedText : UserControl { public static readonly DependencyProperty TextProperty = DependencyProperty.Register("Text", typeof(string), typeof(BorderedText), new PropertyMetadata(null));
public static readonly DependencyProperty TextAlignmentProperty = DependencyProperty.Register("TextAlignment", typeof(TextAlignment), typeof(BorderedText), new PropertyMetadata(TextAlignment.Left));
public static readonly DependencyProperty TextDecorationsProperty = DependencyProperty.Register("TextDecorations", typeof(TextDecorationCollection), typeof(BorderedText), new PropertyMetadata(null));
public static readonly DependencyProperty TextWrappingProperty = DependencyProperty.Register("TextWrapping", typeof(TextWrapping), typeof(BorderedText), new PropertyMetadata(TextWrapping.NoWrap));
public static readonly DependencyProperty CornerRadiusProperty = DependencyProperty.Register("CornerRadius", typeof(CornerRadius), typeof(BorderedText), new PropertyMetadata(new CornerRadius()));
public BorderedText() { InitializeComponent(); }
public string Text { set { SetValue(TextProperty, value); } get { return (string)GetValue(TextProperty); } }
public TextAlignment TextAlignment { set { SetValue(TextAlignmentProperty, value); } get { return (TextAlignment)GetValue(TextAlignmentProperty); } }
public TextDecorationCollection TextDecorations { set { SetValue(TextDecorationsProperty, value); } get { return (TextDecorationCollection)GetValue(TextDecorationsProperty) ; } }
public TextWrapping TextWrapping { set { SetValue(TextWrappingProperty, value); } get { return (TextWrapping)GetValue(TextWrappingProperty); } }
public CornerRadius CornerRadius { set { SetValue(CornerRadiusProperty, value); } get { return (CornerRadius)GetValue(CornerRadiusProperty); } } } }
|
It’s long but it’s simple
because it’s only property definitions. There are no property-changed
handlers. Here’s the XAML file with the Border
and the TextBlock:
Example 2. Silverlight
Project: Petzold.Phone.Silverlight File: BorderedText.xaml
<UserControl x:Class="Petzold.Phone.Silverlight.BorderedText" 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" Name="this">
<Border Background="{Binding ElementName=this, Path=Background}" BorderBrush="{Binding ElementName=this, Path=BorderBrush}" BorderThickness="{Binding ElementName=this, Path=BorderThickness}" CornerRadius="{Binding ElementName=this, Path=CornerRadius}" Padding="{Binding ElementName=this, Path=Padding}">
<TextBlock Text="{Binding ElementName=this, Path=Text}" TextAlignment="{Binding ElementName=this, Path=TextAlignment}" TextDecorations="{Binding ElementName=this, Path=TextDecorations}" TextWrapping="{Binding ElementName=this, Path=TextWrapping}" /> </Border> </UserControl>
|
Notice that the root
element is given a name:
Name="this"
You can give this root element
any name you want, but it’s traditional to use the C# keyword this, because within
the context of the XAML file, this now refers to the current instance of the BorderedText class,
so it’s a familiar concept. The presence of this name means you can
establish bindings from the properties of BorderedText to the
properties of the elements of that make up its visual tree.
The file doesn’t require bindings for the Foreground property or the various font-related
properties because these are inherited through the visual tree. The one TextBlock property
I was sad about losing in this control is Inlines,
but TextBlock
defines that property as get-only so you can’t define a binding on it.
The BorderedTextDemo
program tests the new control:
Example 3. Silverlight
Project: BorderedTextDemo File: MainPage.xaml (excerpt)
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0"> <petzold:BorderedText Text="Ta Da!" FontFamily="Times New Roman" FontSize="96" FontStyle="Italic" FontWeight="Bold" TextDecorations="Underline" Foreground="Red" Background="Lime" BorderBrush="Blue" BorderThickness="8" CornerRadius="36" Padding="16 4" HorizontalAlignment="Center" VerticalAlignment="Center" /> </Grid>
|