WPF Composite Control

Over the years I’ve implemented this code in Delphi, C++ and C# with WinForms. I finally had reason to implement it again, but now in WPF. There were a few interesting issues to solve, so I’ve listed them below.

1. I wanted a simple EllipseTextBox control. A textbox with associated “…” button. The XAML for this is simple enough (I’ve excluded all the UserControl namespaces etc.).

<Grid>
   <Grid.ColumnDefinitions>
      <ColumnDefinition Width="*" />
      <ColumnDefinition Width="Auto" />
   </Grid.ColumnDefinitions>
   <TextBox Grid.Column="0" />
   <Button Grid.Column="1" Content="..." />
</Grid>

Whilst this produces a Button next to a TextBox I obviously needed to add code behind to allow me to bind to the Button Command and TextBox Text properties.

2. So in the code behind I needed to write a few lines of code, but as the TextBox already has a Text property and the Button a Command property, I wanted to set up the relevant DependencyProperty to pass the Text and Command through to the TextBox and Button. To do this I added the following.

public partial class EllipseTextBox : UserControl
{
	public static readonly DependencyProperty TextProperty = TextBox.TextProperty.AddOwner(typeof (EllipseTextBox));
	public static readonly DependencyProperty CommandProperty = ButtonBase.CommandProperty.AddOwner(typeof (EllipseTextBox));

	public EllipseTextBox()
	{
		InitializeComponent();
	}

	public string Text
	{
		get { return (string)GetValue(TextProperty); }
		set { SetValue(TextProperty, value); }
	}

	public ICommand Command
	{
		get { return (ICommand)GetValue(CommandProperty); }
		set { SetValue(CommandProperty, value); }
	}
}

Great so now we can have a TextBox and Button displayed along with the code to pass the Command and Text properties through to the Button and TextBox, but this is of little use of the Command is not hooked up directly to the Button in the UserControl and likewise the Text property on the TextBox. So next step is to wire the XAML to the code behind.

3. Thankfully this isn’t difficult (although it’s rather verbose). So if we update the original XAML replacing the TextBox and Button code with the following

<TextBox Grid.Column="0" Text="{Binding Text, RelativeSource={RelativeSource FindAncestor, AncestorType=ControlLibrary:EllipseTextBox, AncestorLevel=1}}" />
<Button Grid.Column="1" Content="..." Width="{Binding ActualHeight, RelativeSource={RelativeSource Self}}" Command="{Binding Command, RelativeSource={RelativeSource FindAncestor, AncestorType=ControlLibrary:EllipseTextBox, AncestorLevel=1}}"/>

Note: Another nice little trick while implementing this was that I wanted the Button to be square – so I’ve got the Button Width binding to the ActualHeight of the Button. Well I thought it was nice :)