首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >通过附加属性为不同控件(按钮、TextBox、ComboBox)设置圆角边框

通过附加属性为不同控件(按钮、TextBox、ComboBox)设置圆角边框
EN

Code Review用户
提问于 2018-06-22 08:00:59
回答 1查看 18.1K关注 0票数 5

假设您只想为不同的控件设置边框半径:

  • Button
  • TextBox
  • ComboBox

我通常发现的最常见的方法是完全覆盖默认模板,如在“答案”中。

我发现重写每个CornerRadius值的ButtonTextBoxComboBox的默认样式非常糟糕,原因如下:

  • 您将不得不在资源中携带许多样式,它们将被许多默认属性所污染。
  • 如果需要不同的值,则为每个属性值创建重复代码。
  • 您没有清晰、优雅的代码来设置边框半径本身

所以我已经为这个实现了附加属性。实现中变化最大的部分是为ComboBox实现这一点。

代码语言:javascript
运行
复制
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;
    }

    private static void Control_Loaded(object sender, EventArgs e)
    {
        Control control = sender as Control;

        if (control == null || control.Template == null) return;

        control.ApplyTemplate();

        CornerRadius cornerRadius = GetCornerRadius(control);

        Control toggleButton = control.Template.FindName("toggleButton", control) as Control;

        if (control is ComboBox && toggleButton != null)
        {
            toggleButton.ApplyTemplate();

            // Set border radius for border radius border
            Border toggleButtonBorder = toggleButton.Template.FindName("templateRoot", toggleButton) as Border;
            toggleButtonBorder.CornerRadius = cornerRadius;

            // Expand padding for combobox to avoid text clipping by border radius
            control.Padding = new Thickness(
                    control.Padding.Left + cornerRadius.BottomLeft,
                    control.Padding.Top,
                    control.Padding.Right + cornerRadius.BottomRight,
                    control.Padding.Bottom);

            // Decrease width of dropdown and center it to avoid showing "sticking" dropdown corners
            Popup popup = control.Template.FindName("PART_Popup", control) as Popup;

            Popup popup = control.Template.FindName("PART_Popup", control) as Popup;

            if (popup != null)
            {
                double offset = cornerRadius.BottomLeft - 1;
                if (offset > 0)
                    popup.HorizontalOffset = offset;
            }

            SystemDropShadowChrome shadowChrome = control.Template.FindName("shadow", control) as SystemDropShadowChrome;

            if (shadowChrome != null)
            {
                double minWidth = control.ActualWidth - cornerRadius.BottomLeft - cornerRadius.BottomRight;
                if (minWidth > 0)
                    shadowChrome.MinWidth = minWidth;
            }
        }

        // setting borders for non-combobox controls
        Border border = control.Template.FindName("border", control) as Border;

        if (border == null) return;

        border.CornerRadius = cornerRadius;
    }
}

因此,现在可以通过单个属性以in代码的样式设置边框。

代码语言:javascript
运行
复制
<Button local:CornerRadiusSetter.CornerRadius="10">Button</Button>
<Button local:CornerRadiusSetter.CornerRadius="5,7,10,12">Button</Button>
<TextBox local:CornerRadiusSetter.CornerRadius="3,0,0,3" />
<TextBox local:CornerRadiusSetter.CornerRadius="7,8,2,1" />
<ComboBox local:CornerRadiusSetter.CornerRadius="5" />
<ComboBox local:CornerRadiusSetter.CornerRadius="7" />

或者对于Resources中的多个控件:

代码语言:javascript
运行
复制
<Style TargetType="Button">
    <Setter Property="local:CornerRadiusSetter.CornerRadius" Value="10" />
</Style>

造型效果

EN

回答 1

Code Review用户

发布于 2018-06-22 13:49:11

我的天啊。只是为了圆角?

是的,我完全同意这一评论。令人惊讶的是,在WPF中,一些听起来微不足道的事情是极其复杂的。但你能做的只有这么多。你既可以对抗它,也可以拥抱它(你也可以选择不同的框架,但这是另一个故事)。在选择这两种选择时,你必须考虑到以下几点:

你将来不改变默认模板的可能性有多大?根据我的经验,这些机会往往是很渺茫的。如果你足够关心你的UI --拐角半径会困扰你,那么这可能不是你唯一需要改变的地方。那高亮的颜色呢?其他控件呢?你也会改变滚动条按钮的角半径吗?进步吧?

如果我错了就纠正我。为此,您应该为默认按钮样式存储近100行代码,2)创建带有附加属性的新类(不能为按钮编写Border.CornerRadius="10“,对吗?)

是的,你不能,但你可以把它设置成风格。此代码适用于:

代码语言:javascript
运行
复制
<Page
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
  <Button>  
      <Button.Style>
          <Style TargetType="Button">
              <Setter Property="Border.CornerRadius" Value="15"/>
              <Setter Property="Width" Value="100"/>
              <Setter Property="Height" Value="100"/>
              <Setter Property="Template">
                  <Setter.Value>
                      <ControlTemplate>
                          <Border CornerRadius="{TemplateBinding Border.CornerRadius}" 
                                  BorderThickness="1" Background="Green" BorderBrush="Black"/>
                      </ControlTemplate>
                  </Setter.Value>
              </Setter>
          </Style>
      </Button.Style>
  </Button>
</Page>

但是,当然,您可以使用自己的独立附加属性(不需要对任何东西进行子类)。您甚至可以继承它,并为整个MainWindow设置它,这样所有控件都会受到影响。

别误会我,我自己也经常写类似的行为。我只是认为,每当我想通过行为修改控制模板时,仔细考虑含意是个好主意。是的,当您重写模板时,会有一些初始的复制粘贴。但这样才能奏效。要对Padding属性进行数据库化吗?很好。想要通过动画改变角半径属性吗?好的。想要在停靠窗口内使用这些按钮,可以多次重新加载其内容?没问题。

另一方面,您的解决方案与当前的用例有很大关系:

1)它覆盖填充属性,可能会破坏触发器和数据库。

2)不允许在加载控件后对CornerRadius进行修改。

3)如果Loaded事件触发不止一次,它将无法工作。最值得注意的是,你的填充物将继续增长。

4)它在很大程度上依赖于这样一个事实:默认按钮模板有一个名为border的元素。但是它会在未来版本的框架中出现吗?我的意思是,现实地说,它可能会,但这个假设仍然觉得不可靠。

可能还有其他边缘情况,您的行为将失败,但模板不会。所以这一切都值得吗?我不太确定。

票数 4
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/197042

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档