[英]WPF how to binding CornerRadius of Border inside button template
I have a UserControl which has a button inside it. 我有一个其中有一个按钮的UserControl。 This button has a personalized style as bellow.
此按钮具有以下波纹状的个性化样式。 I want to create a property in my UserControl that affects the CornerRadius of a border called "Background" which is inside the button template, so that I can make the button corner round when needed.
我想在UserControl中创建一个属性,该属性会影响按钮模板内名为“ Background”的边框的CornerRadius,以便在需要时可以使按钮的角变圆。
I tried to create a property in my usercontrol and use OnApplyTemplate event and GetTemplateChild method but didn't work. 我试图在用户控件中创建一个属性,并使用OnApplyTemplate事件和GetTemplateChild方法,但是没有用。 I found the border, but nothing happens.
我找到了边界,但是什么也没发生。
public override void OnApplyTemplate()
{
Border border = GetTemplateChild("Background") as Border;
border.CornerRadius = this.CornerRadius;
}
<Style x:Key="ButtonStyle" TargetType="Button">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Foreground" Value="#FF000000"/>
<Setter Property="Padding" Value="0"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal"/>
<VisualState x:Name="MouseOver">
<Storyboard>
<DoubleAnimation Duration="0" To="1" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="BackgroundAnimation"/>
</Storyboard>
</VisualState>
<VisualState x:Name="Pressed">
<Storyboard>
<DoubleAnimation Duration="0" To="1" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="BackgroundAnimation"/>
<DoubleAnimation Duration="0" To=".55" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="DisabledVisualElement"/>
<ColorAnimation Duration="0" To="#8CFFFFFF" Storyboard.TargetProperty="(Rectangle.Fill).(SolidColorBrush.Color)" Storyboard.TargetName="BackgroundGradient"/>
</Storyboard>
</VisualState>
<VisualState x:Name="Disabled">
<Storyboard>
<DoubleAnimation Duration="0" To=".55" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="DisabledVisualElement"/>
</Storyboard>
</VisualState>
</VisualStateGroup>
<VisualStateGroup x:Name="FocusStates">
<VisualState x:Name="Focused"/>
<VisualState x:Name="Unfocused"/>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Border x:Name="Background" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="0" Background="Transparent" />
<Grid Background="{TemplateBinding Background}" Margin="1">
<Border x:Name="BackgroundAnimation" Background="Transparent" Opacity="0"/>
<Rectangle x:Name="BackgroundGradient" Fill="Transparent" />
</Grid>
<ContentPresenter x:Name="contentPresenter" ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
<Rectangle x:Name="DisabledVisualElement" Fill="#FFFFFFFF" IsHitTestVisible="false" Opacity="0" RadiusY="0" RadiusX="0"/>
<Rectangle x:Name="FocusVisualElement" IsHitTestVisible="false" Margin="1" Opacity="0" RadiusY="0" RadiusX="0" Stroke="#FF6DBDD1" StrokeThickness="0"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Edit - Solution 编辑-解决方案
I got what I wanted by creating a heritage of Button with a dependency property for CornerRadius, then using TemplateBinding. 通过为CornerRadius创建具有依赖性属性的Button继承物,然后使用TemplateBinding,得到了我想要的东西。
Important XAML parts: XAML重要部分:
<Style x:Key="ButtonStyle" TargetType="myProject:MyButton">
<ControlTemplate TargetType="myProject:MyButton">
<Border x:Name="Background" BorderBrush="{TemplateBinding BorderBrush}" Background="{TemplateBinding Background}" CornerRadius="{TemplateBinding CornerRadius}">
Full XAML and C# 完整的XAML和C#
public class MyButton : Button
{
public static readonly DependencyProperty CornerRadiusProperty =
DependencyProperty.Register("CornerRadius", typeof(CornerRadius),
typeof(MyButton), new FrameworkPropertyMetadata(new CornerRadius(0, 0, 0, 0)));
public CornerRadius CornerRadius
{
get { return (CornerRadius)GetValue(CornerRadiusProperty); }
set { SetValue(CornerRadiusProperty, value); }
}
}
<Style x:Key="ButtonStyle" TargetType="myProject:MyButton">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="myProject:MyButton">
<Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal"/>
<VisualState x:Name="MouseOver">
<Storyboard>
<DoubleAnimation Duration="0" To="1" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="BackgroundAnimation"/>
</Storyboard>
</VisualState>
<VisualState x:Name="Pressed">
<Storyboard>
<DoubleAnimation Duration="0" To="1" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="BackgroundAnimation"/>
<DoubleAnimation Duration="0" To=".55" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="DisabledVisualElement"/>
<ColorAnimation Duration="0" To="#8CFFFFFF" Storyboard.TargetProperty="(Rectangle.Fill).(SolidColorBrush.Color)" Storyboard.TargetName="BackgroundGradient"/>
</Storyboard>
</VisualState>
<VisualState x:Name="Disabled">
<Storyboard>
<DoubleAnimation Duration="0" To=".55" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="DisabledVisualElement"/>
</Storyboard>
</VisualState>
</VisualStateGroup>
<VisualStateGroup x:Name="FocusStates">
<VisualState x:Name="Focused"/>
<VisualState x:Name="Unfocused"/>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Border x:Name="Background" BorderBrush="{TemplateBinding BorderBrush}" Background="{TemplateBinding Background}" CornerRadius="{TemplateBinding CornerRadius}">
<Grid>
<Border x:Name="BackgroundAnimation" Background="Transparent" Opacity="0"/>
<Rectangle x:Name="BackgroundGradient" Fill="Transparent"/>
</Grid>
</Border>
<ContentPresenter x:Name="contentPresenter" ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
<Rectangle x:Name="DisabledVisualElement" Fill="#FFFFFFFF" IsHitTestVisible="false" Opacity="0" RadiusY="{Binding ElementName=Background, Path=CornerRadius.TopLeft}" RadiusX="{Binding ElementName=Background, Path=CornerRadius.BottomLeft}"/>
<Rectangle x:Name="FocusVisualElement" IsHitTestVisible="false" Margin="1" Opacity="0" RadiusX="0" RadiusY="0" Stroke="#FF6DBDD1" StrokeThickness="0"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
如果您在控件中有一个名为CornerRadius
的属性,我很确定这可以做到:
<Border CornerRadius="{TemplateBinding CornerRadius}"/>
You need to search the VisualTree for the border. 您需要在VisualTree中搜索边框。 it's not part of the UserControl's template so it's not a Template child.
它不是UserControl模板的一部分,因此不是Template子级。
Here's some good extensions to help you with this and other situations where you need to traverse the VisualTree : 这是一些很好的扩展,可以帮助您解决这种情况以及需要遍历VisualTree的其他情况:
public static class VisualTreeHelperExtensions
{
public static T FindVisualParent<T>(DependencyObject depObj) where T : DependencyObject
{
var parent = VisualTreeHelper.GetParent(depObj);
if (parent == null || parent is T)
return (T)parent;
return FindVisualParent<T>(parent);
}
public static T FindVisualChild<T>(DependencyObject depObj) where T : Visual
{
if (depObj != null && IsVisual(depObj))
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(depObj, i);
if (child != null && child is T)
{
return (T)child;
}
foreach (T childOfChild in FindVisualChildren<T>(child))
{
return childOfChild;
}
}
}
return null;
}
public static T FindVisualChild<T>(DependencyObject depObj, string name) where T : FrameworkElement
{
if (depObj != null && IsVisual(depObj))
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(depObj, i);
if (child != null && child is T && (child as T).Name.Equals(name))
{
return (T)child;
}
foreach (T childOfChild in FindVisualChildren<T>(child))
{
if (childOfChild.Name.Equals(name))
return childOfChild;
}
}
}
return null;
}
public static IEnumerable<T> FindVisualChildren<T>(DependencyObject depObj) where T : DependencyObject
{
if (depObj == null)
yield break;
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
{
if (IsVisual(depObj))
{
DependencyObject child = VisualTreeHelper.GetChild(depObj, i);
if (child != null && child is T)
{
yield return (T) child;
}
foreach (T childOfChild in FindVisualChildren<T>(child))
{
yield return childOfChild;
}
}
}
}
private static bool IsVisual(DependencyObject depObj)
{
return depObj is Visual || depObj is Visual3D;
}
} }
In this case use : 在这种情况下,请使用:
public override void OnApplyTemplate()
{
Border border = VisualTreeHelperExtensions.FindVisualChild<Border>(this,"Background") as Border;
border.CornerRadius = this.CornerRadius;
}
The best way to do that through Template Binding. 最好的方法是通过模板绑定。 WPF best practices say that you need to apply UI changes through appropriate WPF mechanism: XAML.
WPF最佳实践表明,您需要通过适当的WPF机制来应用UI更改:XAML。 Consequently, we need to change our Style or Template.
因此,我们需要更改样式或模板。 If it is possible, we should create a flexible template to allow to change UI by changing of style, creation a new Style without modification of the Template.
如果可能,我们应该创建一个灵活的模板,以允许通过更改样式来更改UI,在不修改模板的情况下创建新的样式。 Open/Closed
SOLID
principle: Extend and not modify. 打开/关闭
SOLID
原理:扩展而不修改。 Unfortunately, we forgot that CornerRadius
it is the attached property of class Border
. 不幸的是,我们忘记了
CornerRadius
它是Border
类的附加属性。 So we can create a Template Binding: 因此,我们可以创建一个模板绑定:
<ControlTemplate TargetType="{x:Type Button}"
x:Key="ControlTemplateButtonNormal">
<Border Background="{TemplateBinding Background}"
BorderThickness="{TemplateBinding BorderThickness}"
BorderBrush="{TemplateBinding BorderBrush}"
CornerRadius="{TemplateBinding Border.CornerRadius}"
x:Name="BorderRoot">
<Grid>
<ContentPresenter IsTabStop="False"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
TextElement.Foreground="{TemplateBinding Foreground}" />
</Grid>
</Border>
<Trigger Property="Validation.HasError"
Value="True">
<Setter Property="Visibility"
TargetName="ErrorElement"
Value="Visible" />
<Setter Property="BorderBrush"
TargetName="BorderRoot"
Value="Transparent" />
</Trigger>
<Trigger Property="IsReadOnly"
Value="True">
<Setter Property="Background"
TargetName="BorderRoot"
Value="{StaticResource BorderBrushReadonly}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
Now we can use this template to create different Styles. 现在我们可以使用该模板来创建不同的样式。 Normal button:
普通按钮:
<Style TargetType="{x:Type Button}"
x:Key="ButtonNormalStyle">
<Setter Property="BorderThickness"
Value="1" />
<Setter Property="Border.CornerRadius"
Value="0" />
<Setter Property="Template"
Value="{StaticResource ControlTemplateButtonNormal}" />
</Style>
Button Round: 圆形按钮:
<Style TargetType="{x:Type Button}"
x:Key="ButtonRoundStyle">
<Setter Property="BorderThickness"
Value="1" />
<Setter Property="Border.CornerRadius"
Value="5,5,5,5" />
<Setter Property="Template"
Value="{StaticResource ControlTemplateButtonNormal}" />
</Style>
I hope it will be useful. 我希望它会有用。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.