How to style a WPF Expander Header? - wpf

I would like to apply a style on a WPF Expander Header. In the following XAML I have an Expander but the style is for all of it not just for the header.
Thanks.
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="640"
>
<StackPanel>
<StackPanel.Resources>
<Style TargetType="Expander">
<Style.Resources>
<LinearGradientBrush x:Key="BackBrush" StartPoint="0.5,0" EndPoint="0.5,1">
<GradientStop Color="#EF3132" Offset="0.1" />
<GradientStop Color="#D62B2B" Offset="0.9" />
</LinearGradientBrush>
</Style.Resources>
<Setter Property="Background" Value="{StaticResource BackBrush}"/>
</Style>
</StackPanel.Resources>
<Expander>
<StackPanel>
<TextBlock>Bike</TextBlock>
<TextBlock>Car</TextBlock>
<TextBlock>Truck</TextBlock>
</StackPanel>
</Expander>
</StackPanel>
</Page>

I have combined some XAML from Josh Smith and MSDN and came up with a solution. Indeed, the control (al least the header) must be retemplated.
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="400">
<StackPanel>
<StackPanel.Resources>
<Style TargetType="Border" x:Key="RacePitBorderStyle" >
<Style.Resources>
<LinearGradientBrush x:Key="BackBrush" StartPoint="0.5,0" EndPoint="0.5,1">
<GradientStop Color="#EF3132" Offset="0.1" />
<GradientStop Color="#D62B2B" Offset="0.9" />
</LinearGradientBrush>
</Style.Resources>
<Setter Property="Background" Value="{StaticResource BackBrush}"/>
</Style>
<DataTemplate x:Key="titleText">
<Border Style="{StaticResource RacePitBorderStyle}" Height="24">
<TextBlock Text="{Binding}"
Margin="4 0"
VerticalAlignment="Center"
Foreground="White"
FontSize="11"
FontWeight="Normal"
Width="{Binding
RelativeSource={RelativeSource
Mode=FindAncestor,
AncestorType={x:Type Expander}},
Path=ActualWidth}"
TextWrapping="Wrap"/>
</Border>
</DataTemplate>
<Style TargetType="{x:Type Expander}">
<Setter Property="HeaderTemplate" Value="{StaticResource titleText}"/>
</Style>
</StackPanel.Resources>
<Expander Name="hcontCtrl" Header="This is the header.">
<StackPanel>
<TextBox>This is a textbox</TextBox>
<Button>A button</Button>
</StackPanel>
</Expander>
</StackPanel>
</Page>

I think Vasile's answer is on the right track, but it seems like it does a lot more than the original poster needed. All the original question was asking to do was to change the background of the header. While the change presented does do that, it also does other things.
One of these other things is to replace the default implementation, I believe a ContentPresenter, with a TextBlock. So what happens when later we change our Expander so that the header is more complicated? Maybe something like:
<Expander>
<Expander.Header>
<StackPanel>
<Border height="5" width="5" Foreground="Blue"/>
<TextBlock>Ha!</TextBlock>
</StackPanel>
</Expander.Header>
</Expander>
I don't know, but it's not good. Instead, I think we want to keep this simple.
<DataTemplate x:Key="expanderHeader">
<ContentPresenter
Content={Binding}
TextBlock.Background={StaticResource myBrush}/>
</DataTemplate>
<Style TargetType="Expander">
<Setter Property="HeaderTemplate" Value="{StaticResource expanderHeader}"/>
</Style>
That way when someone puts something that is not just text in our styled expander, we don't break. If you want to make sure you wrap the entirety of what they do with this background, which is probably desired, that would look like:
<DataTemplate x:Key="expanderHeader">
<Border Background={StaticResource myBrush}>
<ContentPresenter Content={Binding}/>
</Border>
</DataTemplate>

Depends what you want to style -- you can style any part of it. If you want to change the content in the header, just place all your UI in the Expander.Header property, and it'll show in the header area.
if that does't meet your needs, you probably need to re-template the control. Take a look at the control templates shipped in WPF here

Related

Is it possible to change properties of a resource instantiated with StaticResource element in XAML?

With this piece of XAML:
<Window.Resources>
<Rectangle x:Key="rectangle" x:Shared="False" Width="20" Height="8" Fill="Red" />
</Window.Resources>
<StaticResource x:Name="r1" ResourceKey="rectangle" />
<StaticResource x:Name="r2" ResourceKey="rectangle" />
it is possible to assign a value to say, Margin property, to each instance independently by code:
r1.Margin = 2;
r2.Margin = 5;
Is it possible to do it directly in XAML? I tried:
<StaticResource ResourceKey="rectangle" Margin="3"/>
but Margin is not a property of StaticResource...
Rephrasing after the XY problem sensor fired (appropriately)!
I want to draw rectangles with exactly the same properties except one, e.g. the margin or the color, in order to be able to change the shared properties centrally and still be able to provide specific properties in XAML. Can I use a resource like in my attempt?
Adding my exact need and code as suggested by comments
My exact need is to show the effect of setting different properties to some rectangle, i.e. changing Rectangle.RenderTransformOrigin and Rectangle.RenderTransform to compare effects. It's indeed to learn WPF, not for a production application. At the moment, I use a style (rotated) as I wasn't able to use a resource (this is the reason of my question above).
<Window x:Class="Test.MainWindow"
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"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Test"
mc:Ignorable="d"
Title="Transform Center" Height="400" Width="600">
<Window.Resources>
<Style x:Key="title" TargetType="TextBlock">
<Setter Property="HorizontalAlignment" Value="Center" />
<Setter Property="VerticalAlignment" Value="Bottom" />
<Setter Property="Margin" Value="0,0,0,10" />
</Style>
<Style x:Key="rotated" TargetType="Rectangle">
<Setter Property="Width" Value="201" />
<Setter Property="Height" Value="81" />
<Setter Property="Fill" Value="CadetBlue"/>
<Setter Property="HorizontalAlignment" Value="Left"/>
</Style>
<Style x:Key="fixed" TargetType="Rectangle">
<Setter Property="Width" Value="30" />
<Setter Property="Height" Value="30" />
<Setter Property="Fill" Value="Indigo" />
<Setter Property="HorizontalAlignment" Value="Left"/>
</Style>
</Window.Resources>
<Grid >
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<StackPanel Grid.Row="0" Grid.Column="0" Background="Beige" Margin="5">
<Rectangle Style="{StaticResource fixed}" />
<Rectangle Style="{StaticResource rotated}" />
</StackPanel>
<TextBlock Grid.Row="0" Grid.Column="0"
Style="{StaticResource title}" Text="No rotation" />
<StackPanel Grid.Row="0" Grid.Column="1" Background="Beige" Margin="5">
<Rectangle Style="{StaticResource fixed}" />
<Rectangle Style="{StaticResource rotated}">
<Rectangle.RenderTransformOrigin>.5,.5</Rectangle.RenderTransformOrigin>
<Rectangle.RenderTransform>
<RotateTransform Angle="20" />
</Rectangle.RenderTransform>
</Rectangle>
</StackPanel>
<TextBlock Grid.Row="0" Grid.Column="1"
Style="{StaticResource title}"
Text="RenderTransformOrigin" />
<StackPanel Grid.Row="1" Grid.Column="0" Background="Beige" Margin="5">
<Rectangle Style="{StaticResource fixed}" />
<Rectangle Style="{StaticResource rotated}">
<Rectangle.RenderTransform>
<RotateTransform Angle="20" CenterX="100" CenterY="40" />
</Rectangle.RenderTransform>
</Rectangle>
</StackPanel>
<TextBlock Grid.Row="1" Grid.Column="0"
Style="{StaticResource title}"
Text="RotateTransform Center" />
<!-- The center coordinates relative to the Rectangle are the sum
of both center coordinates, i.e. .5 + .5 = 1 (bottom-right corner) -->
<StackPanel Grid.Row="1" Grid.Column="1" Background="Beige" Margin="5">
<Rectangle Style="{StaticResource fixed}" />
<Rectangle Style="{StaticResource rotated}">
<Rectangle.RenderTransformOrigin>.5,.5</Rectangle.RenderTransformOrigin>
<Rectangle.RenderTransform>
<RotateTransform Angle="20" CenterX="100" CenterY="40" />
</Rectangle.RenderTransform>
</Rectangle>
</StackPanel>
<TextBlock Grid.Row="1" Grid.Column="1"
Style="{StaticResource title}"
Text="Both" />
</Grid>
</Window>
Does XAML allows to change a property of a resource when it is instantiated with <StaticResource>
Short answer: No.
The StaticResource markup extension simply references a resource based on a key. It can't change any properties of the resolved resource. You will have to set the properties of the resolved resource itself by for example casting the target property of the resource to a Rectangle.
I want to draw rectangles with exactly the same properties except one, e.g. the margin or the color
That sounds like a style to me. In WPF, there are usually several different ways to accomplish the same thing, and this is no exception.
it seems setting up a view-model is a bit oversized
Maybe. Maybe not. It's hard to say, as there aren't any other details in your question. That said, given the problem statement above, I'd agree it's certainly not necessary, and absent any other requirements, I don't see anything to be gained by using view models.
Even so, here's a code example that shows three different ways, two of which are based on view models and templates:
<Window x:Class="TestSO58683029RectStyle.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:p="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"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:l="clr-namespace:TestSO58683029RectStyle"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.DataContext>
<l:MainViewModel>
<l:MainViewModel.Rectangle1>
<l:RectangleViewModel Margin="2"/>
</l:MainViewModel.Rectangle1>
<l:MainViewModel.Rectangle2>
<l:RectangleViewModel Margin="5"/>
</l:MainViewModel.Rectangle2>
</l:MainViewModel>
</Window.DataContext>
<Window.Resources>
<p:Style TargetType="Rectangle">
<Setter Property="Width" Value="20"/>
<Setter Property="Height" Value="8"/>
<Setter Property="Fill" Value="Red"/>
<Setter Property="HorizontalAlignment" Value="Left"/>
</p:Style>
<DataTemplate DataType="{x:Type l:RectangleViewModel}">
<Rectangle Width="20" Height="8" Fill="Red" HorizontalAlignment="Left" Margin="{Binding Margin}"/>
</DataTemplate>
<x:Array x:Key="rectangleArray1" Type="{x:Type l:RectangleViewModel}">
<l:RectangleViewModel Margin="2"/>
<l:RectangleViewModel Margin="5"/>
</x:Array>
</Window.Resources>
<StackPanel>
<!-- Uses style -->
<Rectangle/>
<Rectangle Margin="2"/>
<Rectangle Margin="5"/>
<!-- Uses view models, individual properties -->
<ContentControl Content="{Binding Rectangle1}"/>
<ContentControl Content="{Binding Rectangle2}"/>
<!-- Uses view models, collection -->
<ItemsControl ItemsSource="{StaticResource rectangleArray1}"/>
</StackPanel>
</Window>
The view model approaches rely on these classes (they don't implement INotifyPropertyChanged, because there's no need in this simple example):
class RectangleViewModel
{
public Thickness Margin { get; set; }
}
class MainViewModel
{
public RectangleViewModel Rectangle1 { get; set; }
public RectangleViewModel Rectangle2 { get; set; }
}
As you can see, in the case of the style-based approach, a single <Style/> element in the resource dictionary can be used to define the default values for any properties you like. Then you can explicitly use a <Rectangle/> element where you want it in the content of your window, setting any other property explicitly. You can even override properties that were set in the style, if that's needed for any reason.
Note that in the above, the data template explicitly sets its property values. But you can actually combine the two techniques, by referring to the style resource when you declare the template, like so:
<DataTemplate DataType="{x:Type l:RectangleViewModel}">
<Rectangle Margin="{Binding Margin}" Style="{StaticResource ResourceKey={x:Type Rectangle}}"/>
</DataTemplate>
As noted in the comments, declaring concrete UI elements as resources is generally the wrong way to do something in WPF. I hesitate to say it's always wrong, but I would say it's almost always wrong. Concrete UI elements should be declared as actual content in the XAML, with styles used to provide default formatting for those elements. Otherwise you should be using templates, and allow WPF to create the UI elements as needed, based on the template you provide.

Need multiple styles dependent on Listboxitem

i have a Listbox, which stores two different object types, based on the same baseclass. (e.g. BaseObject = baseclass and the children of it: CustomPath and CustomImage)
The Datasource:
ObservableCollection<BattlegroundBaseObject> _baseObjectCollection;
public ObservableCollection<BattlegroundBaseObject> BaseObjectCollection
{
get { return _baseObjectCollection?? (_baseObjectCollection= new ObservableCollection<BaseObject>()); }
}
The Listbox databinding: <ListBox ItemsSource="{Binding BaseObjectCollection}"
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem" x:Name="ListBoxPathLineStyle">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem" x:Name="BattlegroundObjectControlTemplate">
<Path Stroke="{Binding ObjectColor}" StrokeThickness="{Binding StrokeThickness}" Data="{Binding PathGeometryData}" x:Name="PathLine" Opacity="{Binding Opacity}">
</Path>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="true">
<Setter Property="Effect" TargetName="PathLine">
<Setter.Value>
<DropShadowEffect Color="CornflowerBlue" ShadowDepth="3" BlurRadius="10" />
</Setter.Value>
</Setter>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListBox.ItemContainerStyle>
I want to add to the ControlTemplate where the Path is, also a Image and to differ it by type or a property. doesnt matter.
anyone any ideas?
You can add to ListBox resources DataTemplate for each type.
In my example classes Car and Motorbike derived from Vehicle class.
<ListBox x:Name="listBox">
<ListBox.Resources>
<DataTemplate DataType="{x:Type local:Car}">
<StackPanel Background="Red">
<TextBlock Text="{Binding Name}" />
</StackPanel>
</DataTemplate>
<DataTemplate DataType="{x:Type local:Motorbike}">
<StackPanel Background="Orange">
<TextBlock Text="{Binding Name}" />
</StackPanel>
</DataTemplate>
</ListBox.Resources>
</ListBox>
EDIT:
You can add style for ListBoxItem to resources:
<ListBox x:Name="listBox">
<ListBox.Resources>
<Style TargetType="ListBoxItem">
<Style.Triggers>
<Trigger Property="IsSelected" Value="true">
<Setter Property="Effect">
<Setter.Value>
<DropShadowEffect Color="CornflowerBlue" ShadowDepth="3" BlurRadius="10" />
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
<DataTemplate DataType="{x:Type local:Car}">
<StackPanel Background="Red">
<TextBlock Text="{Binding Name}" />
</StackPanel>
</DataTemplate>
<DataTemplate DataType="{x:Type local:Motorbike}">
<StackPanel Background="Orange">
<TextBlock Text="{Binding Name}" />
</StackPanel>
</DataTemplate>
</ListBox.Resources>
</ListBox>
You could define some DataTemplates for your different classes. They determine how the classes are displayed. I've been using them to display derived classes differently when working with a collection of the base class.
<DataTemplate DataType="{x:Type CustomPath}">
<TextBlock Text="This is a CustomPath"/>
</DataTemplate>
<DataTemplate DataType="{x:Type CustomImage}">
<TextBlock Text="This is a CustomImage"/>
</DataTemplate>
At the moment you are changing the style of the controls that WPF is using to render your bound data. A better way to do this is to provide WPF with a way of generating the correct controls. Ignore the ListBoxItem and use DataTemplates for your actual objects.
First you need to tell the Window or control how to find your types.
<Window or UserControl
...
xmlns:model="clr-namespace:yourNamespace"
>
Then you can provide WPF with a way to show your objects e.g.
<DataTemplate TargetType="{x:Type model:CustomPath}">
<Path Stroke="{Binding ObjectColor}" StrokeThickness="{Binding StrokeThickness}"
Data="{Binding PathGeometryData}" x:Name="PathLine" Opacity="{Binding Opacity}"/>
<!-- maybe use a binding from the Path.Effect back to the IsSelected and ValueConverters
to re-apply the selection effect-->
</DataTemplate>
<DataTemplate TargetType="{x:Type model:CustomImage}">
<Image Src="{Binding SomeProperty}" />
</DataTemplate>
Now all you need to do is to make these available to the ListBox in some way. Almost every element in WPF can have .Resources added to it, so you could choose to do these across the entire window
<Window ...>
<Window.Resources>
<DataTemplate .../>
<DataTemplate .../>
</Window.Resources>
...
<ListBox .../>
</Window>
or you can apply it more locally
<Window ...>
...
<ListBox>
<ListBox.Resources>
<DataTemplate .../>
<DataTemplate .../>
</ListBox.Resources>
</ListBox>
</Window>
And this way your listbox definition can become much neater too, e.g. if you are using Window.Resources
<ListBox ItemsSource="{Binding BaseObjectCollection}"/>

ListViewItem custom template: ContentPresenter stays empty

I have the following ListView in my code. views:GameCard is a custom UserControl and {Binding} is a valid DataContext object with three items. Without the custom ItemContainerStyle everything works perfectly — the list shows three GameCards with correct info, etc. As soon as I add the ItemContainerStyle part, I get nothing but three "ABCD"s; so the data is still loaded correctly, but my UserControl is no longer displayed (I only added the "ABCD" to check if the data was there, as otherwise I got nothing but empty box).
Every piece of info I could find online seems to indicate that just putting a ContentPresenter element in the template should work, but it doesn't seem to in this case. What am I missing?
<ListView Grid.Row="1" ItemsSource="{Binding}" BorderThickness="0,0,1,0"
ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<ListView.Background>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#FF614B4B" Offset="0"/>
<GradientStop Color="#FFDA7070" Offset="1"/>
</LinearGradientBrush>
</ListView.Background>
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemTemplate>
<DataTemplate>
<views:GameCard />
</DataTemplate>
</ListView.ItemTemplate>
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Grid>
<TextBlock Text="ABCD" />
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListView.ItemContainerStyle>
</ListView>
You need to set the TargetType of your ControlTemplate. And in order to make your ItemTemplate work, you'd also need to bind the Content and ContentTemplate properties.
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListViewItem">
<Grid>
....
<ContentPresenter
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
... />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListView.ItemContainerStyle>
This may not be the case with you but so far I haven't had to modify the ItemContainerStyle yet, just the ListView.View. Since you're putting a Grid in your template style I'd assume you're looking for a GridView and this is how you do that:
<ListView.View>
<GridView>
<GridViewColumn Width="120">
<GridViewColumnHeader Height="14" >
<TextBlock Text="Type" FontSize="9"/>
</GridViewColumnHeader>
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Name, FallbackValue=MISSING}" />
<!-- or content presenter with bindings here -->
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
...
your style and everything is fine .All what required is to Assign or bind the Content property to your ContentPresenter. I hope this will help.

Button issue in silverlight windows phone 7

I am new in windows phone 7.
I am doing a sample for making a rounded corner button in silverlight for windows phone 7.
I achieved the rounded corners. But it is not showing the content of the button.
Whats wrong in my code ?.
Please help me.
now my button looks like this. No text in it.
I posted my code below.
Thanks.
MainPage.xaml
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<StackPanel VerticalAlignment="Center">
<Button Width="200" Height="80" Content="Click Me" Style="{StaticResource myCustomButtonStyle}" >
</Button>
</StackPanel>
</Grid>
ButtonResourceDictionary.xaml
<LinearGradientBrush x:Key="myCustomButtonColor" EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#FF296180" Offset="0.589"/>
<GradientStop Color="#FF5BC0F3"/>
</LinearGradientBrush>
<Style x:Name="myCustomButtonStyle" TargetType="Button">
<Setter Property="BorderThickness" Value="0"></Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border x:Name="buttonBorder" Background="{StaticResource myCustomButtonColor}" CornerRadius="12"></Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
You need to add a 'ContentPresenter' element into your template to display the content

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?
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>
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.
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>
#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.
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>

Resources