WPF(附加属性和依赖属性)
附加属性的值可以被子元素继承
我们一般定义一个附加属性会如下一样定义:
using System.Windows;
using System.Windows.Controls;
public class Test : Button
{
public static int GetCount(DependencyObject obj)
{
return (int)obj.GetValue(CountProperty);
}
public static void SetCount(DependencyObject obj, int value)
{
obj.SetValue(CountProperty, value);
}
public static readonly DependencyProperty CountProperty =
DependencyProperty.RegisterAttached("Count", typeof(int), typeof(Test),
new PropertyMetadata(0));
}
这样定义并不会让子元素继承该属性 ,我们可以在注册时将PropertyMetadata改为其的派生类FrameworkPropertyMetadata,在FrameworkPropertyMetadata中有带FrameworkPropertyMetadataOptions类型参数的构造函数。
FrameworkPropertyMetadataOptions是个枚举类型:
None = 0, | 未指定任何选项;依赖属性使用 Windows Presentation Foundation (WPF) 属性系统的默认行为。 |
AffectsMeasure = 1, | 更改此依赖属性的值会影响布局组合的测量过程。 |
AffectsArrange = 2, | 更改此依赖属性的值会影响布局组合的排列过程。 |
AffectsParentMeasure = 4, | 更改此依赖属性的值会影响父元素上的测量过程。 |
AffectsParentArrange = 8, | 更改此依赖属性的值会影响父元素上的排列过程。 |
AffectsRender = 16, | 更改此依赖属性的值会影响呈现或布局组合的某一方面(不是测量或排列过程)。 |
Inherits = 32, | 此依赖属性的值将由子元素继承。 |
OverridesInheritanceBehavior = 64, | 此依赖属性的值跨越分隔的树以实现属性值继承。 |
NotDataBindable = 128, | 不允许将数据绑定到此依赖属性。 |
BindsTwoWayByDefault = 256, | 此依赖属性上的数据绑定的 System.Windows.Data.BindingMode 默认为 System.Windows.Data.BindingMode.TwoWay。 |
Journal = 1024, | 此依赖属性的值应由日记记录进程或在由 Uniform resource identifiers (URIs) 导航时进行保存或存储。 |
SubPropertiesDoNotAffectRender = 2048 | 此依赖属性值上的子属性不会影响呈现的任何方面。 |
所以我们如下注册附加属性就可以了:
using System.Windows;
using System.Windows.Controls;
public class Test : Button
{
public static int GetCount(DependencyObject obj)
{
return (int)obj.GetValue(CountProperty);
}
public static void SetCount(DependencyObject obj, int value)
{
obj.SetValue(CountProperty, value);
}
public static readonly DependencyProperty CountProperty =
DependencyProperty.RegisterAttached("Count", typeof(int), typeof(Test),
new FrameworkPropertyMetadata(0, FrameworkPropertyMetadataOptions.Inherits));
}
一个属性即可作为依赖属性使用也可作为附加属性使用
例如:TextBlock.FontSize
<Window x:Class="WpfApp3.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:WpfApp3"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="MainWindow" Width="800" Height="450" mc:Ignorable="d">
<Grid>
<StackPanel TextBlock.FontSize="15">
<TextBlock Text="TextBlock1" FontSize="12"/>
<TextBlock Text="TextBlock1"/>
<TextBlock Text="TextBlock1"/>
</StackPanel>
</Grid>
</Window>
那如何在代码中自定义一个类似这样的属性呢?作为初学者,一开始我也很迷惑。
通过F12查看FontSize的定义:
//
// 摘要:
// 标识 System.Windows.Controls.TextBlock.FontSize 依赖属性。
//
// 返回结果:
// System.Windows.Controls.TextBlock.FontSize 依赖项属性的标识符。
[CommonDependencyPropertyAttribute]
public static readonly DependencyProperty FontSizeProperty;
//
// 摘要:
// 返回指定的依赖对象的 System.Windows.Controls.TextBlock.FontSize 附加属性值。
//
// 参数:
// element:
// 要从中检索 System.Windows.Controls.TextBlock.FontSize 附加属性值的依赖对象。
//
// 返回结果:
// System.Windows.Controls.TextBlock.FontSize 附加属性的当前值(位于指定的依赖对象上)。
//
// 异常:
// T:System.ArgumentNullException:
// element 为 null。
[TypeConverter(typeof(FontSizeConverter))]
public static double GetFontSize(DependencyObject element);
//
// 摘要:
// 设置指定的依赖对象上的 System.Windows.Controls.TextBlock.FontSize 附加属性值。
//
// 参数:
// element:
// 要在其上设置 System.Windows.Controls.TextBlock.FontSize 属性值的依赖对象。
//
// value:
// 要将此属性设为的新值。
//
// 异常:
// T:System.ArgumentNullException:
// element 为 null。
public static void SetFontSize(DependencyObject element, double value);
可以发现,即定义了FontSizeProperty依赖属性,又定义附加属性的Get和Set方法。
下面我们写个demo测试一下,根据上述的写法是否可行:
using System.Windows;
using System.Windows.Controls;
public class Test : Button
{
public static int GetCount(DependencyObject obj)
{
return (int)obj.GetValue(CountProperty);
}
public static void SetCount(DependencyObject obj, int value)
{
obj.SetValue(CountProperty, value);
}
public static readonly DependencyProperty CountProperty =
DependencyProperty.Register("Count", typeof(int), typeof(Test),
new FrameworkPropertyMetadata(0,FrameworkPropertyMetadataOptions.Inherits));
public int Count
{
get { return (int)GetValue(CountProperty); }
set { SetValue(CountProperty, value); }
}
}
<Window x:Class="WpfApp3.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:WpfApp3"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="MainWindow" Width="800" Height="450" mc:Ignorable="d">
<Grid>
<StackPanel local:Test.Count="680">
<local:Test Content="{Binding RelativeSource={RelativeSource Mode=Self},Path=Count}" />
</StackPanel>
</Grid>
</Window>
测试可以发现Content值并不是680,因为Count是注册成的依赖属性, 值不能被子元素继承。可以将其注册为附加属性就可以实现Content的值为父元素中定义的附加属性的值。
using System.Windows;
using System.Windows.Controls;
public class Test : Button
{
public static int GetCount(DependencyObject obj)
{
return (int)obj.GetValue(CountProperty);
}
public static void SetCount(DependencyObject obj, int value)
{
obj.SetValue(CountProperty, value);
}
public static readonly DependencyProperty CountProperty =
DependencyProperty.RegisterAttached("Count", typeof(int), typeof(Test),
new FrameworkPropertyMetadata(0,FrameworkPropertyMetadataOptions.Inherits));
public int Count
{
get { return (int)GetValue(CountProperty); }
set { SetValue(CountProperty, value); }
}
}
通过查看TextBlock的源代码也可以知道,其实FontSize是注册为附加属性的。
/// <summary>
/// DependencyProperty for <see cref="FontSize" /> property.
/// </summary>
[CommonDependencyProperty]
public static readonly DependencyProperty FontSizeProperty =
TextElement.FontSizeProperty.AddOwner(
typeof(TextBlock));
/// <summary>
/// DependencyProperty for <see cref="FontSize" /> property.
/// </summary>
[CommonDependencyProperty]
public static readonly DependencyProperty FontSizeProperty =
DependencyProperty.RegisterAttached(
"FontSize",
typeof(double),
typeof(TextElement),
new FrameworkPropertyMetadata(
SystemFonts.MessageFontSize,
FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.Inherits),
new ValidateValueCallback(IsValidFontSize));