[XAML]类似WPF绑定的Binding的读取方法_.NET_编程开发_程序员俱乐部

中国优秀的程序员网站程序员频道CXYCLUB技术地图
热搜:
更多>>
 
您所在的位置: 程序员俱乐部 > 编程开发 > .NET > [XAML]类似WPF绑定的Binding的读取方法

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

 2015/3/24 21:37:55  Kation  程序员俱乐部  我要评论(0)
  • 摘要:在WPF的XAML里,依赖属性可以使用基于BindingBase之类的MarkupExtensin读取XAML时,会自动的把该BindingBase转换为BindingExpressionBase然后再放入DependencyObject的EffectiveValueEntry里那么问题来了,在我们自己做一个轻量级依赖框架时,为什么读取BindingBase会报错假设,一个属性名称为Title,类型为stringXAML文档为<Pagexmlns="http://schemas
  • 标签:方法

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

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

然后再放入DependencyObject的EffectiveValueEntry里

 

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

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

XAML文档为

class="brush:html;gutter:true;"><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

该方法核心用到XamlXmlReaderXamlObjectWriter

一个读取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()) { }

    //设置属性值
    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

我们需要重写GetValueSetValue方法

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

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团队并通过了

但至今未解决……

发表评论
用户名: 匿名