简体   繁体   English

WPF如何在按钮模板内绑定边框的CornerRadius

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM