如何以通用方式更改按钮的圆度

Posted

技术标签:

【中文标题】如何以通用方式更改按钮的圆度【英文标题】:How to change the roundness of a button in a generic way 【发布时间】:2021-05-28 15:10:22 【问题描述】:

我目前正在努力使我的 WPF 应用程序更通用一点。 到目前为止,对于我想创建的每个按钮,我都使用了不同的样式来修改圆度(这会创建很多无用的代码)。

使用以下代码我设法创建了一个可以从 XAML 文件更改的变量,但我无法将它链接到圆度本身。

谁能告诉我我做错了什么?我已经查看了很多论坛,但似乎没有人知道除了“不要以通用方式做”之外的答案。

我可以确定所有内容都在编译,并且样式已正确应用于按钮(不存在 xaml 链接问题)。

我使用的风格:

<Style x:Key="AwakeButton" TargetType="x:Type customcontrols:AwakeButton" BasedOn="StaticResource x:Type Button"
       xmlns:extensions="Awake.Services.Properties:Extensions">
    <Setter Property="customcontrols:AwakeButton.BorderRoundness" Value="4.0"/>
    <Style.Resources>
        <Style TargetType="Border">
            <Setter Property="CornerRadius" Value="Binding Path=BorderRoundness" />
            <!--<Setter Property="CornerRadius" Value="10" />-->
        </Style>
    </Style.Resources>
</Style>

我为此创建的按钮超载:

public class AwakeButton : Button

    public AwakeButton()
    
        
    

    public static DependencyProperty BorderRoundnessProperty =
         DependencyProperty.RegisterAttached("BorderRoundness", typeof(double), typeof(AwakeButton)); 
    public static void SetBorderRoundness(UIElement element, double value)
    
        element.SetValue(BorderRoundnessProperty, value);
        
    

    public static double GetBorderRoundness(UIElement element)
    
        return (double)element.GetValue(BorderRoundnessProperty);
    

我如何在页面中使用它:

<customcontrols:AwakeButton Style="StaticResource AwakeButton" Margin="142,115,0,0"  Width="136" Height="167" BorderRoundness="5">

【问题讨论】:

this my answer 展示了如何使用附加的 DP。如果您创建派生按钮类,您可以使用常规 DP,但仍需要更改模板 我已经看到了那个答案,但是到那时我无法让它工作(命名空间问题),而且我认为现在使用自定义类更容易(即使我没有真的需要一个) 【参考方案1】:

您必须将BorderRoundness 绑定到父AwakeButton,否则使用当前的DataContext 进行解析,该DataContext 不包含此属性。此外,如果您从Button 派生,则不必附加依赖属性,您可以使用Register(...) 方法注册一个普通属性。还要让 DPs static readonly

<Setter Property="CornerRadius" Value="Binding BorderRoundness, RelativeSource=RelativeSource AncestorType=x:Type local:AwakeButton" />

如果您不更改按钮的任何特殊内容,您还可以创建附加属性而不是仅用于公开BorderRoundness 属性的专用子类型。

public static class ButtonProperties

   public static readonly DependencyProperty BorderRoundnessProperty =
      DependencyProperty.RegisterAttached("BorderRoundness", typeof(double), typeof(ButtonProperties));

   public static void SetBorderRoundness(UIElement element, double value)
   
      element.SetValue(BorderRoundnessProperty, value);

   

   public static double GetBorderRoundness(UIElement element)
   
      return (double)element.GetValue(BorderRoundnessProperty);
   

您可以使用附加的属性绑定语法(括号)引用BorderRoundness

<Style x:Key="AwakeButton" TargetType="x:Type Button" BasedOn="StaticResource x:Type Button">
   <Setter Property="local:ButtonProperties.BorderRoundness" Value="4.0"/>
   <Style.Resources>
      <Style TargetType="Border">
         <Setter Property="CornerRadius" Value="Binding (local:ButtonProperties.BorderRoundness), RelativeSource=RelativeSource AncestorType=x:Type Button" />
      </Style>
   </Style.Resources>
</Style>

您现在使用带有新创建的附加边框圆度属性的常规按钮。

<Button Grid.Row="0" Style="StaticResource AwakeButton" Margin="142,115,0,0"  Width="136" Height="167" local:ButtonProperties.BorderRoundness="5"/>

【讨论】:

谢谢,这正是我错过的 x) 现在完美运行【参考方案2】:

圆度作为 CornerRadius 应用于按钮的边框。 Border 是在 Button 的 ControlTemplate 中定义的。 ControlTemplate 定义控件的外观。 换句话说,您需要将属性值委托给 ControlTemplate 中的相关元素。

要将值委托给 ControlTemplate,您必须覆盖此模板并将模板化的父属性绑定到模板元素:

在您的 AwakeButton 中将 BorderRoundness 属性定义为简单的 DependencyProperty(未附加)并覆盖默认样式定义,以便 AwakeButton 将使用其自己的默认样式。这样,Button 就可以重复使用,而不必在每次使用时都重新定义 Style,这在您将项目发布为库时尤其重要:

AwakeButton.cs

public class AwakeButton : Button

  public static readonly DependencyProperty BorderRoundnessProperty = DependencyProperty.Register(
    "BorderRoundness",
    typeof(Thickness),
    typeof(AwakeButton),
    new PropertyMetadata(default(Thickness)));

  public Thickness DestinationPath
  
    get => (Thickness) GetValue(AwakeButton.BorderRoundnessProperty);
    set => SetValue(AwakeButton.BorderRoundnessProperty, value);
  

  static AwakeButton()
  
    DefaultStyleKeyProperty.OverrideMetadata(typeof(AwakeButton), new FrameworkPropertyMetadata(typeof(AwakeButton)));
  

Generic.xaml.cs 此文件位于 Themes 文件夹中,包含所有默认样式。 WPF 将自动检查此文件的默认样式,如果没有找到其他样式覆盖,则应用它。

<Style TargetType="AwakeButton">
  <Setter Property="Template">
    <Setter.Value>
      <ControlTemplate TargetType="AwakeButton">
        <Border BorderBrush=TemplateBinding BorderBrush"
                BorderThickness="TemplateBinding BorderThickness"
                BorderBrush="TemplateBinding BorderBrush"
                CornerRadius="TemplateBinding BorderRoundness">
          <ContentPresenter />
        </Border>
      </ControlTemplate>
    </Setter.Value>
  </Setter>
</Stayle>

示例

<Grid>
  <AwakeButton BorderRoundness="8" />
</Grid>

但如果你想让它真正通用,使用附加属性,你必须做出附加行为。以下代码适用于在其可视化树中包含 Border 作为子级的每个 DependencyObject

class Element : DependencyObject

  #region CornerRoundness attached property

  public static readonly DependencyProperty CornerRoundnessProperty = DependencyProperty.RegisterAttached(
    "CornerRoundness",
    typeof(CornerRadius),
    typeof(Element),
    new PropertyMetadata(default(CornerRadius), Element.OnCornerRoundnessChanged));

  public static void SetCornerRoundness(DependencyObject attachingElement, CornerRadius value) =>
    attachingElement.SetValue(Element.CornerRoundnessProperty, value);

  public static CornerRadius GetCornerRoundness(DependencyObject attachingElement) =>
    (CornerRadius) attachingElement.GetValue(Element.CornerRoundnessProperty);

  #endregion CornerRoundness attached property


  private static void OnCornerRoundnessChanged(DependencyObject attachingElement, DependencyPropertyChangedEventArgs e)
  
    if (Element.TryFindVisualChildElement(attachingElement, out Border elementBorder))
    
      elementBorder.CornerRadius = (CornerRadius) e.NewValue;
    
  

  public static bool TryFindVisualChildElement<TChild>(DependencyObject parent, out TChild resultElement)
    where TChild : DependencyObject
  
    resultElement = null;

    if (parent is Popup popup)
    
      parent = popup.Child;
      if (parent == null)
      
        return false;
      
    

    for (var childIndex = 0; childIndex < VisualTreeHelper.GetChildrenCount(parent); childIndex++)
    
      DependencyObject childElement = VisualTreeHelper.GetChild(parent, childIndex);

      if (childElement is TChild child)
      
        resultElement = child;
        return true;
      

      if (Element.TryFindVisualChildElement(childElement, out resultElement))
      
        return true;
      
    

    return false;
  

示例

<StackPanel>
  <Button Element.CornerRoundness="8" />
  <ToggleButton Element.CornerRoundness="8" />
</StackPanel>

【讨论】:

以上是关于如何以通用方式更改按钮的圆度的主要内容,如果未能解决你的问题,请参考以下文章

如何以编程方式更改表格单元格中单选按钮组的单选按钮?

如何在 Android 中以编程方式更改按钮大小?

如何在iphone中以编程方式更改按钮文本

如何在 UIAlertController 中以编程方式更改特定按钮文本颜色

如何以编程方式更改轮廓按钮图标?

以编程方式更改按钮颜色