依赖属性(Dependency Properties)进阶(一)

接着依赖属性(Dependency Properties)基础进行更深一步的学习。

1.依赖属性提供的属性功能()

1) 元数据重写

通过定义 PropertyMetadata,类可以定义依赖项属性的行为,例如,其默认值和属性系统回调。 很多依赖项属性类都已经将默认元数据作为其注册过程的一部分而创建。 这包含作为 WPF API 一部分的依赖项属性。 通过其类继承继承依赖项属性的类可以重写原始的元数据,以便可以通过元数据更改的属性的特征将与任何特定于子类的要求匹配。

在依赖项属性上重写元数据的操作必须在将该属性提供给属性系统使用之前进行,也就是说,在对注册属性的对象的特定实例进行实例化之前进行。 必须在将其自身提供为OverrideMetadata  forType 参数的类型的静态构造内执行对 OverrideMetadata 的调用。 如果在所有者类型的实例存在之后尝试更改元数据,这将不会引发异常,但会在属性系统中导致不一致的行为。 此外,每种类型只可以重写一次元数据。 以后在同一类型上重写元数据的尝试将会引发异常。

下面示例重写MyButtonContent依赖属性,将默认值改为Override

publicclass MyButton : Button
{
	static MyButton()
	{
		Button.ContentProperty.OverrideMetadata(typeof(MyButton), 			newFrameworkPropertyMetadata("Override"));
	}
}

通过元数据重写,在项目使用该控件时,设计时的Context就是Override了。

2) 属性值继承

属性值继承是 Windows PresentationFoundation (WPF) 属性系统的一项功能。 属性值继承使元素树中的子元素可以从父元素那里获取特定属性的值,并继承该值,就好像它是在最近的父元素中的任意位置设置的一样。 父元素还可以通过属性值继承来获得其值,因此系统有可能一直递归到页面根元素。 属性值继承不是属性系统的默认行为;属性必须用特定的元数据设置来建立,以便使该属性能够对子元素启动属性值继承。

如果Dependency属性在用注册的时候时指定Inherits为不可继承,这样继承就会失效。

如下,将MyButton的FontSize属性置为不可继承:

public class MyButton : Button
{
	static MyButton()
	{
		//…
		FrameworkPropertyMetadata fm = newFrameworkPropertyMetadata();
		fm.Inherits = false;
		Button.FontSizeProperty.OverrideMetadata(typeof(MyButton), fm);
	}
}

项目中使用:

<GridGrid.Column="0" Grid.Row="1"TextElement.FontSize="20">
	<Button Width="250"Content="属性值继承" Margin="73,28,73,169" />
	<mycontrol:MyButtonWidth="250" Margin="73,68,73,129" />
</Grid>

效果(第一个button继承了Grid的字体大小,而第二个没有继承)


2. 为依赖属性添加所有者类型

将类添加为针对不同类型注册的依赖性属性的所有者。 通过执行此操作,WPF XAML读取器和属性系统都可以将该类识别为属性的其他所有者。 添加所有者时,也可以选择添加类来提供类型特定的元数据。使用AddOwner方法。

示例(MyButton上添加一个标识文本(SymbolText)依赖属性,然后为其添加所有者MyLabel)

MyButton:

public class MyButton : Button
{
	//…
	public string SymbolText
	{
		get { return (string)GetValue(SymbolTextProperty); }
		set { SetValue(SymbolTextProperty, value); }
	}

	public static readonly DependencyProperty SymbolTextProperty =
		DependencyProperty.Register("SymbolText", typeof(string), typeof(MyButton), new PropertyMetadata("★"));
}
MyLabel( 这边支持元数据重写,修改了默认的标识文本 ):
public class MyLabel : Label
{
	//…
	public static readonly DependencyProperty SymbolTextProperty = MyButton.SymbolTextProperty.AddOwner(typeof(MyLabel), new PropertyMetadata("△"));
	public string SymbolText
	{
		get { return (string)this.GetValue(SymbolTextProperty); }
		set { this.SetValue(SymbolTextProperty, value); }
	}
}
样式代码就不贴了。

引用代码

<Grid Grid.Column="0" Grid.Row="1" TextElement.FontSize="20">
	<Button Width="250" Content="属性值继承" Margin="73,28,73,169" />
	<mycontrol:MyButton Width="250" Margin="73,68,73,129" />
	<mycontrol:MyLabel Width="250" Content="MyLabel" Margin="73,118,73,79"/>
</Grid>
效果

疑问:原用ImageSource做的,但是默认值一直加不上,高手请赐教,代码如下:

/// <summary>
/// 图片
/// <para>这是依赖属性</para>
/// </summary>
public ImageSource Icon
{
	get { return (ImageSource)GetValue(IconProperty); }
	set { SetValue(IconProperty, value); }
}
public static readonly DependencyProperty IconProperty =
	DependencyProperty.Register("Icon", typeof(ImageSource), typeof(MyButton), new PropertyMetadata(new BitmapImage(new Uri("/MyControls;component/Image/rabbit_32.png", UriKind.RelativeOrAbsolute))));

3. 只读依赖属性

在 Windows Presentation Foundation(WPF) 框架中定义的某些依赖项属性为只读。 通常指定只读依赖项属性的原因是:这些属性应该用于状态确定,但是有多种因素影响该状态,从用户界面设计的角度看,仅将属性设置为该状态并不能达到预期的效果。 例如,通过鼠标输入确认,属性 IsMouseOver 实际上仅为表层状态。 任何通过避开真正的鼠标输入以编程方式设置此值的尝试都将是不可预期的并将导致产生不一致的情况。

只读依赖属性创建注意点:

  • 注册属性时,调用 RegisterReadOnly 方法而不是常规的 Register 方法进行属性注册。
  • 当实现 CLR“包装”属性时,请确保该包装也没有设置的实现,以使公开的公共包装的只读状态中不存在不一致现象。
  • 只读注册返回的对象是 DependencyPropertyKey,而不是 DependencyProperty 您仍应将此字段作为成员存储,但是通常不将其设置为此类型的公共成员。

示例:为MyTextBox添加只读依赖属性,内容为文本长度:

public class MyTextBox:TextBox
{		
	//只公布Get方法
	/// <summary>
	/// 文本长度
	/// </summary>
	public int TextCount
	{
		get { return (int)GetValue(TextCountProperty.DependencyProperty); }
		private set { SetValue(TextCountProperty, value); }
	}

	public static readonly DependencyPropertyKey TextCountProperty =
		DependencyProperty.RegisterReadOnly("TextCount", typeof(int), typeof(MyTextBox), new PropertyMetadata(0));
}


4. 依赖属性元数据

依赖项属性的元数据还可以由定义依赖项属性的类来唯一地指定,可以在依赖项属性添加到另一个类时进行更改,可以由所有从定义基类继承依赖项属性的派生类来明确地重写。

依赖项属性的元数据对象可以包含以下类型的信息:

  • 依赖项属性的默认值。
  • 对影响每个所有者类型的强制行为或更改通知行为的回调实现的引用。 
  • 如果所讨论的依赖项属性被视为一个 WPF框架级别的属性,则元数据中可能包含 WPF框架级别的依赖项属性特征,这些特征报告各种服务(如 WPF框架级别的布局引擎和属性继承逻辑)的信息和状态。
public static DependencyProperty Register(
	string name,--依赖项对象的名称
	Type propertyType,--属性的类型
	Type ownerType,--依赖项对象的所有者类型
	PropertyMetadata typeMetadata,--依赖项对象的属性元数据
	ValidateValueCallback validateValueCallback--对回调的引用
)

public PropertyMetadata(
	Object defaultValue,--依赖项对象的默认值
	--每当属性的有效值更改时,属性系统都将调用该处理程序实现
	PropertyChangedCallback propertyChangedCallback,
	--每当属性系统对该属性调用 CoerceValue 时都将调用此处理程序实现
	CoerceValueCallback coerceValueCallback
)
--核心级别具有呈现/用户界面影响的非框架属性提供属性元数据
public UIPropertyMetadata(
	Object defaultValue,
	PropertyChangedCallback propertyChangedCallback,
	CoerceValueCallback coerceValueCallback,
	bool isAnimationProhibited--是否禁止动画处理,默认为false
)
--框架的属性系统
public FrameworkPropertyMetadata(
	Object defaultValue,
	--元数据选项标志(FrameworkPropertyMetadataOptions 值的组合)
	FrameworkPropertyMetadataOptions flags,
	PropertyChangedCallback propertyChangedCallback,
	CoerceValueCallback coerceValueCallback,
	bool isAnimationProhibited,
	--此属性的绑定时使用的 UpdateSourceTrigger
	UpdateSourceTrigger defaultUpdateSourceTrigger
)

5依赖属性回调、验证及强制值

执行顺序:

验证:ValidateValueCallback

强制值:CoerceValueCallback

回调:PropertyChangedCallback

示例(仅能输入数字的TextBox,并且有最大最小值验证)

/// <summary>
/// 文本值,仅能输入整数
/// </summary>
public string Value
{
	get { return (string)GetValue(ValueProperty); }
	set { SetValue(ValueProperty, value); }
}

public static readonly DependencyProperty ValueProperty =
	DependencyProperty.Register("Value",
		typeof(string),
		typeof(MyTextBox),
		new FrameworkPropertyMetadata(
			"5", 
			FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
			PropertyChanged,
			CoerceValue,
			true,
			UpdateSourceTrigger.LostFocus),
		ValidateValue);
疑问

原继承TextBox来做的,想替换调原来的Text属性,没成功。TextBox中使用PART_ContentHost来指定输入,并且指定[ContentProperty("Text")],没找到合适的方法去替换,有高手知道的赐教下。

private static bool ValidateValue(object value)
{
	try
	{
		int d = System.Convert.ToInt32(value);
		Debug.WriteLine("ValidateValue:Pass Value:" + value.ToString());
		return true;
	}
	catch
	{
		Debug.WriteLine("ValidateValue:Fail Value:" + value.ToString());
		return false;
	}
}

private static object CoerceValue(DependencyObject d, object baseValue)
{
	try
	{
		MyTextBox box = (MyTextBox)d;
		int i = Convert.ToInt32(baseValue);
		if (i > box.MaxValue)
		{
			i = box.MaxValue;
		}
		if (i < box.MinValue)
		{
			i = box.MinValue;
		}
		Debug.WriteLine("CoerceValue:Pass Value:" + baseValue.ToString() + " CoerceValue:"+i.ToString());
		((MyTextBox)d).IsLocked = true;
		((MyTextBox)d).Text = i.ToString();
		((MyTextBox)d).IsLocked = false;
		return i.ToString();
	}
	catch
	{
		Debug.WriteLine("CoerceValue:Fail Value:" + baseValue.ToString());
		return ValueProperty.DefaultMetadata.DefaultValue;
	}
}

private static void PropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
	//d.CoerceValue(MaxValueProperty);
	Debug.WriteLine("PropertyChanged:Pass Value:" + e.OldValue.ToString() + "->" + e.NewValue.ToString());
}

debug结果:

ValidateValue:PassValue:01

CoerceValue:PassValue:01 CoerceValue:1

ValidateValue:PassValue:1(此次调用应该由于强制回调引起)

PropertyChanged:PassValue:0->1


6.监听依赖属性

监听有两种方法:
a. 依赖属性回调,第5点中说明了。
b. 用DependencyPropertyDescriptor类:
使用方法如下(将Text和Value属性关联起来):

public MyTextBox()
{
	DependencyPropertyDescriptor descriptor = DependencyPropertyDescriptor.FromProperty(MyTextBox.TextProperty, typeof(MyTextBox));
	descriptor.AddValueChanged(this, OldTextChanged);
}

private void OldTextChanged(object sender, EventArgs e)
{
	if (IsLocked == false)
	{
		Value = Text;
	}
}



作者:FoolRabbit
出处:http://blog.csdn.net/rabbitsoft_1987
欢迎任何形式的转载,未经作者同意,请保留此段声明!

  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

FoolRabbit

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值