程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> [XAML]類似WPF綁定的Binding的讀取方法,xamlwpf

[XAML]類似WPF綁定的Binding的讀取方法,xamlwpf

編輯:C#入門知識

[XAML]類似WPF綁定的Binding的讀取方法,xamlwpf


在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()) { }

    //讀取屬性前
    protected override void OnBeforeProperties(object value)
    {
        _Instance = value;
        //記錄是否是依賴類型
        _IsDependencyObject = typeof(DependencyObject).IsAssignableFrom(_Instance.GetType());
        base.OnBeforeProperties(value);
    }

    //設置屬性值
    //只有值類型才會調用
    protected override bool OnSetValue(object eventSender, XamlMember member, object value)
    {
        if (_IsDependencyObject)
        {
            //獲取依賴屬性
            DependencyProperty dp = DependencyProperty.FromName(member.Name, member.DeclaringType.UnderlyingType);
            if (dp == null)
            {
                //如果不是依賴屬性,則使用CLR方法賦值
                return base.OnSetValue(eventSender, member, value);
            }
            DependencyObject target = (DependencyObject)_Instance;
            //使用自己框架的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團隊並通過了

但至今未解決……

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved