WPF如何在按钮模板中绑定边框的角半径

本文关键字:边框 绑定 按钮 WPF | 更新日期: 2023-09-27 18:06:30

我有一个UserControl里面有一个按钮。这个按钮有一个个性化的风格,如下所示。我想在我的UserControl中创建一个属性,它影响按钮模板内部名为"Background"的边框的角半径,这样我就可以在需要时使按钮角圆。

我试图在我的usercontrol中创建一个属性,并使用OnApplyTemplate事件和GetTemplateChild方法,但没有工作。我找到了边界,但是什么也没发生。

    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>

编辑-解决方案

我得到了我想要的通过创建一个继承按钮与一个依赖属性的CornerRadius,然后使用TemplateBinding。

重要的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}">

完整的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>

WPF如何在按钮模板中绑定边框的角半径

最好的方法是通过模板绑定。WPF最佳实践表明,您需要通过适当的WPF机制(XAML)应用UI更改。因此,我们需要更改样式或模板。如果可能的话,我们应该创建一个灵活的模板,允许通过改变样式来改变UI,创建一个新的样式而不修改模板。打开/关闭 SOLID原则:扩展而不修改。不幸的是,我们忘记了CornerRadius它是Border类的附加属性。所以我们可以创建一个模板绑定:

<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>

现在我们可以使用这个模板来创建不同的样式。普通按钮:

<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>

按钮轮:

<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>

希望对大家有用。

如果你有一个名为CornerRadius的属性在你的控制,我很确定这将做到这一点:

<Border CornerRadius="{TemplateBinding CornerRadius}"/>

您需要在VisualTree中搜索边框。它不是UserControl模板的一部分,所以它不是template的子元素。

下面是一些很好的扩展,可以帮助您处理这种情况和其他需要遍历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;
    }

}

在这种情况下使用:

 public override void OnApplyTemplate()
{
    Border border = VisualTreeHelperExtensions.FindVisualChild<Border>(this,"Background") as Border;
    border.CornerRadius = this.CornerRadius;
}