如何在 WPF 中创建/制作圆角按钮?

我需要在 WPF 中创建一个 圆角光滑按钮圆角光滑按钮。谁能告诉我需要哪些步骤?

187253 次浏览

You have to create your own ControlTemplate for the Button. just have a look at the sample

created a style called RoundCorner and inside that i changed rather created my own new Control Template with Border (CornerRadius=8) for round corner and some background and other trigger effect. If you have or know Expression Blend it can be done very easily.

<Style x:Key="RoundCorner" TargetType="{x:Type Button}">
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="Padding" Value="1"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Grid x:Name="grid">
<Border x:Name="border" CornerRadius="8" BorderBrush="Black" BorderThickness="2">
<Border.Background>
<RadialGradientBrush GradientOrigin="0.496,1.052">
<RadialGradientBrush.RelativeTransform>
<TransformGroup>
<ScaleTransform CenterX="0.5" CenterY="0.5"
ScaleX="1.5" ScaleY="1.5"/>
<TranslateTransform X="0.02" Y="0.3"/>
</TransformGroup>
</RadialGradientBrush.RelativeTransform>
<GradientStop Offset="1" Color="#00000000"/>
<GradientStop Offset="0.3" Color="#FFFFFFFF"/>
</RadialGradientBrush>
</Border.Background>
<ContentPresenter HorizontalAlignment="Center"
VerticalAlignment="Center"
TextElement.FontWeight="Bold">
</ContentPresenter>
</Border>


</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsPressed" Value="True">
<Setter Property="Background" TargetName="border">
<Setter.Value>
<RadialGradientBrush GradientOrigin="0.496,1.052">
<RadialGradientBrush.RelativeTransform>
<TransformGroup>
<ScaleTransform CenterX="0.5" CenterY="0.5" ScaleX="1.5" ScaleY="1.5"/>
<TranslateTransform X="0.02" Y="0.3"/>
</TransformGroup>
</RadialGradientBrush.RelativeTransform>
<GradientStop Color="#00000000" Offset="1"/>
<GradientStop Color="#FF303030" Offset="0.3"/>
</RadialGradientBrush>
</Setter.Value>
</Setter>
</Trigger>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="BorderBrush" TargetName="border" Value="#FF33962B"/>
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Opacity" TargetName="grid" Value="0.25"/>
</Trigger>


</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

Using

<Button Style="{DynamicResource RoundCorner}"
Height="25"
VerticalAlignment="Top"
Content="Show"
Width="100"
Margin="5" />

This is more of a minimal control template to get a button with rounded corners, however you will not have any hover or click visual effects. But you can add these to the control template as needed. I was working with a dark background, hence the white background.

<Style x:Key="RoundedButtonStyle" TargetType="{x:Type Button}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border CornerRadius="15" Background="White" BorderThickness="1" Padding="2">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

I used the control template from the following blog post as my starting point: http://shihac-sharp.blogspot.com.au/2012/05/button-with-rounded-corners-in-wpf.html

<Button x:Name="btnBack" Grid.Row="2" Width="300"
Click="btnBack_Click">
<Button.Template>
<ControlTemplate>
<Border CornerRadius="10" Background="#463190">
<TextBlock Text="Retry" Foreground="White"
HorizontalAlignment="Center"
Margin="0,5,0,0"
Height="40"
FontSize="20"></TextBlock>
</Border>
</ControlTemplate>
</Button.Template>
</Button>

This is working fine for me.

You can try this...........

 <Border BorderBrush="Black" Name="MyBorder"
Height="78"
Background="Red"
Width="74"
CornerRadius="3">
<Button Width="{Binding MyBorder.Width}"
Height="{Binding MyBorder.Height}"
Content="Hi" Background="Red"/>
</Border>

in your app.xaml add this part of style :

   <Application.Resources>
<Style TargetType="FrameworkElement" x:Key="VisibleAnimation">
<Setter Property="Visibility" Value="Collapsed"/>
<Setter Property="Opacity" Value="10"/>
<Setter Property="Height" Value="700"></Setter>
<Style.Triggers>
<Trigger Property="Visibility" Value="Visible">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity"
From="0.0" To="1.0" Duration="0:0:0.5"/>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
</Trigger>
</Style.Triggers>
</Style>


<Style TargetType="Button" x:Key="BTNCORNER">
<Setter Property="Background" Value="White" />
<Setter Property="TextBlock.TextAlignment" Value="Center" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border CornerRadius="7,7,7,7" Background="White"    BorderBrush="#ccc" BorderThickness="1,1,1,1" >
<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>


</Application.Resources>

Button

 <Button x:Name="loginButton"
Style="{StaticResource BTNCORNER}"
Margin="50,20,20,20"
Click="loginButton_Click"
FontSize="20" Width="93" Height="42"  />

As alternative, you can code something like this:

    <Border
x:Name="borderBtnAdd"
BorderThickness="1"
BorderBrush="DarkGray"
CornerRadius="360"
Height="30"
Margin="0,10,10,0"
VerticalAlignment="Top" HorizontalAlignment="Right" Width="30">
<Image x:Name="btnAdd"
Source="Recursos/Images/ic_add_circle_outline_black_24dp_2x.png"
Width="{Binding borderBtnAdd.Width}" Height="{Binding borderBtnAdd.Height}"/>
</Border>

The "Button" will look something like this:

How it could looks like

You could set any other content instead of the image.

Well the best way to get round corners fast and with standard animation is to create a copy of the control template with Blend. Once you get a copy set the corner radius on the Grid tag and you should be able to have your control with full animation functionality and applyable to any button control. look this is the code:

<ControlTemplate x:Key="ButtonControlTemplate" TargetType="Button">
<Grid x:Name="RootGrid" Background="{TemplateBinding Background}"
CornerRadius="8,8,8,8">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal">
<Storyboard>
<PointerUpThemeAnimation Storyboard.TargetName="RootGrid" />
</Storyboard>
</VisualState>
<VisualState x:Name="PointerOver">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="RootGrid" Storyboard.TargetProperty="Background">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ButtonBackgroundPointerOver}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="BorderBrush">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ButtonBorderBrushPressed}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ButtonForegroundPointerOver}" />
</ObjectAnimationUsingKeyFrames>
<PointerUpThemeAnimation Storyboard.TargetName="RootGrid" />
</Storyboard>
</VisualState>
<VisualState x:Name="Pressed">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="RootGrid" Storyboard.TargetProperty="Background">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ButtonBackgroundPressed}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="BorderBrush">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ButtonBorderBrushPressed}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ButtonForegroundPressed}" />
</ObjectAnimationUsingKeyFrames>
<PointerDownThemeAnimation Storyboard.TargetName="RootGrid" />
</Storyboard>
</VisualState>
<VisualState x:Name="Disabled">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="RootGrid" Storyboard.TargetProperty="Background">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ButtonBackgroundDisabled}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="BorderBrush">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ButtonBorderBrushDisabled}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ButtonForegroundDisabled}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<!--<Border CornerRadius="8,8,8,8"
Background="#002060"
BorderBrush="Red"
BorderThickness="2">-->
<ContentPresenter x:Name="ContentPresenter"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Content="{TemplateBinding Content}"
ContentTransitions="{TemplateBinding ContentTransitions}"
ContentTemplate="{TemplateBinding ContentTemplate}"
Padding="{TemplateBinding Padding}"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
AutomationProperties.AccessibilityView="Raw"/>
<!--</Border>-->
</Grid>
</ControlTemplate>

I also edited the VisualState="PointerOver" specifically at Storyboard.TargetName="BorderBrush", because its ThemeResource get squared corners whenever PointerOver triggers.

Then you should be able to apply it to your control style like this:

<Style TargetType="ContentControl" x:Key="ButtonLoginStyle"
BasedOn="{StaticResource CommonLoginStyleMobile}">
<Setter Property="FontWeight" Value="Bold"/>
<Setter Property="Background" Value="#002060"/>
<Setter Property="Template" Value="{StaticResource ButtonControlTemplate}"/>
</Style>

So you can apply your styles to any Button.

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;
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 for multiple buttons without style duplicates:

<Button local:CornerRadiusSetter.CornerRadius="10">Click me!</Button>
<Button local:CornerRadiusSetter.CornerRadius="5, 0, 0, 5">Click me!</Button>
<Button local:CornerRadiusSetter.CornerRadius="3, 20, 8, 15">Click me!</Button>

This is an adapted version of @Kishore Kumar 's answer that is simpler and more closely matches the default button style and colours. It also fixes the issue that his "IsPressed" trigger is in the wrong order and will never be executed since the "MouseOver" will take precedent:

<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Grid x:Name="grid">
<Border x:Name="border" CornerRadius="2" BorderBrush="#707070" BorderThickness="1" Background="LightGray">
<ContentPresenter HorizontalAlignment="Center"
VerticalAlignment="Center"
TextElement.FontWeight="Normal">
</ContentPresenter>
</Border>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" TargetName="border" Value="#BEE6FD"/>
<Setter Property="BorderBrush" TargetName="border" Value="#3C7FB1"/>
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter Property="BorderBrush" TargetName="border" Value="#2C628B"/>
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Opacity" TargetName="grid" Value="0.25"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>

I know this post is super old, but I have an answer that's surprisingly missing from the above and is also much simpler than most.

<Button>
<Button.Resources>
<Style TargetType="Border">
<Setter Property="CornerRadius" Value="5"/>
</Style>
</Button.Resources>
</Button>

Since the default ControlTemplate for the Button control uses a Border element, adding a style for Border to the Button's resources applies that style to that Border. This lets you add rounded corners without having to make your own ControlTemplate and without any code. It also works on all varieties of Button (e.g. ToggleButton and RepeatButton).

Although this question is long-since answered, I used an alternative approach that people might find simpler than any of these solutions (even Keith Stein's excellent answer). So I'm posting it in case it might help anyone.

You can achieve rounded corners on a button without having to write any XAML (other than a Style, once) and without having to replace the template or set/change any other properties. Just use an EventSetter in your style for the button's "Loaded" event and change it in code-behind.

(And if your style lives in a separate Resource Dictionary XAML file, then you can put the event code in a code-behind file for your resource dictionary.)

I do it like this:

Xaml Style:

<Style x:Key="ButtonStyle" TargetType="{x:Type Button}" BasedOn="{StaticResource {x:Type Button}}">
<EventSetter Event="Loaded"                   Handler="ButtonLoaded"/>
</Style>

Code-Behind:

public partial class ButtonStyles
{
private void ButtonLoaded(object sender, RoutedEventArgs e)
{
if (!(sender is Button b))
return;


// Find the first top-level border we can.


Border border = default;
for (var i = 0; null == border && i < VisualTreeHelper.GetChildrenCount(b); ++i)
border = VisualTreeHelper.GetChild(b, i) as Border;


// If we found it, set its corner radius how we want.


if (border != null)
border.CornerRadius = new CornerRadius(3);
}
}

If you had to add the code-behind file to an existing resource dictionary xaml file, you can even have the code-behind file automatically appear underneath that XAML file in the Visual Studio Solution if you want. In a .NET Core project, just give it appropriate corresponding name (e.g if the resource Dictionary is "MyDictionary.xaml", name the code-behind file "MyDictionary.xaml.cs"). In a .NET Framework project, you need to edit the .csproj file in XML mode

The simplest solution without changing the default style is:

<Style TargetType="Button" x:Key="RoundButton">
<Style.Resources>
<Style TargetType="Border">
<Setter Property="CornerRadius" Value="5" />
</Style>
</Style.Resources>
</Style>

Then just define your button like this:

<Button Style="{DynamicResource RoundButton}" />

I know its a old question but if you are looking to make the button on c# instead of xaml you can set the CornerRadius that will round your button.

Button buttonRouded = new Button
{
CornerRadius = 10,
};

despite the fact years have gone, i thing it's interesting to think about different way to approach it.

The way to recreate all the button template is an excellent way if you want to change everything but it's demoralizing for the beginner or if you just want to round corner of the button. It's true you don't have to change everything but at least you will must change events...

The way to modify "border" design in the button.resources is excellent too, if you are a beginner but it can be very boring to change all your buttons if you want to raise your design with more parameters.

There is a solution with a foot in both camps:

Put this code in window/page resources:

<Style TargetType="Border" x:Key="RoundMe">
<Setter Property="CornerRadius" Value="4"/>
</Style>

Then for the buttons:

  <Button.Resources>
<Style TargetType="Border" BasedOn="{StaticResource RoundMe}"/>
</Button.Resources>

The most simple way is

<Button Content="OK"
Background="#D73C46"
Foreground="White"
BorderThickness="0"
Margin="10,25,10,5"
Width="275"
Height="34"
FontSize="12"
Click="CloseWindow"
HorizontalAlignment="Center">
<Button.Resources>
<Style TargetType="{x:Type Border}">
<Setter Property="CornerRadius" Value="3"/>
</Style>
</Button.Resources>
</Button>

When first starting I always disliked how a snippet of code with no explanation of location was given. So, there's this:

<Window x:Class ...>
<Window.Resources>
<Style x:Key="btnRound" TargetType="Button">
<Style.Resources>
<Style TargetType="Border">
<Setter Property="CornerRadius" Value="5"/>
</Style>
</Style.Resources>
</Style>
</Window.Resources>
<Grid>
<Button Content="Select" Click="Select_Click" Style="{StaticResource btnRound}" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top" Padding="10,2"/>
</Grid>
</Window>

for anyone who wonders why their xaml doesn't work when they put in the button rounded code, check the previous line to see if it ends with />, and take out the / if it does.