Gart
Gart

Reputation: 2336

WPF dynamic layout: how to enforce square proportions (width equals height)?

I'm learning WPF and can't figure out how to enfore my buttons to take a square shape.

Here is my XAML Markup:

<Window x:Class="Example"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Height="368" Width="333">
  <Window.Resources>
    <Style x:Key="ToggleStyle" BasedOn="{StaticResource {x:Type ToggleButton}}"
                            TargetType="{x:Type RadioButton}">
    </Style>
  </Window.Resources>
  <RadioButton Style="{StaticResource ToggleStyle}">
        Very very long text
  </RadioButton>
</Window>

Specifying explicit values for Width and Height attributes seems like a wrong idea - the button should calculate its dimensions based on its contents automagically, but keep its width and height equal. Is this possible?

Upvotes: 38

Views: 30198

Answers (5)

Amo Robb
Amo Robb

Reputation: 850

I found a simpler solution for the case where you want the button to expand and shrink, tipically when its text changes:

   <RadioButton  Style="{StaticResource ToggleStyle2}" 
           HorizontalAlignment="Center" VerticalAlignment="Center"
           HorizontalContentAlignment="Center" VerticalContentAlignment="Center"
           Width="{Binding Path=ActualWidth, ElementName=txtblck_innerText }"
           Height="{Binding Path=ActualWidth, ElementName=txtblck_innerText }">
           
            
           <TextBlock x:Name="txtblck_innerText" 
                 MinWidth="{Binding ActualHeight, RelativeSource={RelativeSource Self}}" >
                Very very long text.........
           </TextBlock>
    </RadioButton>

This solution above applies when the size of the button is imposed by its content (comes from inside) as stated in the question.

However, in case you want a square button whose size is imposed by the place where it is going to be (comes from outside), for instance, the size of the grid cell that contains the button, a solution could be:

     <RadioButton Style="{StaticResource ToggleStyle2}"
          HorizontalAlignment="Center" VerticalAlignment="Center"
          HorizontalContentAlignment="Center" VerticalContentAlignment="Center">
         <Viewbox Stretch="Uniform">
            <Border Width="20" Height="20">
               <Viewbox Stretch="Uniform">  
                  <TextBlock x:Name="txtblck_innerText">
                         Very very long text...........
                  </TextBlock>                       
               </Viewbox>
            </Border>
         </Viewbox>  
     </RadioButton>

Upvotes: 2

Ethan Shoe
Ethan Shoe

Reputation: 574

@Dan Pruzey's answer was good, but did not shrink the square if the window was downsized. What I found to work best was to set the height to auto and the width to that height:

<Button Height="Auto" Width="{Binding ActualHeight, RelativeSource={RelativeSource Self}}"/>

This kept the button a square when sizing my window bigger and smaller. Hope this helps someone.

Upvotes: 5

yvan vander sanden
yvan vander sanden

Reputation: 985

The currently accepted answer cannot grow AND shrink. I think I found a better approach, by defining a custom style with a rectangle (stretched uniformly).

<Style x:Key="SquareButton" TargetType="Button">
    <Setter Property="Foreground" Value="White"/>
    <Setter Property="Background" Value="#3a3a3a" />
    <Setter Property="BorderBrush" Value="#5a5a5a"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type Button}">

                <Border HorizontalAlignment="Center">
                    <Grid>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="auto"/>
                        </Grid.ColumnDefinitions>
                        <Rectangle Stretch="Uniform"
                                         Stroke="{TemplateBinding BorderBrush}"
                                         StrokeThickness="4"
                                         Margin="0" 
                                         Fill="{TemplateBinding Background}" 
                                         Panel.ZIndex="1"
                                         />
                        <ContentPresenter Margin="10" 
                           HorizontalAlignment="Center" 
                           VerticalAlignment="Center" 
                           Panel.ZIndex="2" />
                    </Grid>
                    </Border>

            </ControlTemplate>
        </Setter.Value>
    </Setter>
    <Style.Triggers>
        <Trigger Property="IsMouseOver" Value="True">
            <Setter Property="Background" Value="#4a4a4a"/>
        </Trigger>
    </Style.Triggers>
</Style>

Btw, you can also create a perfect round button this way. Just replace Rectangle with Ellipse.

Upvotes: 5

Dan Puzey
Dan Puzey

Reputation: 34208

Try this - it seems to work in Kaxaml:

<Button 
    MinWidth="{Binding ActualHeight, RelativeSource={RelativeSource Self}}" 
    MinHeight="{Binding ActualWidth, RelativeSource={RelativeSource Self}}">
  Some content
</Button>

(To test, I put a TextBox inside the button, because that's an easy way to change content size without re-parsing the Xaml.)

Edit: sorry, should probably have specified it as a style to match your example:

<Style TargetType="Button" x:Key="SquareButton">
  <Setter Property="MinWidth" Value="{Binding ActualHeight, RelativeSource={RelativeSource Self}}" />
  <Setter Property="MinHeight" Value="{Binding ActualWidth, RelativeSource={RelativeSource Self}}" />
</Style>

Upvotes: 57

n535
n535

Reputation: 5123

I think you want to bind button's width to its height, like this:

<Button Name="myButton"
Width="{Binding ElementName=myButton, Path=Height}"
Height="100">
Button Text
</Button>

Upvotes: 4

Related Questions