假设您只想为不同的控件设置边框半径:
Button
TextBox
ComboBox
我通常发现的最常见的方法是完全覆盖默认模板,如在“答案”中。
我发现重写每个CornerRadius
值的Button
、TextBox
、ComboBox
的默认样式非常糟糕,原因如下:
所以我已经为这个实现了附加属性。实现中变化最大的部分是为ComboBox实现这一点。
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代码的样式设置边框。
<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
中的多个控件:
<Style TargetType="Button">
<Setter Property="local:CornerRadiusSetter.CornerRadius" Value="10" />
</Style>
造型效果
发布于 2018-06-22 13:49:11
我的天啊。只是为了圆角?
是的,我完全同意这一评论。令人惊讶的是,在WPF中,一些听起来微不足道的事情是极其复杂的。但你能做的只有这么多。你既可以对抗它,也可以拥抱它(你也可以选择不同的框架,但这是另一个故事)。在选择这两种选择时,你必须考虑到以下几点:
你将来不改变默认模板的可能性有多大?根据我的经验,这些机会往往是很渺茫的。如果你足够关心你的UI --拐角半径会困扰你,那么这可能不是你唯一需要改变的地方。那高亮的颜色呢?其他控件呢?你也会改变滚动条按钮的角半径吗?进步吧?
如果我错了就纠正我。为此,您应该为默认按钮样式存储近100行代码,2)创建带有附加属性的新类(不能为按钮编写Border.CornerRadius="10“,对吗?)
是的,你不能,但你可以把它设置成风格。此代码适用于:
<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
的元素。但是它会在未来版本的框架中出现吗?我的意思是,现实地说,它可能会,但这个假设仍然觉得不可靠。
可能还有其他边缘情况,您的行为将失败,但模板不会。所以这一切都值得吗?我不太确定。
https://codereview.stackexchange.com/questions/197042
复制相似问题