How do I Bind a WPF Ellipse's Height to its own Width?
Asked Answered
P

4

5

I have an ellipse drawn inside a Grid.Row and Grid.Column. The Row is always taller than the Column is wide.

I want to draw an ellipse that fills the width of the grid column and who's height makes it a perfect circle.

I also want to draw a single digit number exactly in the center of the above ellipse. Basically ending up with a circle that has a number centered inside it.

I can easily set HorizontalAlignment="Stretch" on my Ellipse and on the TextBlock containing the number. This takes care of the width for me. However, how do I get the Height of the Ellipse and TextBlock to always match its Width, even when the Grid.Column width changes?

Here's some XAML to illustrate this. In the XAML below, I want the hard coded '63' to be based on the Grid.Column width, or the Width feild of the ellipse:

    <Ellipse Grid.Row="1" Grid.Column="0" HorizontalAlignment="Stretch" Height="63" Stroke="Black" StrokeThickness="3" VerticalAlignment="Top"/>
    <TextBlock Grid.Row="1" Grid.Column="0" Width="63" Height="63" VerticalAlignment="Top" Text="1" TextAlignment="Center" FontSize="42" FontWeight="Bold"/>

Thanks for all your help. I ended up using Herdo's answer. Just set HorizontalAlignment to Stretch and then bind the height to the actual width of the ellipse. I did this same thing to the ellipse and to the text block:

    <Ellipse HorizontalAlignment="Stretch" Height="{Binding ActualWidth, RelativeSource={RelativeSource Self}}"/>
    <TextBlock HorizontalAlignment="Stretch" Height="{Binding ActualWidth, RelativeSource={RelativeSource Self}}"/>
Pounce answered 19/3, 2015 at 20:10 Comment(1)
Putting the Ellipse and the TextBlock into a common Grid would also solve the text centering problem.Glynn
H
18

Assuming you have a Grid containing the Ellipse, you can use the ActualWidth property, and keeping it stretched, without setting the Width property - allowing you to use the Ellipse without any reference to the parent container:

<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="*"/> <!-- Or whatever width -->
    </Grid.ColumnDefinitions>

    <Ellipse Grid.Column="0"
             HorizontalAlignment="Stretch"
             Height="{Binding ActualWidth, RelativeSource={RelativeSource Self}}"/>
</Grid>

Please take into account the remarks for the ActualWidth property on MSDN:

Because ActualWidth is a calculated value, you should be aware that there could be multiple or incremental reported changes to it as a result of various operations by the layout system. The layout system may be calculating required measure space for child elements, constraints by the parent element, and so on.

Therefore, the following example code...

<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="*"/>
        <ColumnDefinition Width="30"/>
        <ColumnDefinition Width="*"/>
    </Grid.ColumnDefinitions>
    <Grid.RowDefinitions>
        <RowDefinition Height="*"/>
        <RowDefinition Height="*"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>

    <Ellipse Grid.Row="1"
             Grid.Column="1"
             HorizontalAlignment="Stretch"
             Fill="Red"
             Height="{Binding ActualWidth, RelativeSource={RelativeSource Self}}"/>
</Grid>

...will result into this behavior (the width/height would update every time, the container - the Grid in this case - changes its layout):

enter image description here

Headachy answered 19/3, 2015 at 20:34 Comment(0)
G
4

You don't need any bindings. Put a Path with a circular EllipseGeometry and Stretch="Uniform" into a common inner Grid, which you then place into your outer Grid.

<Grid>
    <Grid.ColumnDefinitions>
        ...
    </Grid.ColumnDefinitions>
    <Grid.RowDefinitions>
        ...
    </Grid.RowDefinitions>
    <Grid Grid.Row="1" Grid.Column="0" VerticalAlignment="Top">
        <Path Stretch="Uniform" Stroke="Black" StrokeThickness="3">
            <Path.Data>
                <EllipseGeometry RadiusX="1" RadiusY="1"/>
            </Path.Data>
        </Path>
        <TextBlock VerticalAlignment="Center" HorizontalAlignment="Center"
                   FontSize="42" FontWeight="Bold" Text="1"/>
    </Grid>
</Grid>
Glynn answered 19/3, 2015 at 21:31 Comment(0)
T
2

You have to bind the Height of the Ellipse to the grid column width like this :

<Grid x:Name="MyGrid">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="60"/>
            <ColumnDefinition Width="70"/>
            <ColumnDefinition Width="80"/>
        </Grid.ColumnDefinitions>

        <Ellipse Grid.Row="1" Grid.Column="0" HorizontalAlignment="Stretch" Height="{Binding ElementName=MyGrid, Path=ColumnDefinitions[0].Width.Value}" Stroke="Black" StrokeThickness="3" VerticalAlignment="Top"/>
    </Grid>
Tyrr answered 19/3, 2015 at 20:31 Comment(0)
S
1

I have skipped unimportant attributes

<Ellipse x:Name="El" Width="50" Height="{Binding ElementName=El,Path=ActualWidth}"/>
<TextBlock Height="{Binding ElementName=El,Path=ActualWidth}"/>
Sycosis answered 19/3, 2015 at 20:16 Comment(2)
This works, but the Width is hard coded to "50". I have this Ellipse and the TextBlock inside a column in a grid, and the column changes size based on the width of the window. It can probably fluctuate between 30-70 depending on how much the main app window is stretched by the user. I need the Ellipse Width to be based on the width of the grid.column that contains it.Pounce
so, just remove hardcoded width and set horizontalalignment=stretchSycosis

© 2022 - 2024 — McMap. All rights reserved.