This is where TemplateBinding comes in (TemplateBinding is used inside control templates and is used to retrieve values from the templated control, in this case the Button).
<Ellipse Fill="LightGreen"
Width="{TemplateBinding ActualWidth}" Height="{TemplateBinding ActualHeight}"/>
Note that this is a shorter form of using:
{Binding ActualWidth, RelativeSource={RelativeSource TemplatedParent}}
The TemplateBinding markup extension is just optimized for only TemplatedParent bindings.
That said, if you wanted it to be a circle only, if your ellipse was the smaller of W/H, then your content will easily flow out of it, which I doubt is what you actually want..? I had thought of using a multi value converter to do that, but you can't bind to the converter parameter, so that's out.
In that case, an attached behavior would work, but it's not pretty.
<Window x:Class="WpfApplication1.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:c="clr-namespace:WpfApplication1"
xmlns:local="clr-namespace:WpfApplication1"
Title="Window1" Height="300" Width="300">
<Grid>
<Button Content="Yo!" Width="50" Height="30">
<Button.Template>
<ControlTemplate TargetType="Button">
<Grid>
<Ellipse Fill="LightGreen" local:ConstrainWidthHeight.ConstrainedWidth="{TemplateBinding ActualWidth}" local:ConstrainWidthHeight.ConstrainedHeight="{TemplateBinding ActualHeight}"/>
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
</ControlTemplate>
</Button.Template>
</Button>
</Grid>
</Window>
...and the attached behavior:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
namespace WpfApplication1 {
public class ConstrainWidthHeight {
public static readonly DependencyProperty ConstrainedWidthProperty =
DependencyProperty.RegisterAttached( "ConstrainedWidth", typeof( double ), typeof( ConstrainWidthHeight ), new PropertyMetadata( double.NaN, OnConstrainValuesChanged ) );
public static readonly DependencyProperty ConstrainedHeightProperty =
DependencyProperty.RegisterAttached( "ConstrainedHeight", typeof( double ), typeof( ConstrainWidthHeight ), new UIPropertyMetadata( double.NaN, OnConstrainValuesChanged ) );
public static double GetConstrainedHeight( FrameworkElement obj ) {
return (double) obj.GetValue( ConstrainedHeightProperty );
}
public static void SetConstrainedHeight( FrameworkElement obj, double value ) {
obj.SetValue( ConstrainedHeightProperty, value );
}
public static double GetConstrainedWidth( FrameworkElement obj ) {
return (double) obj.GetValue( ConstrainedWidthProperty );
}
public static void SetConstrainedWidth( FrameworkElement obj, double value ) {
obj.SetValue( ConstrainedWidthProperty, value );
}
private static void OnConstrainValuesChanged( object sender, DependencyPropertyChangedEventArgs e ) {
FrameworkElement element = sender as FrameworkElement;
if( element != null ) {
double width = GetConstrainedWidth( element );
double height = GetConstrainedHeight( element );
if( width != double.NaN && height != double.NaN ) {
double value = Math.Min( width, height );
element.Width = value;
element.Height = value;
}
}
}
}
}
Okay, now the reason why using an attached behavior is required (AFAICT anyway), is that in order to center the ellipse (in a non-square/non-circle scenario), you need the HorizontalAlignment and VerticalAlignment to be able to take effect. The default value of both is Stretch, and when an explicit Width/Height is set, it behaves like Center.
With Stretch="Uniform" on, your Ellipse will always physically take up the whole space, it's only the drawing of the Ellipse that will be constrained. Using this, your drawn Ellipse figure will always start at the top left. So in this case if your button is Wider than it is tall, the drawn portion of the Ellipse won't get centered along with the text.
This code is a good example of what you are probably not looking for:
<Ellipse Height="{TemplateBinding ActualHeight}" Width="{TemplateBinding ActualWidth}" Fill="LightGreen" Stretch="Uniform" />
...and the button using it (with a non-square width/height):
<Button Content="YO!" Style="{StaticResource Button2}" Width="120" Height="53" VerticalAlignment="Top"></Button>
Looks like this:
... compared to this with the attached property option: