首页 > 学院 > 开发设计 > 正文

[XAML]类似WPF绑定的Binding的读取方法

2019-11-17 02:29:59
字体:
来源:转载
供稿:网友

[XAML]类似WPF绑定的Binding的读取方法

在WPF的XAML里,依赖属性可以使用基于BindingBase之类的MarkupExtensin

读取XAML时,会自动的把该BindingBase转换为BindingExPRessionBase

然后再放入DependencyObject的EffectiveValueEntry里

那么问题来了,在我们自己做一个轻量级依赖框架时,为什么读取BindingBase会报错

假设,一个属性名称为Title,类型为string

XAML文档为

<Page xmlns="http://schemas.wodsoft.com/web/presentation"      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"      Title="{Binding Content, ElementName=source}">    <ContentControl Name="source" Content="Test"/></Page>

在该轻量级框架里

Binding和WPF的一样

在ProvideValue方法执行时,同样会返回BindingExpression

如果读取该XAML,则会报错

类型“Wodsoft.Web.Data.BindingExpression”的对象无法转换为类型“System.String”。

因为XAML读取器会使用CLR来赋值,即使用Title属性的Setter来赋值

显然,BindingExpression无法给Title直接赋值

那么WPF是如何办到的呢?

也许你用过WPF的XamlReader,位于System.Windows.Markup下

该类的静态方法Load能读取XAML内容

同样也能正确读取Binding等MarkupExtension

该方法核心用到XamlXmlReader与XamlObjectWriter

一个读取XAML内容,一个把XAML内容变成Object

我们现在就要通过这两个类实现我们的需求

首先实现一个ObjectReader

public class ObjectReader{    public static object Load(Stream stream)    {        XamlXmlReader reader = new XamlXmlReader(stream);        XamlObjectWriter writer = new ObjectWriter();        while (reader.Read())        {            writer.WriteNode(reader);        }        writer.Close();        return writer.Result;    }}

XamlXmlReader就用原本的Reader

它负责读取XAML文档内容

我们要写一个ObjectWriter,继承自XamlObjectWriter

在里面实现我们的依赖系统

public class ObjectWriter : XamlObjectWriter{    public ObjectWriter() : base(new XamlSchemaContext()) { } object _Instance; protected override void OnBeforeProperties(object value) { _Instance = value; base.OnBeforeProperties(value); }    //设置属性值    protected override bool OnSetValue(object eventSender, XamlMember member, object value)    {        if (eventSender is DependencyObject)        {            //获取依赖属性            DependencyProperty dp = DependencyProperty.FromName(member.Name, member.DeclaringType.UnderlyingType);            if (dp == null)            {                //如果不是依赖属性,则使用CLR方法赋值                return base.OnSetValue(eventSender, member, value);            }            DependencyObject target = (DependencyObject)eventSender;            //使用自己框架的SetValue方法赋值            target.SetValue(dp, value);            return true;        }        else            return base.OnSetValue(eventSender, member, value);    }    //写入成员方法    public override void WriteStartMember(XamlMember property)    {        //判断是否是依赖类型        if (property.DeclaringType != null && property.DeclaringType.UnderlyingType.IsSubclassOf(typeof(DependencyObject)))        {            //如果是属性            if (property.UnderlyingMember is PropertyInfo)            {                //防止目标类型未调用静态构造函数                //这里我不知道还有什么方法可以引发类型的静态构造函数                if (_Instance == null)                    _Instance = Activator.CreateInstance(property.DeclaringType.UnderlyingType);                //获取依赖属性                DependencyProperty dp = DependencyProperty.FromName(property.Name, property.DeclaringType.UnderlyingType);                if (dp != null)                {                    //如果是依赖属性                    //覆盖XamlMember                    //使用我们自己MemberInvoker                    property = new XamlMember((PropertyInfo)property.UnderlyingMember, SchemaContext, new ObjectMemberInvoker(dp));                }            }        }        base.WriteStartMember(property);    }    private object _Instance;    private bool _IsDependencyObject;}

OnSetValue方法是设置普通值类型的属性时用到的

WriteStartMember则是当非值类型属性时调用到

这里需要编写一个ObjectMemberInvoker,继承自XamlMemberInvoker

我们需要重写GetValue和SetValue方法

这样我们就能达到我们的目标了

public class ObjectMemberInvoker : XamlMemberInvoker{    public ObjectMemberInvoker(DependencyProperty property)    {        Property = property;    }    public DependencyProperty Property { get; private set; }    public override object GetValue(object instance)    {        DependencyObject d = (DependencyObject)instance;        return d.GetValue(Property);    }    public override void SetValue(object instance, object value)    {        DependencyObject d = (DependencyObject)instance;        if (value is BindingExpression)        {            //...        }        else            d.SetValue(Property, value);    }}

在SetValue方法里判断value

如果是绑定类则调用相关方法

否则调用依赖属性的设置方法

现在我们就能正常读取绑定而不会报错了

结束语

XAML很强大,可以扩展出很多东西

但是里面有很多东西微软是没有开放的

拿来做框架会遇到很多坑

甚至于没有解决方法

更多出现于VS的XAML编辑器里

比如这个问题

http://stackoverflow.com/questions/18671317/each-dictionary-entry-must-have-an-associated-key

这个BUG已经有人报告给VS团队并通过了

但至今未解决&hellip;…


发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表