您的位置:知识库 » .NET技术

WPF基础到企业应用系列7——深入剖析依赖属性(WPF/Silverlight核心)

作者: 圣殿骑士  来源: 博客园  发布时间: 2010-08-29 20:07  阅读: 6351 次  推荐: 1   原文链接   [收藏]  
摘要:从这篇文章开始的下面几篇文章中,作者会为我们分别深入讲解一下依赖属性、路由事件、命令和绑定等相关概念,希望这几篇文章对大家能有所帮助。
[1] WPF基础到企业应用系列7——深入剖析依赖属性(WPF/Silverlight核心)
[2] WPF基础到企业应用系列7——深入剖析依赖属性(WPF/Silverlight核心)
[3] WPF基础到企业应用系列7——深入剖析依赖属性(WPF/Silverlight核心)
[4] WPF基础到企业应用系列7——深入剖析依赖属性(WPF/Silverlight核心)
[5] WPF基础到企业应用系列7——深入剖析依赖属性(WPF/Silverlight核心)
[6] WPF基础到企业应用系列7——深入剖析依赖属性(WPF/Silverlight核心)

 

  十三. 依赖属性监听

  如果想监听依赖属性的改变,可以用两种方法实现,在很多时候,我们两种方法都会用到:
  用DependencyPropertyDescriptor 比较简便,在代码里面写起来也比较便捷;
  用OverrideMetadata的方式主要在自定义控件以及处理一些类间关系的时候;
  第一种方法:派生自这个类,然后定义它的属性,重写属性的原数据并传递一个PropertyChangedCallBack参数即可,如下代码:

public class MyTextBox : TextBox
{
public MyTextBox(): base()
{
}

static MyTextBox()
{
//第一种方法,通过OverrideMetadata
FlowDirectionProperty.OverrideMetadata(typeof(MyTextBox), new FrameworkPropertyMetadata(new PropertyChangedCallback(FlowDirectionPropertyChanged)));
}

private static void FlowDirectionPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
{
((MyTextBox)sender).FontWeight = (((MyTextBox)sender).FlowDirection == FlowDirection.LeftToRight) ? FontWeights.Bold : FontWeights.Normal;

}
}

  第二种方法:这个方法更加简单,获取DependencyPropertyDescriptor并调用AddValueChange()为其挂接一个回调函数,如下代码:

private void Window1_Loaded(object sender, RoutedEventArgs e)
{
//第二种方法,通过OverrideMetadata
DependencyPropertyDescriptor descriptor = DependencyPropertyDescriptor.FromProperty(TextBox.TextProperty, typeof(TextBox));
descriptor.AddValueChanged(tbxEditMe, tbxEditMe_TextChanged);
}

private void tbxEditMe_TextChanged(object sender, EventArgs e)
{
MessageBox.Show("", "Changed");
}

  十四. 代码段(自动生成)

  代码段可以说是一个非常普遍且实用的功能,我们可以利用它来简化和规范我们的代码。在项目当中我们通常会定义大量的代码段,如怎样写一个类、怎样定义一个方法、公用代码库等等都可以定义成代码段,今天不着重讲这一块,下面就来看看在默认的VS中有哪些代码段:

2010-8-26 9-12-59

  上面看到的是visual basic的代码段(一不小心截图截错了,呵呵),但不幸的是针对C#的代码段却很少。不过没关系,既然默认没有提供那么多代码段,我们可以自己动手嘛,正所谓自己动手丰衣足食嘛!相信大家都有自定义代码段的经历,同时在网上也有很多好的代码段下载,我用得最多的是DrWPFSnippets,由于接触WPF和Silverlight是在07年,所以当时自己也定义过一些代码段,由于自己主要精力还是在技术架构、ASP.NET、WCF、OO等方面,所以在08年以后就开始使用网上的代码段资源了,当然由于之前项目也自己写了一些代码段,所以很多时候都是混合起来使用,大家可以到http://drwpf.com/blog/2010/04/30/updated-code-snippets/去下载,这个代码段包最早从2007年11月就提供下载了,在今年四月份进行了升级,同时支持VS2005/VS2008/VS2010,所以大家可以下载下来体验一下,很不错的哦!下载以后点击DrWPFSnippets.vsi就会自动安装,安装完成以后,你会看到如下界面,图中的Shortcut就是你要按的快捷键,不过生成的代码会出现有些帮助类找不到的情况,如RoutedEvent会生成一个RoutedEventHelper的类,这个是没有关系的,你到网上一搜就可以把这个类加入到你的代码当中。那么运行就十分正常了。在安装的时候提醒一下,最好一次安装成功,否则你会为众多的弹窗口感到十分厌恶,呵呵!

2010-8-26 10-33-42

  那么现在你就可以在项目当中使用了,如按下re+TAB键两次,你就会看到如下界面,然后选择你的选项即可生成需要的代码(这里re就是Routed event的简写)。

vsprin

  如下是生成的代码,你可以直接使用或者经过适当修改使用。

    #region Swindle

/// <summary>
///
Swindle Routed Event
/// </summary>
public static readonly RoutedEvent SwindleEvent = EventManager.RegisterRoutedEvent("Swindle",
RoutingStrategy.Bubble, typeof(TrioEventHandler), typeof(Window1));

/// <summary>
///
Occurs when ...
/// </summary>
public event TrioEventHandler Swindle

{
add { AddHandler(SwindleEvent, value); }
remove { RemoveHandler(SwindleEvent, value); }
}

/// <summary>
///
A helper method to raise the Swindle event.
/// </summary>
/// <param name="arg"> </param>
/// <param name="arg2"> </param>
/// <param name="arg3"> </param>
protected TrioEventArgs RaiseSwindleEvent(bool arg, bool arg2, bool arg3)
{
return RaiseSwindleEvent(this, arg, arg2, arg3);
}

/// <summary>
///
A static helper method to raise the Swindle event on a target element.
/// </summary>
/// <param name="target">
UIElement or ContentElement on which to raise the event</param>
/// <param name="arg"> </param>
/// <param name="arg2"> </param>
/// <param name="arg3"> </param>
internal static TrioEventArgs RaiseSwindleEvent(DependencyObject target, bool arg, bool arg2, bool arg3)
{
if (target == null) return null;

TrioEventArgs args = new TrioEventArgs(arg, arg2, arg3);
args.RoutedEvent = SwindleEvent;
RoutedEventHelper.RaiseEvent(target, args);
return args;
}

#endregion

  十五. 模拟依赖属性实现

  古人有”不入虎穴焉得虎子“的名句,我们今天也试着入一入虎穴,探探依赖属性里面到底藏着什么不可告人的秘密,在往下讲之前,我们先来看一下DependencyObject DependencyProperty 以及PropertyMetadata到底包含哪些功能,如下面三幅图

2010-8-26 18-38-50

2010-8-26 18-52-44

2010-8-26 19-14-21

  通过前面三幅图,我们就可以了解WPF依赖属性系统的大体结构以及主要功能,再者通过前面我们对它的使用,对它的内部实现也有一个相对比较清晰的认识,那么接下来要做的就是:借助Reflector+VS调试内部代码功能一起来研究其内部的实现原理。 本来想详细写清楚开发的过程,但是有点多,所以我打算直接讲这几个类。大家也可以通过这个思路来试一试,同时还可以参考Mono的源码、WF的依赖属性源码等。这里要推荐的是周永恒的博客,此人对技术的理解很是透彻,博文虽少,但每篇都堪称经典,所以他的文章,我都通读三遍。虽然大多概念都懂,并且读到深处也能产生共鸣,其最主要目的还是学习他这种”阐述问题的思路“,后来也和此人MSN聊过几次。所以这个依赖属性的框架在某些程度上也借鉴了他的一些写法。

  有了前面的思路,首先定义DependencyProperty这个类,它里面存储前面我们提到希望抽出来的字段。DependencyProperty内部维护了一个全局的Map用来储存所有的DependencyProperty,对外暴露了一个Register方法用来注册新的DependencyProperty。当然,为了保证在Map中键值唯一,注册时需要根据传入的名字和注册类的的 HashCode取异或来生成Key。 所以我们就可以完成DependencyProperty类了,代码如下,介绍详见代码注释。:

public sealed class DependencyProperty
{
//全局的IDictionary用来储存所有的DependencyProperty
internal static IDictionary<int, DependencyProperty> properties = new Dictionary<int, DependencyProperty>();
//存储元数据的集合
private List<PropertyMetadata> _metadataMap = new List<PropertyMetadata>();
private static int globalIndex = 0;
private PropertyMetadata def_metadata;
private bool attached;
private string name;
private int _index;
private Type owner_type;
private Type property_type;
private Type validator_type;

// 构造函数
private DependencyProperty()
{

}

//构造函数私有,保证外界不会对它进行实例化
private DependencyProperty(string name, Type propertyType, Type ownerType, PropertyMetadata defaultMetadata)
{
this.name = name;
property_type = propertyType;
owner_type = ownerType;
def_metadata = defaultMetadata;
}

// 常用属性
public PropertyMetadata DefaultMetadata
{
get { return def_metadata; }
}

public bool IsAttached
{
get { return attached; }
}

public int Index
{
get { return _index; }
set { _index = value; }
}

public string Name
{
get { return name; }
}

public Type OwnerType
{
get { return owner_type; }
}

public Type PropertyType
{
get { return property_type; }
}

public Type ValidatorType
{
get { return validator_type; }
}


public override int GetHashCode()
{
return name.GetHashCode() ^ owner_type.GetHashCode();
}

//注册依赖属性
public static DependencyProperty Register(string name, Type propertyType, Type ownerType)
{
return Register(name, propertyType, ownerType, new PropertyMetadata());
}

//注册的公用方法,把这个依赖属性加入到IDictionary的键值集合中,Key为name和owner_type的GetHashCode取异,Value就是我们注册的DependencyProperty
public static DependencyProperty Register(string name, Type propertyType, Type ownerType, PropertyMetadata defaultMetadata)
{
DependencyProperty property = new DependencyProperty(name, propertyType, ownerType, defaultMetadata);
globalIndex++;
property.Index = globalIndex;

if (properties.ContainsKey(property.GetHashCode()))
{
throw new InvalidOperationException("A property with the same name already exists");
}

//把刚实例化的DependencyProperty添加到这个全局的IDictionary种
properties.Add(property.GetHashCode(), property);
return property;
}

//注册只读依赖属性
public static DependencyProperty RegisterReadOnly(string name, Type propertyType, Type ownerType, PropertyMetadata typeMetadata)
{
DependencyProperty property = Register(name, propertyType, ownerType, typeMetadata);
return property;
}

//注册附加依赖属性
public static DependencyProperty RegisterAttached(string name, Type propertyType, Type ownerType)
{
return RegisterAttached(name, propertyType, ownerType, new PropertyMetadata(), null);
}

public static DependencyProperty RegisterAttached(string name, Type propertyType, Type ownerType, PropertyMetadata defaultMetadata)
{
return RegisterAttached(name, propertyType, ownerType, defaultMetadata, null);
}

public static DependencyProperty RegisterAttached(string name, Type propertyType, Type ownerType, PropertyMetadata defaultMetadata, Type validatorType)
{
DependencyProperty property = Register(name, propertyType, ownerType, defaultMetadata);
property.attached = true;
property.validator_type = validatorType;
return property;
}

//子类继承重写以及其他需要重写Metadata的时候使用
public void OverrideMetadata(Type forType, PropertyMetadata metadata)
{
metadata.Type = forType;
_metadataMap.Add(metadata);
}

//获取元数据信息
public PropertyMetadata GetMetadata(Type type)
{
PropertyMetadata medatata = _metadataMap.FirstOrDefault((i) => i.Type == type) ??
_metadataMap.FirstOrDefault((i) => type.IsSubclassOf(i.Type));
if (medatata == null)
{
medatata = def_metadata;
}
return medatata;
}

}

  有了DependencyProperty ,那么接下来就需要定义DependencyObject 来使用这个DependencyProperty 。首先使用DependencyProperty .Register方法注册了一个新的DependencyProperty ,然后提供了GetValue和SetValue两个方法来操作刚刚构造的DependencyProperty 。这个时候我们看到一个简单的依赖属性系统已初见端倪了,详见代码注释。

namespace Realize_DPs
{
public abstract class DependencyObject : IDisposable
{
//添加一个List来记录修改信息
private List<EffectiveValueEntry> _effectiveValues = new List<EffectiveValueEntry>();

//属性包装器,通过它来访问依赖属性
public object GetValue(DependencyProperty dp)
{
//首先通过判断是否改动过,以此来决定是读元数据的默认值还是改动了的值
EffectiveValueEntry effectiveValue = _effectiveValues.FirstOrDefault((i) => i.PropertyIndex == dp.Index);
if (effectiveValue.PropertyIndex != 0)
{
return effectiveValue.Value;
}
else
{
PropertyMetadata metadata;
metadata = DependencyProperty.properties[dp.GetHashCode()].DefaultMetadata;
return metadata.DefaultValue;
}
}

//属性包装器,通过它来设置依赖属性的值
public void SetValue(DependencyProperty dp, object value)
{
//首先通过判断是否改动过,以及改动过,则继续对改动过的元素赋值,否则对_effectiveValues增加元素
EffectiveValueEntry effectiveValue = _effectiveValues.FirstOrDefault((i) => i.PropertyIndex == dp.Index);
if (effectiveValue.PropertyIndex != 0)
{
effectiveValue.Value = value;
}
else
{
effectiveValue = new EffectiveValueEntry() { PropertyIndex = dp.Index, Value = value };
_effectiveValues.Add(effectiveValue);
}
}

public void Dispose()
{
//暂时还没有处理
}
}

internal struct EffectiveValueEntry
{
internal int PropertyIndex { get; set; }
internal object Value { get; set; }
}
}

  前面有了DependencyProperty DependencyObject 类,那我们现在来新建一个比较重要的类 PropertyMetadata ,它的作用和功能很强大,我们这里只是简单进行了构建,如下代码:

namespace Realize_DPs
{
public delegate void SetValueOverride(DependencyObject d, object value);

public delegate object GetValueOverride(DependencyObject d);

public class PropertyMetadata
{
private object default_value;
private DependencyPropertyOptions options = DependencyPropertyOptions.Default;
private bool _sealed = false;
private SetValueOverride set_value;
private GetValueOverride get_value;
private Attribute[] attributes;
private Type type;

// 构造函数重载
public PropertyMetadata()
{

}

public PropertyMetadata(object defaultValue)
{
default_value = defaultValue;
}

public PropertyMetadata(DependencyPropertyOptions options)
{
this.options = options;
}

public PropertyMetadata(params Attribute[] attributes)
{
this.attributes = attributes;
}

public PropertyMetadata(object defaultValue, params Attribute[] attributes)
{
default_value = defaultValue;
this.attributes = attributes;
}

public PropertyMetadata(object defaultValue, DependencyPropertyOptions options)
{
default_value = defaultValue;
this.options = options;
}

public PropertyMetadata(DependencyPropertyOptions options, params Attribute[] attributes)
{
this.options = options;
this.attributes = attributes;
}

public PropertyMetadata(object defaultValue, DependencyPropertyOptions options, params Attribute[] attributes)
{
this.options = options;
default_value = defaultValue;
this.attributes = attributes;
}

public PropertyMetadata(object defaultValue, DependencyPropertyOptions options, GetValueOverride getValueOverride, SetValueOverride setValueOverride)
{
this.options = options;
default_value = defaultValue;
set_value = setValueOverride;
get_value = getValueOverride;
}

public PropertyMetadata(object defaultValue, DependencyPropertyOptions options, GetValueOverride getValueOverride, SetValueOverride setValueOverride, params Attribute[] attributes)
{
this.options = options;
default_value = defaultValue;
set_value = setValueOverride;
get_value = getValueOverride;
this.attributes = attributes;
}

// 常用属性
public object DefaultValue
{
get { return default_value; }
set { default_value = value; }
}

public GetValueOverride GetValueOverride
{
get { return get_value; }
set { get_value = value; }
}

public bool IsMetaProperty
{
get { return (options & DependencyPropertyOptions.Metadata) == DependencyPropertyOptions.Metadata; }
}

public bool IsNonSerialized
{
get { return (options & DependencyPropertyOptions.NonSerialized) == DependencyPropertyOptions.NonSerialized; }
}

public bool IsReadOnly
{
get { return (options & DependencyPropertyOptions.Readonly) == DependencyPropertyOptions.Readonly; }
}

protected bool IsSealed
{
get { return _sealed; }
}

public DependencyPropertyOptions Options
{
get { return options; }
set { options = value; }
}

public SetValueOverride SetValueOverride
{
get { return set_value; }
set { set_value = value; }
}

public Type Type
{
get { return type; }
set { type = value; }
}

protected virtual void Merge(PropertyMetadata baseMetadata, DependencyProperty dp)
{
// 实现元数据继承之间的合并
}

protected virtual void OnApply(DependencyProperty dependencyProperty, Type targetType)
{
// 当元数据被这个属性应用,OnApply就会被触发,在此时元数据也将被密封起来。
}
}
}

  前面我们实现了一个简单的依赖属性系统,现在就得先测试一下其功能,代码如下:

class Program : DependencyObject
{
public static readonly DependencyProperty CounterProperty;
static Program()
{
//注册依赖属性Counter
CounterProperty = DependencyProperty.Register("Counter",
typeof(double),
typeof(Program),
new PropertyMetadata(8.0));
}

//属性包装器,暴露读写接口
public double Counter
{
get { return (double)GetValue(CounterProperty); }
set {SetValue(CounterProperty, value); }
}

static void Main(string[] args)
{
Program pro = new Program();
Console.WriteLine("读取元数据设置的默认值: "+pro.Counter.ToString());

Program pro2 = new Program();
pro2.Counter = 22.5;
Console.WriteLine("通过SetValue设置改变了的值: " + pro2.Counter.ToString());
Console.ReadLine();
}
}

  那么测试结果为:

2010-8-26 0-50-36  利用VS自带的类图,可以看到刚才我们实现的这个依赖属性类及类之间的关系图: 2010-8-26 0-49-19

  由于上面的代码在很多方面都很粗糙,所以希望大家能下载代码进行改造,同时也希望给出反馈。

 

1
0
标签:WPF

.NET技术热门文章

    .NET技术最新文章

      最新新闻

        热门新闻