• [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

    该方法核心用到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()) { }
    
        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

    我们需要重写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团队并通过了

    但至今未解决……

  • 相关阅读:
    尝试了一下Flex
    Flash版的拉格朗日插值程序
    关于CSS属性display:none和visible:hidden的区别
    KMaster知识管理平台功能简介
    企业级知识库系统KMaster推荐
    ie6下的location.href错误
    利用Jquery实现http长连接(LongPoll)
    jQuery高亮插件
    当前知识管理系统的焦点问题以及我的一些解决办法
    知识库如何跟其他业务系统结合
  • 原文地址:https://www.cnblogs.com/Kation/p/xaml_reader.html
Copyright © 2020-2023  润新知