问题描述
我目前正在努力使我的 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" Width="136" Height="167" BorderRoundness="5">
解决方法
您必须将 BorderRoundness
绑定到父级 AwakeButton
,否则会使用当前的 DataContext
进行解析,该 Button
不包含此属性。此外,如果您从 Register(...)
派生,则不必附加依赖属性,只需使用 static
方法注册一个普通属性即可。还要使 DP 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" Width="136" Height="167" local:ButtonProperties.BorderRoundness="5"/>
您现在使用带有新创建的附加边框圆度属性的常规按钮。
var numArray = [88,5,700];
,
圆度作为 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>
但是如果你想让它真正通用,使用附加属性,你必须做出一个附加行为。以下代码适用于在其可视化树中包含 DependencyObject
作为子项的每个 Border
:
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>