56

I want to have a Button that defines no CornerRadius and two others that do, how can I achieve this?

<Style TargetType="Button" x:Key="TabButton">
    <Setter Property="Background" Value="White" />
    <Setter Property="TextBlock.TextAlignment" Value="Center" />
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="Button">
                <Border CornerRadius="0" Background="White" BorderBrush="#ccc" BorderThickness="0,1,1,0" >
                    <ContentPresenter x:Name="contentPresenter" ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
                </Border>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

<Style TargetType="Button" x:Key="TabButtonFirst" BasedOn="{StaticResource TabButton}">
    <Setter Property="CornerRadius" Value="3,0,0,0" />
</Style>

<Style TargetType="Button" x:Key="TabButtonLast" BasedOn="{StaticResource TabButton}">
    <Setter Property="CornerRadius" Value="0,0,0,3" />
</Style>
Chris
  • 26,744
  • 48
  • 193
  • 345
  • 2
    Button does not have the CornerRadius property. Set it in your ControlTemplate for your Border control. – Nitesh Jul 16 '13 at 15:43
  • 4
    You will need two styles for the Button to achieve what you are doing or create a Custom Button to implement CornerRadius as DependencyProperty, and bind it with CornerRadius of Border in ControlTemplate. – Nitesh Jul 16 '13 at 15:47

8 Answers8

64

You're not limited to the dependency properties of the control you're templating. In this case, while Button does not have a CornerRadius property, Border does, so you can use Border.CornerRadius instead:

<Style TargetType="Button" x:Key="TabButton">
    <Setter Property="Background" Value="White" />
    <Setter Property="TextBlock.TextAlignment" Value="Center" />
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="Button">
                <Border CornerRadius="{TemplateBinding Border.CornerRadius}" 
                        Background="White" BorderBrush="#ccc" 
                        BorderThickness="0,1,1,0" >
                    <ContentPresenter x:Name="contentPresenter" 
                                      ContentTemplate="{TemplateBinding ContentTemplate}" 
                                      Content="{TemplateBinding Content}" 
                                      HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                                      Margin="{TemplateBinding Padding}" 
                                      VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
                </Border>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

<Style TargetType="Button" x:Key="TabButtonFirst" BasedOn="{StaticResource TabButton}">
    <Setter Property="Border.CornerRadius" Value="3,0,0,0" />
</Style>

<Style TargetType="Button" x:Key="TabButtonLast" BasedOn="{StaticResource TabButton}">
    <Setter Property="Border.CornerRadius" Value="0,0,0,3" />
</Style>

With this approach, you no longer need to maintain multiple copies of your control template.

Pieter Witvoet
  • 2,773
  • 1
  • 22
  • 33
  • Amazing answer! – mattyb Dec 15 '16 at 19:15
  • 4
    I know we don't like +1 comments, but THIS is the real answer most people should be looking for in the situations which are leading them to this question. – MojoFilter Jul 07 '17 at 14:16
  • @Pieter Witvoet : How can I bind the Value of CornerRadius? Because the Button has no. – GreenEyedAndy Apr 10 '19 at 12:14
  • @GreenEyedAndy: binding to attached properties requires a static `SetPropertyName` method. There's no such method for `CornerRadius` (it wasn't designed to be used as an attached property), but you can provide your own method, or create an attached property of your own. – Pieter Witvoet Apr 10 '19 at 14:45
  • @Pieter Witvoet : but than I must subclass the Button right? – GreenEyedAndy Apr 10 '19 at 18:01
  • 1
    @GreenEyedAndy: no, that `Set` method can be located in any class. For example, if you have a class `MyClass` with a `public static void SetMyProperty(DependencyObject o, CornerRadius value)` method, then you can add `local:MyClass.MyProperty="{Binding ...}"` to your xaml button. Have that method call `o.SetValue(Border.CornerRadiusProperty, value)` and you're done. – Pieter Witvoet Apr 10 '19 at 20:58
50

Just create a new Button like this:

<!--Button-->
            <Button  
               Name="myButton"
               Content="OK"
               FontFamily="Century Gothic"
               Foreground="white"
               Background="CornflowerBlue"
               BorderThickness="0"
               Padding="10"
               Margin="10,5">

                <Button.Resources>
                    <Style TargetType="{x:Type Border}">
                        <Setter Property="CornerRadius" Value="7"/>
                    </Style>
                </Button.Resources>

            </Button>
Christina
  • 555
  • 1
  • 6
  • 15
  • 3
    Nice and simple answer, perfect if you want a button to have its own properties. – Andrea Antonangeli Oct 24 '17 at 09:53
  • 1
    I was able to use this example and work it into a style, which is great because the accepted answer breaks all the default button behaviors. – John Jan 09 '20 at 17:08
  • 1
    @Chandraprakash its a great answer that's adequate for lots of situations, but its not as clean as it looks like. It actually overrides the Style not only for the Border in Button, but also for all borders that might be placed as content of the button. – 0xBADF00D Feb 26 '20 at 12:51
  • 2
    @0xBADF00D For my usage, this is the most cleanest code i ever seen and works flawlessly as i also use this on window.resources, so any button other other control i set this border will be applied to all the controls in the wpf form. – Chandraprakash Jul 17 '20 at 09:49
39

As Nitesh has said you do not have a CornerRadius Property on the Button, it is a property of the Border as you have shown in your first style, just duplicate your first Style and change the CornerRadius, then assign it to the Style of the appropriate Button.

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <Style TargetType="Button" x:Key="TabButton">
            <Setter Property="Background" Value="White" />
            <Setter Property="TextBlock.TextAlignment" Value="Center" />
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="Button">
                        <Border CornerRadius="0" Background="White" BorderBrush="#ccc" BorderThickness="0,1,1,0" >
                            <ContentPresenter x:Name="contentPresenter" ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
                        </Border>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
        <Style TargetType="Button" x:Key="TabButtonFirst">
            <Setter Property="Background" Value="White" />
            <Setter Property="TextBlock.TextAlignment" Value="Center" />
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="Button">
                        <Border CornerRadius="3,0,0,0" Background="White" BorderBrush="#ccc" BorderThickness="0,1,1,0" >
                            <ContentPresenter x:Name="contentPresenter" ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
                        </Border>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
         </Style>
         <Style TargetType="Button" x:Key="TabButtonLast">
            <Setter Property="Background" Value="White" />
            <Setter Property="TextBlock.TextAlignment" Value="Center" />
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="Button">
                        <Border CornerRadius="0,0,0,3" Background="White" BorderBrush="#ccc" BorderThickness="0,1,1,0" >
                            <ContentPresenter x:Name="contentPresenter" ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
                        </Border>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
      </Window.Resources>
        <Grid Background="Black">
        <Button Style="{StaticResource TabButton}" Content="Button" Height="23" HorizontalAlignment="Left" Margin="12,72,0,0" Name="button1" VerticalAlignment="Top" Width="75" />
        <Button Style="{StaticResource TabButtonFirst}" Content="Button" Height="23" HorizontalAlignment="Left" Margin="10,43,0,0" Name="button2" VerticalAlignment="Top" Width="75" />
        <Button Style="{StaticResource TabButtonLast}" Content="Button" Height="23" HorizontalAlignment="Left" Margin="12,101,0,0" Name="button3" VerticalAlignment="Top" Width="75" />
    </Grid>
</Window>

enter image description here

Mark Hall
  • 53,938
  • 9
  • 94
  • 111
  • 2
    Functionally works fine, but button lose its default styling, i.e. "OnMouseOver", "OnMouseClick" events don't change visual state of button. – monstr Jul 01 '14 at 09:53
  • 2
    I run into this whenever I have two templates with only one tiny little change. This kind of repetitive code really bothers me. There's got to be a better way. – Jordan Sep 11 '15 at 15:02
  • 5
    @Jordan: there is, see my answer. :) – Pieter Witvoet Oct 06 '15 at 10:38
  • 3
    While this works, it's not DRY at all. With anything but trivial styling, this will quickly become unmanageable. – Josh Noe Jul 12 '16 at 18:07
  • What happens if I want a round button? I have to make the CornerRadius at least half the size of the button. How can I be sure that the button doesn't grow bigger than the corner radius? – El Mac Nov 24 '16 at 17:21
  • @ElMac I would probably look at this [SO Question and answers](http://stackoverflow.com/questions/12470412/how-to-implement-a-circle-button-in-xaml) – Mark Hall Nov 25 '16 at 18:36
14

Instead of using ControlTemplate you can simply use Style.Resources and target the border:

<Style TargetType="Button" x:Key="TabButton">
  <Setter Property="Background" Value="White" />
  <Setter Property="TextBlock.TextAlignment" Value="Center" />
  <Style.Resources>
    <Style TargetType="Border">
      <Setter Property="CornerRadius" Value="3,0,0,0" />
    </Style>
  </Style.Resources>
</Style>
Andy Braham
  • 9,594
  • 4
  • 48
  • 56
5

You can use attached properties for setting button border radius (also the same will work for textboxes).

Create class for attached property

public class CornerRadiusSetter
{
    public static CornerRadius GetCornerRadius(DependencyObject obj) => (CornerRadius)obj.GetValue(CornerRadiusProperty);

    public static void SetCornerRadius(DependencyObject obj, CornerRadius value) => obj.SetValue(CornerRadiusProperty, value);

    public static readonly DependencyProperty CornerRadiusProperty =
        DependencyProperty.RegisterAttached(nameof(Border.CornerRadius), typeof(CornerRadius),
            typeof(CornerRadiusSetter), new UIPropertyMetadata(new CornerRadius(), CornerRadiusChangedCallback));

    public static void CornerRadiusChangedCallback(object sender, DependencyPropertyChangedEventArgs e)
    {
        Control control = sender as Control;

        if (control == null) return;

        control.Loaded += Control_Loaded;
    }

    private static void Control_Loaded(object sender, EventArgs e)
    {
        Control control = sender as Control;

        if (control == null || control.Template == null) return;

        control.ApplyTemplate();

        Border border = control.Template.FindName("border", control) as Border;

        if (border == null) return;

        border.CornerRadius = GetCornerRadius(control);
    }
}

Then you can use attached property syntax to style multiple buttons without style duplicates:

<Button local:CornerRadiusSetter.CornerRadius="3,0,0,0">Click me!</Button>

<Button local:CornerRadiusSetter.CornerRadius="0,0,0,3">Click me!</Button>
Vadim Ovchinnikov
  • 13,327
  • 5
  • 62
  • 90
  • 1
    Good lord, thank you for this! All the other solutions require basically reimplementing all the visual effects of the button, while this lets you use the defaults. – Mike Caron Jul 10 '19 at 18:32
1

I would make my own custom button class (inherited from Button) that includes a CornerRadius dependency property. And then the target type of your style becomes this new class and you can use template binding to set the corner radius.

With this approach not only do you not need to maintain multiple copies of your control template, but you don't need to create a new style for each time you want to modify your corner radius. You can just set or bind directly to your CornerRadius dependency property.

So your code for the control could look something like this:

public class MyCustomButton : Button
{
    public static readonly DependencyProperty CornerRadiusProperty =
        DependencyProperty.Register("CornerRadius", typeof(CornerRadius), typeof(MyCustomButton), new FrameworkPropertyMetadata(new CornerRadius(0)));

    public CornerRadius CornerRadius
    {
        get { return (CornerRadius)GetValue(CornerRadiusProperty); }
        set { SetValue(CornerRadiusProperty, value); }
    }
}

And the XAML:

<Style TargetType="MyCustomButton" x:Key="TabButton">
    <Setter Property="Background" Value="White" />
    <Setter Property="TextBlock.TextAlignment" Value="Center" />
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="MyCustomButton">
                <Border CornerRadius="{TemplateBinding CornerRadius}" Background="White" BorderBrush="#ccc" BorderThickness="0,1,1,0" >
                    <ContentPresenter x:Name="contentPresenter" ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
                </Border>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

So for your buttons that need no corner radius, since the dependency property defaults it to 0, you don't need to do anything. For the ones that have a corner radius, you just set the dependency property to the appropriate value as you would the CornerRadius property for a normal Border.

GrantA
  • 490
  • 5
  • 13
1

I was wondering where to put this, and I put it below my <Button Content="..

<Button.Resources>
 <Style TargetType="{x:Type Border}">
  <Setter Property="CornerRadius" Value="15"/>
 </Style>
</Button.Resources>

and above <Button.RenderTransform>

Works for me

Spinstaz
  • 287
  • 6
  • 12
0
 private Button [] Buttonsd() // Select all buttons on page
        {

            /// casting the content into panel
            Panel mainContainer = (Panel)this.Content;

            /// GetAll UIElement
            UIElementCollection element = mainContainer.Children;
         
            /// casting the UIElementCollection into List
            /// 
            List<DependencyObject> lstElement = element.Cast<DependencyObject>().ToList();

            /// Geting all Control from list
            /// 
            Button [] btns =  lstElement.OfType<Button>().ToArray();





            return btns;

            //maingrid.Children.Add(new Button() { Content = "HELLOMOTO" }); added new controls


           



        }     


private void RadiusButtons(int radiusvalue,Button btn)
            {
                ControlTemplate template = btn.Template;
                Border Border = (Border)template.FindName("border", btn);
                CornerRadius radius = new CornerRadius(radiusvalue);
                Border.CornerRadius = radius;
    
              }
    
                private void AllButtonsSettings()
            {
                Button[] buttons = Buttonsd(); // create buttons collection 
    
                foreach(Button btn in Buttonsd())
                {
                    RadiusButtons(21, btn);
                }
    
    
    
            }
Igor
  • 55
  • 1
  • 3