控件中绝大多数都是依赖属性。
依赖属性只有继承了DependencyProperty的控件类才能注册,依赖属性与普通属性相比可以绑定并通知界面。此外依赖属性注册时还有三个回调函数,分别为。
给控件添加依赖属性可以在控件模板里绑定其他控件的属性如:继承button添加CornerRadius依赖属性在控件模板里绑定到Border的CornerRadius属性中,实现给button添加圆角效果。
一、继承Button增加圆角依赖属性
使用Code Snippet , 在派生里写propdp双击Tab自动写入依赖属性模板:
csharp
public int MyProperty
{
get { return (int)GetValue(MyPropertyProperty); }
set { SetValue(MyPropertyProperty, value); }
}
// Using a DependencyProperty as the backing store for MyProperty. This enables animation, styling, binding, etc...
public static readonly DependencyProperty MyPropertyProperty =
DependencyProperty.Register("MyProperty", typeof(int), typeof(ownerclass), new PropertyMetadata(0));
其中,将int改为自己需要的类型(这里改为CornerRadius);
MyProperty属性名字改为通俗易懂的名字(这里改为ButtonCornerRadius);
ownerclass 改为依赖属性所在类的类名(这里为CornerButton);
其中 PropertyMetadata 为元数据,实例化时可以给出初始值,如模板中的0就是元数据的初始值。
此外还有三个回调,分别为:
1、PropertyMetadata 构造函数的参数propertyChangedCallback(值回调),coerceValueCallback(强制回调)
2、DependencyProperty Register函数参数validateValueCallback(验证回调)
写完后依赖属性注册如下:
csharp
public class CornerButton : Button
{
public CornerRadius ButtonCornerRadius
{
get { return (CornerRadius)GetValue(ButtonCornerRadiusProperty); }
set { SetValue(ButtonCornerRadiusProperty, value); }
}
// Using a DependencyProperty as the backing store for ButtonCornerRadius. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ButtonCornerRadiusProperty =
DependencyProperty.Register(
"ButtonCornerRadius",
typeof(CornerRadius),
typeof(CornerButton),
new PropertyMetadata(
new CornerRadius(1),
propertyChangedCallback: ButtonRaidusChangedCallback,
coerceValueCallback: ButtonRaidusCoerceValueCallback
),
validateValueCallback: ButtonRaidusValiationCallback
);
/*
回调函数具体实现,如下面的 二
...
*/
static CornerButton()
{
DefaultStyleKeyProperty.OverrideMetadata(
typeof(CornerButton),
new FrameworkPropertyMetadata(typeof(CornerButton))
);
}
}
其中,静态构造函数中的代码用于 "指定控件的默认样式键" ,即此控件样式会去寻找TargetType 为此控件类型的Style,不受基础控件样式的影响(但目前就这个例子来说,写不写看不出区别来)。
二、 依赖属性里的回调函数:
1、propertyChangedCallback(值回调):当值发生改变时,可以对自定义控件进行一些变化。其中参数d为属性发生变化所在对象实例,e为属性更改的数据(如新值,旧值)。
ini
private static void ButtonRaidusChangedCallback(
DependencyObject d,
DependencyPropertyChangedEventArgs e
)
{
var cornerButton = (CornerButton)d;
var cornerRadius = (CornerRadius)e.NewValue;
var triggervalue = 10;
if (
cornerRadius.BottomLeft < triggervalue
&& cornerRadius.BottomRight < triggervalue
&& cornerRadius.TopRight < triggervalue
&& cornerRadius.TopLeft < triggervalue
)
{
cornerButton.Background = new SolidColorBrush(Colors.Red);
}
else
{
cornerButton.Background = new SolidColorBrush(Colors.Purple);
}
}
这里当属性值在一定范围时改变控件的背景颜色。
2、coerceValueCallback(强制回调):当值发生改变时,如果不是自己希望的值可以强制返回一个希望值。
csharp
private static object ButtonRaidusCoerceValueCallback(DependencyObject d, object baseValue)
{
var cornerRadius = (CornerRadius)baseValue;
var triggervalue = 5;
if (
cornerRadius.BottomLeft < triggervalue
&& cornerRadius.BottomRight < triggervalue
&& cornerRadius.TopRight < triggervalue
&& cornerRadius.TopLeft < triggervalue
)
{
return new CornerRadius(0);
}
return baseValue;
}
这里当低于某个值时,强行返回一个值。
3、validateValueCallback(验证回调):在给属性赋值时,先验证是否符合自己希望的范围或值。要是不是自己希望的值可以返回false,
此时要是在运行时会抛出ArgumentException异常。要是在设计时,会报错无法通过编译。
csharp
private static bool ButtonRaidusValiationCallback(object value)
{
var cornerRadius = (CornerRadius)value;
var maxValue = 15;
if (
cornerRadius.BottomLeft < maxValue
&& cornerRadius.BottomRight < maxValue
&& cornerRadius.TopRight < maxValue
&& cornerRadius.TopLeft < maxValue
)
return true;
else
return false;
}
这里当属性值大于某个值时,返回false。
回调触发顺序: 当属性被赋值时:验证回调->强制回调->验证回调->值回调
三、样式编写及外部调用:
1、样式:
ini
<Style TargetType="{x:Type c:CornerButton}">
<Style.Setters>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type c:CornerButton}">
<Border
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="{TemplateBinding ButtonCornerRadius}">
<ContentPresenter />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style.Setters>
<Style.Triggers>
<Trigger Property="ButtonCornerRadius" Value="8">
<Setter Property="BorderThickness" Value="8" />
<Setter Property="BorderBrush" Value="Blue" />
</Trigger>
<Trigger Property="ButtonCornerRadius" Value="10">
<Setter Property="BorderThickness" Value="10" />
<Setter Property="BorderBrush" Value="Yellow" />
</Trigger>
</Style.Triggers>
</Style>
第一行中的c为命名空间。
可以看到依赖属性也可以使用Triggers来使样式进行变化。
2、调用:
ini
<Grid>
<StackPanel>
<c:CornerButton
x:Name="cornerbtn"
Width="120"
Height="60"
Background="Purple"
ButtonCornerRadius="6" />
<StackPanel
Margin="0,10"
HorizontalAlignment="Center"
Orientation="Horizontal">
<Button
Width="60"
Height="30"
Click="Button_Click_3"
Content="3" />
<Button
Width="60"
Height="30"
Click="Button_Click_8"
Content="8" />
<Button
Width="60"
Height="30"
Click="Button_Click_10"
Content="10" />
<Button
Width="60"
Height="30"
Click="Button_Click_15"
Content="15" />
</StackPanel>
</StackPanel>
</Grid>
csharp
private void Button_Click_3(object sender, RoutedEventArgs e)
{
cornerbtn.ButtonCornerRadius = new CornerRadius(3);
}
private void Button_Click_8(object sender, RoutedEventArgs e)
{
cornerbtn.ButtonCornerRadius = new CornerRadius(8);
}
private void Button_Click_10(object sender, RoutedEventArgs e)
{
cornerbtn.ButtonCornerRadius = new CornerRadius(10);
}
private void Button_Click_15(object sender, RoutedEventArgs e)
{
try
{
cornerbtn.ButtonCornerRadius = new CornerRadius(15);
}
catch (ArgumentException ex)
{
MessageBox.Show($"{ex.Message}");
}
}
3、效果展示:
默认值为6,触发值回调,背景颜色从默认设置的紫色改为红色。


点击按钮3后,属性值被设置为3,由于小于强制回调里的条件,被强制设置成0.


点击8按钮,样式里的Trigger将边框设置成8且边框颜色为蓝色。


同理点击10按钮,样式里的Trigger将边框设置成10且边框颜色为黄色,且触发值回调将背景设置成紫色。

点击15按钮,值超出了验证回调里的条件,抛出异常。


参考链接:
bash
https://www.cnblogs.com/douzi2/p/17002595.html
vbnet
https://learn.microsoft.com/zh-cn/dotnet/desktop/wpf/properties/dependency-property-callbacks-and-validation
arduino
【WPF 中依赖属性及附加属性的概念及用法】 https://www.bilibili.com/video/BV1oV4y1F7TU