• Xamarin.Forms中 Navigation,NavigationPage详解


    1.Xamarin Forms下有四个成员类:Element,VisualElement,Page,NavigationPage

    基类为Element,继承的子类分别是VisualElement,Page,NavigationPage.

    2.Navigation 为VisualElement的一个成员对象,该对象是INavigation接口类型的。

    3.INavigation接口中有5个方法,如下

    namespace Xamarin.Forms
    {
        public interface INavigation
        {
            //
            // Methods
            //
            Task<Page> PopAsync ();
    
            Task<Page> PopModalAsync ();
    
            Task PopToRootAsync ();
    
            Task PushAsync (Page page);
    
            Task PushModalAsync (Page page);
        }
    }
    

    4.NavigationPage下有PopAsync(),PopToRootAsync(),PushAsync(Page)的具体实现。

    5.我们平时使用的时候会在App.cs 中使用“return new NavigationPage(new HomePage())”这种方式来启动一个可以包含子页面的页面。

    而在HomePage页面中我们使用“Navigation.PushAsync(new NextPage())”来启动子页面。

    Page对象派生出来的子类有:ContentPage,TabbedPage,NavigationPage,CarouselPage,MasterDetailPage 5大常用页面。

    6.有的时候我们可能产生疑问,INavigation的5个方法的具体实现在哪里?(答案在第7部分)

    我们可以看下这几个Page派生的类中有哪些东西,如下图:标记部分需要我们仔细查看

    NavigationPage:

    ContentPage:

    TabbedPage:

    CarouselPage:

    MasterDetailPage:

    7.我们先来看下VisualElement的具体内容:

    View Code

    这个对象中有一个BindableProperty对象NavigationProperty

    public static readonly BindableProperty NavigationProperty = VisualElement.NavigationPropertyKey.BindableProperty;
    

     其实它下面还有一个对象,是个静态的NavigationPropertyKey,内容如下:

    internal static readonly BindablePropertyKey NavigationPropertyKey = BindableProperty.CreateReadOnly ("Navigation", typeof(INavigation), typeof(VisualElement), null, BindingMode.OneWayToSource, null, null, null, null);
    

    这个对象是自创建的,就是说是在VisualElement中这个对象实例话后就会有的,ContentPage继承了Page,Page继承了VisualElement,所以说这个对象是在所有继承 Page对象的子类实例话后就存在的。  

    VisualElement 中 Navigation对象的代码:

    using System;
    
    public INavigation Navigation {
    	get {
    		return (INavigation)base.GetValue (VisualElement.NavigationProperty);
    	}
    	internal set {
    		base.SetValue (VisualElement.NavigationPropertyKey, value);
    	}
    }
    

    这里的base是指,父类"Element"的父类"BindableObject"中的方法。

    接下来,我们来看下BindableObject是什么东西?BindableObject的public members如下

      1 using System;
      2 using System.Collections.Generic;
      3 using System.ComponentModel;
      4 using System.Reflection;
      5 using System.Runtime.CompilerServices;
      6 
      7 namespace Xamarin.Forms
      8 {
      9     public abstract class BindableObject : INotifyPropertyChanged
     10     {
     11         //
     12         // Static Fields
     13         //
     14         public static readonly BindableProperty BindingContextProperty = BindableProperty.Create ("BindingContext", typeof(object), typeof(BindableObject), null, BindingMode.OneWay, null, new BindableProperty.BindingPropertyChangedDelegate (BindableObject.BindingContextPropertyBindingPropertyChanged), null, null, new BindableProperty.BindablePropertyBindingChanging (BindableObject.BindingContextPropertyBindingChanging));
     15 
     16         //
     17         // Properties
     18         //
     19         public object BindingContext {
     20             get {
     21                 return this.inheritedContext ?? this.GetValue (BindableObject.BindingContextProperty);
     22             }
     23             set {
     24                 this.SetValue (BindableObject.BindingContextProperty, value);
     25             }
     26         }
     27 
     28         //
     29         // Constructors
     30         //
     31         protected BindableObject ()
     32         {
     33             this.bindings = new Dictionary<BindableProperty, BindingBase> (4);
     34             this.values = new Dictionary<BindableProperty, object> (4);
     35             this.manuallySetValues = new List<BindableProperty> (4);
     36             this.delayedSetters = new Dictionary<BindableProperty, Queue<Action>> (2);
     37             base..ctor ();
     38         }
     39 
     40         //
     41         // Static Methods
     42         //
     43         private static void BindingContextPropertyBindingChanging (BindableObject bindable, BindingBase oldBindingBase, BindingBase newBindingBase)
     44         {
     45             object context = bindable.inheritedContext;
     46             Binding binding = oldBindingBase as Binding;
     47             Binding binding2 = newBindingBase as Binding;
     48             if (context == null && binding != null) {
     49                 context = binding.Context;
     50             }
     51             if (context != null && binding2 != null) {
     52                 binding2.Context = context;
     53             }
     54         }
     55 
     56         private static void BindingContextPropertyBindingPropertyChanged (BindableObject bindable, object oldvalue, object newvalue)
     57         {
     58             object obj = bindable.inheritedContext;
     59             bindable.inheritedContext = null;
     60             bindable.ApplyBindings (obj ?? oldvalue);
     61             bindable.OnBindingContextChanged ();
     62         }
     63 
     64         protected static void SetInheritedBindingContext (BindableObject bindable, object value)
     65         {
     66             if (bindable.HasManuallySetValue (BindableObject.BindingContextProperty)) {
     67                 return;
     68             }
     69             object bindingContext = bindable.BindingContext;
     70             BindingBase bindingBase;
     71             if (bindable.bindings.TryGetValue (BindableObject.BindingContextProperty, out bindingBase)) {
     72                 ((Binding)bindingBase).Context = value;
     73                 bindable.inheritedContext = null;
     74             }
     75             else {
     76                 if (object.ReferenceEquals (bindable.BindingContext, value)) {
     77                     return;
     78                 }
     79                 bindable.inheritedContext = value;
     80             }
     81             bindable.ApplyBindings (bindingContext);
     82             bindable.OnBindingContextChanged ();
     83         }
     84 
     85         //
     86         // Methods
     87         //
     88         protected void ApplyBindings (object oldContext = null)
     89         {
     90             foreach (KeyValuePair<BindableProperty, BindingBase> current in this.bindings) {
     91                 if (oldContext != null) {
     92                     current.Value.Unapply ();
     93                 }
     94                 current.Value.Apply (this.BindingContext, this, current.Key);
     95             }
     96         }
     97 
     98         public void ClearValue (BindableProperty property)
     99         {
    100             this.ClearValue (property, true);
    101         }
    102 
    103         public void ClearValue (BindablePropertyKey propertyKey)
    104         {
    105             if (propertyKey == null) {
    106                 throw new ArgumentNullException ("propertyKey");
    107             }
    108             this.ClearValue (propertyKey.BindableProperty, false);
    109         }
    110 
    111         private void ClearValue (BindableProperty property, bool checkaccess)
    112         {
    113             if (property == null) {
    114                 throw new ArgumentNullException ("property");
    115             }
    116             if (checkaccess && property.IsReadOnly) {
    117                 throw new InvalidOperationException (string.Format ("The BindableProperty "{0}" is readonly.", new object[] {
    118                     property.PropertyName
    119                 }));
    120             }
    121             object value = this.GetValue (property);
    122             object defaultValue = property.DefaultValue;
    123             bool flag = object.Equals (value, defaultValue);
    124             if (!flag) {
    125                 if (property.PropertyChanging != null) {
    126                     property.PropertyChanging (this, value, property.DefaultValue);
    127                 }
    128                 this.OnPropertyChanging (property.PropertyName);
    129             }
    130             this.manuallySetValues.Remove (property);
    131             this.values.Remove (property);
    132             if (!flag) {
    133                 this.OnPropertyChanged (property.PropertyName);
    134                 if (property.PropertyChanged != null) {
    135                     property.PropertyChanged (this, value, property.DefaultValue);
    136                 }
    137             }
    138         }
    139 
    140         internal bool GetIsBound (BindableProperty targetProperty)
    141         {
    142             if (targetProperty == null) {
    143                 throw new ArgumentNullException ("targetProperty");
    144             }
    145             return this.bindings.ContainsKey (targetProperty);
    146         }
    147 
    148         public object GetValue (BindableProperty property)
    149         {
    150             if (property == null) {
    151                 throw new ArgumentNullException ("property");
    152             }
    153             object defaultValue;
    154             if (!this.values.TryGetValue (property, out defaultValue)) {
    155                 defaultValue = property.DefaultValue;
    156             }
    157             return defaultValue;
    158         }
    159 
    160         private bool HasManuallySetValue (BindableProperty property)
    161         {
    162             if (property == null) {
    163                 throw new ArgumentNullException ("property");
    164             }
    165             return this.manuallySetValues.Contains (property);
    166         }
    167 
    168         protected virtual void OnBindingContextChanged ()
    169         {
    170             EventHandler bindingContextChanged = this.BindingContextChanged;
    171             if (bindingContextChanged != null) {
    172                 bindingContextChanged (this, EventArgs.Empty);
    173             }
    174         }
    175 
    176         protected virtual void OnPropertyChanged ([CallerMemberName] string propertyName = null)
    177         {
    178             PropertyChangedEventHandler propertyChanged = this.PropertyChanged;
    179             if (propertyChanged != null) {
    180                 propertyChanged (this, new PropertyChangedEventArgs (propertyName));
    181             }
    182         }
    183 
    184         protected virtual void OnPropertyChanging ([CallerMemberName] string propertyName = null)
    185         {
    186             PropertyChangingEventHandler propertyChanging = this.PropertyChanging;
    187             if (propertyChanging != null) {
    188                 propertyChanging (this, new PropertyChangingEventArgs (propertyName));
    189             }
    190         }
    191 
    192         public void RemoveBinding (BindableProperty property)
    193         {
    194             if (property == null) {
    195                 throw new ArgumentNullException ("property");
    196             }
    197             BindingBase bindingBase;
    198             if (!this.bindings.TryGetValue (property, out bindingBase)) {
    199                 return;
    200             }
    201             bindingBase.Unapply ();
    202             if (property.BindingChanging != null) {
    203                 property.BindingChanging (this, bindingBase, null);
    204             }
    205             this.bindings.Remove (property);
    206         }
    207 
    208         public void SetBinding (BindableProperty targetProperty, BindingBase binding)
    209         {
    210             if (targetProperty == null) {
    211                 throw new ArgumentNullException ("targetProperty");
    212             }
    213             if (binding == null) {
    214                 throw new ArgumentNullException ("binding");
    215             }
    216             BindingBase bindingBase;
    217             if (this.bindings.TryGetValue (targetProperty, out bindingBase)) {
    218                 bindingBase.Unapply ();
    219             }
    220             this.bindings [targetProperty] = binding;
    221             if (targetProperty.BindingChanging != null) {
    222                 targetProperty.BindingChanging (this, bindingBase, binding);
    223             }
    224             binding.Apply (this.BindingContext, this, targetProperty);
    225         }
    226 
    227         private void SetValue (BindableProperty property, object value, bool checkaccess)
    228         {
    229             if (property == null) {
    230                 throw new ArgumentNullException ("property");
    231             }
    232             if (checkaccess && property.IsReadOnly) {
    233                 throw new InvalidOperationException (string.Format ("The BindableProperty "{0}" is readonly.", new object[] {
    234                     property.PropertyName
    235                 }));
    236             }
    237             if (!this.manuallySetValues.Contains (property)) {
    238                 this.manuallySetValues.Add (property);
    239             }
    240             this.SetValueCore (property, value, true, false, checkaccess);
    241         }
    242 
    243         public void SetValue (BindablePropertyKey propertyKey, object value)
    244         {
    245             if (propertyKey == null) {
    246                 throw new ArgumentNullException ("propertyKey");
    247             }
    248             this.SetValue (propertyKey.BindableProperty, value, false);
    249         }
    250 
    251         public void SetValue (BindableProperty property, object value)
    252         {
    253             this.SetValue (property, value, true);
    254         }
    255 
    256         private void SetValueActual (BindableProperty property, object value, bool currentlyApplying, bool clearBindings, bool raiseOnEqual)
    257         {
    258             object defaultValue;
    259             if (!this.values.TryGetValue (property, out defaultValue)) {
    260                 defaultValue = property.DefaultValue;
    261             }
    262             bool flag = object.Equals (value, defaultValue);
    263             if (!flag || raiseOnEqual) {
    264                 if (property.PropertyChanging != null) {
    265                     property.PropertyChanging (this, defaultValue, value);
    266                 }
    267                 this.OnPropertyChanging (property.PropertyName);
    268                 this.values [property] = value;
    269             }
    270             BindingBase bindingBase;
    271             if (this.bindings.TryGetValue (property, out bindingBase) && clearBindings && bindingBase.GetRealizedMode (property) == BindingMode.OneWay) {
    272                 this.RemoveBinding (property);
    273             }
    274             if (!flag || raiseOnEqual) {
    275                 if (bindingBase != null && !currentlyApplying) {
    276                     this.applying = true;
    277                     bindingBase.Apply (true);
    278                     this.applying = false;
    279                 }
    280                 this.OnPropertyChanged (property.PropertyName);
    281                 if (property.PropertyChanged != null) {
    282                     property.PropertyChanged (this, defaultValue, value);
    283                 }
    284             }
    285         }
    286 
    287         internal void SetValueCore (BindableProperty property, object value, bool clearBindings, bool raiseOnEqual = false, bool checkaccess = true)
    288         {
    289             if (property == null) {
    290                 throw new ArgumentNullException ("property");
    291             }
    292             if (checkaccess && property.IsReadOnly) {
    293                 return;
    294             }
    295             if (value != null && !property.ReturnType.IsInstanceOfType (value)) {
    296                 MethodInfo runtimeMethod = property.ReturnType.GetRuntimeMethod ("op_Implicit", new Type[] {
    297                     value.GetType ()
    298                 });
    299                 if (runtimeMethod == null || runtimeMethod.ReturnType != property.ReturnType) {
    300                     Log.Warning ("SetValue", "Can not convert {0} to type '{1}'", new object[] {
    301                         value,
    302                         property.ReturnType
    303                     });
    304                     return;
    305                 }
    306                 value = runtimeMethod.Invoke (null, new object[] {
    307                     value
    308                 });
    309             }
    310             if (property.ValidateValue != null && !property.ValidateValue (this, value)) {
    311                 throw new ArgumentException ("Value was an invalid value for " + property.PropertyName, "value");
    312             }
    313             if (property.CoerceValue != null) {
    314                 value = property.CoerceValue (this, value);
    315             }
    316             bool currentlyApplying = this.applying;
    317             Queue<Action> queue;
    318             if (this.delayedSetters.TryGetValue (property, out queue)) {
    319                 queue.Enqueue (delegate {
    320                     this.SetValueActual (property, value, currentlyApplying, clearBindings, raiseOnEqual);
    321                 });
    322                 return;
    323             }
    324             queue = new Queue<Action> ();
    325             this.delayedSetters.Add (property, queue);
    326             this.SetValueActual (property, value, currentlyApplying, clearBindings, raiseOnEqual);
    327             while (queue.Count > 0) {
    328                 Action action = queue.Dequeue ();
    329                 action ();
    330             }
    331             this.delayedSetters.Remove (property);
    332         }
    333 
    334         protected void UnapplyBindings ()
    335         {
    336             foreach (BindingBase current in this.bindings.Values) {
    337                 current.Unapply ();
    338             }
    339         }
    340 
    341         //
    342         // Events
    343         //
    344         public event PropertyChangingEventHandler PropertyChanging;
    345 
    346         public event PropertyChangedEventHandler PropertyChanged;
    347 
    348         public event EventHandler BindingContextChanged;
    349     }
    350 }
    View Code

     其中有必要说下BindableProperty这个对象,它的public memebers 如下

      1 using System;
      2 using System.Diagnostics;
      3 using System.Linq.Expressions;
      4 using System.Reflection;
      5 
      6 namespace Xamarin.Forms
      7 {
      8     [DebuggerDisplay ("{PropertyName}")]
      9     public sealed class BindableProperty
     10     {
     11         internal delegate void BindablePropertyBindingChanging (BindableObject bindable, BindingBase oldValue, BindingBase newValue);
     12 
     13         public delegate void BindingPropertyChangedDelegate (BindableObject bindable, object oldValue, object newValue);
     14 
     15         public delegate void BindingPropertyChangedDelegate<TPropertyType> (BindableObject bindable, TPropertyType oldValue, TPropertyType newValue);
     16 
     17         public delegate void BindingPropertyChangingDelegate<TPropertyType> (BindableObject bindable, TPropertyType oldValue, TPropertyType newValue);
     18 
     19         public delegate void BindingPropertyChangingDelegate (BindableObject bindable, object oldValue, object newValue);
     20 
     21         public delegate object CoerceValueDelegate (BindableObject bindable, object value);
     22 
     23         public delegate TPropertyType CoerceValueDelegate<TPropertyType> (BindableObject bindable, TPropertyType value);
     24 
     25         public delegate bool ValidateValueDelegate (BindableObject bindable, object value);
     26 
     27         public delegate bool ValidateValueDelegate<TPropertyType> (BindableObject bindable, TPropertyType value);
     28 
     29         //
     30         // Properties
     31         //
     32         internal BindableProperty.BindablePropertyBindingChanging BindingChanging {
     33             get;
     34             private set;
     35         }
     36 
     37         internal BindableProperty.CoerceValueDelegate CoerceValue {
     38             get;
     39             private set;
     40         }
     41 
     42         public Type DeclaringType {
     43             get;
     44             private set;
     45         }
     46 
     47         public BindingMode DefaultBindingMode {
     48             get;
     49             private set;
     50         }
     51 
     52         public object DefaultValue {
     53             get;
     54             private set;
     55         }
     56 
     57         public bool IsReadOnly {
     58             get;
     59             private set;
     60         }
     61 
     62         internal BindableProperty.BindingPropertyChangedDelegate PropertyChanged {
     63             get;
     64             private set;
     65         }
     66 
     67         internal BindableProperty.BindingPropertyChangingDelegate PropertyChanging {
     68             get;
     69             private set;
     70         }
     71 
     72         public string PropertyName {
     73             get;
     74             private set;
     75         }
     76 
     77         public Type ReturnType {
     78             get;
     79             private set;
     80         }
     81 
     82         internal BindableProperty.ValidateValueDelegate ValidateValue {
     83             get;
     84             private set;
     85         }
     86 
     87         //
     88         // Constructors
     89         //
     90         private BindableProperty (string propertyName, Type returnType, Type declaringType, object defaultValue, BindingMode defaultBindingMode = BindingMode.OneWay, BindableProperty.ValidateValueDelegate validateValue = null, BindableProperty.BindingPropertyChangedDelegate propertyChanged = null, BindableProperty.BindingPropertyChangingDelegate propertyChanging = null, BindableProperty.CoerceValueDelegate coerceValue = null, BindableProperty.BindablePropertyBindingChanging bindingChanging = null, bool isReadOnly = false)
     91         {
     92             if (propertyName == null) {
     93                 throw new ArgumentNullException ("propertyName");
     94             }
     95             if (object.ReferenceEquals (returnType, null)) {
     96                 throw new ArgumentNullException ("returnType");
     97             }
     98             if (object.ReferenceEquals (declaringType, null)) {
     99                 throw new ArgumentNullException ("declaringType");
    100             }
    101             if (defaultBindingMode != BindingMode.Default && defaultBindingMode != BindingMode.OneWay && defaultBindingMode != BindingMode.OneWayToSource && defaultBindingMode != BindingMode.TwoWay) {
    102                 throw new ArgumentException ("Not a valid type of BindingMode", "defaultBindingMode");
    103             }
    104             if (defaultValue == null && Nullable.GetUnderlyingType (returnType) == null && returnType.GetTypeInfo ().IsValueType) {
    105                 throw new ArgumentException ("Not a valid default value", "defaultValue");
    106             }
    107             if (defaultValue != null && !returnType.IsInstanceOfType (defaultValue)) {
    108                 throw new ArgumentException ("Default value did not match return type", "defaultValue");
    109             }
    110             if (defaultBindingMode == BindingMode.Default) {
    111                 defaultBindingMode = BindingMode.OneWay;
    112             }
    113             this.PropertyName = propertyName;
    114             this.ReturnType = returnType;
    115             this.DeclaringType = declaringType;
    116             this.DefaultValue = defaultValue;
    117             this.DefaultBindingMode = defaultBindingMode;
    118             this.PropertyChanged = propertyChanged;
    119             this.PropertyChanging = propertyChanging;
    120             this.ValidateValue = validateValue;
    121             this.CoerceValue = coerceValue;
    122             this.BindingChanging = bindingChanging;
    123             this.IsReadOnly = isReadOnly;
    124         }
    125 
    126         //
    127         // Static Methods
    128         //
    129         internal static BindableProperty Create (string propertyName, Type returnType, Type declaringType, object defaultValue, BindingMode defaultBindingMode, BindableProperty.ValidateValueDelegate validateValue, BindableProperty.BindingPropertyChangedDelegate propertyChanged, BindableProperty.BindingPropertyChangingDelegate propertyChanging, BindableProperty.CoerceValueDelegate coerceValue, BindableProperty.BindablePropertyBindingChanging bindingChanging)
    130         {
    131             return new BindableProperty (propertyName, returnType, declaringType, defaultValue, defaultBindingMode, validateValue, propertyChanged, propertyChanging, coerceValue, bindingChanging, false);
    132         }
    133 
    134         internal static BindableProperty Create<TDeclarer, TPropertyType> (Expression<Func<TDeclarer, TPropertyType>> getter, TPropertyType defaultValue, BindingMode defaultBindingMode, BindableProperty.ValidateValueDelegate<TPropertyType> validateValue, BindableProperty.BindingPropertyChangedDelegate<TPropertyType> propertyChanged, BindableProperty.BindingPropertyChangingDelegate<TPropertyType> propertyChanging, BindableProperty.CoerceValueDelegate<TPropertyType> coerceValue, BindableProperty.BindablePropertyBindingChanging bindingChanging, bool isReadOnly = false) where TDeclarer : BindableObject
    135         {
    136             if (getter == null) {
    137                 throw new ArgumentNullException ("getter");
    138             }
    139             Expression expression = getter.Body;
    140             UnaryExpression unaryExpression = expression as UnaryExpression;
    141             if (unaryExpression != null) {
    142                 expression = unaryExpression.Operand;
    143             }
    144             MemberExpression memberExpression = expression as MemberExpression;
    145             if (memberExpression == null) {
    146                 throw new ArgumentException ("getter must be a MemberExpression", "getter");
    147             }
    148             PropertyInfo propertyInfo = (PropertyInfo)memberExpression.Member;
    149             BindableProperty.ValidateValueDelegate validateValue2 = null;
    150             BindableProperty.BindingPropertyChangedDelegate propertyChanged2 = null;
    151             BindableProperty.BindingPropertyChangingDelegate propertyChanging2 = null;
    152             BindableProperty.CoerceValueDelegate coerceValue2 = null;
    153             if (validateValue != null) {
    154                 validateValue2 = ((BindableObject bindable, object value) => validateValue (bindable, (TPropertyType)((object)value)));
    155             }
    156             if (propertyChanged != null) {
    157                 propertyChanged2 = delegate (BindableObject bindable, object oldValue, object newValue) {
    158                     propertyChanged (bindable, (TPropertyType)((object)oldValue), (TPropertyType)((object)newValue));
    159                 };
    160             }
    161             if (propertyChanging != null) {
    162                 propertyChanging2 = delegate (BindableObject bindable, object oldValue, object newValue) {
    163                     propertyChanging (bindable, (TPropertyType)((object)oldValue), (TPropertyType)((object)newValue));
    164                 };
    165             }
    166             if (coerceValue != null) {
    167                 coerceValue2 = ((BindableObject bindable, object value) => coerceValue (bindable, (TPropertyType)((object)value)));
    168             }
    169             return new BindableProperty (propertyInfo.Name, propertyInfo.PropertyType, typeof(TDeclarer), defaultValue, defaultBindingMode, validateValue2, propertyChanged2, propertyChanging2, coerceValue2, bindingChanging, isReadOnly);
    170         }
    171 
    172         public static BindableProperty Create (string propertyName, Type returnType, Type declaringType, object defaultValue, BindingMode defaultBindingMode = BindingMode.OneWay, BindableProperty.ValidateValueDelegate validateValue = null, BindableProperty.BindingPropertyChangedDelegate propertyChanged = null, BindableProperty.BindingPropertyChangingDelegate propertyChanging = null, BindableProperty.CoerceValueDelegate coerceValue = null)
    173         {
    174             return new BindableProperty (propertyName, returnType, declaringType, defaultValue, defaultBindingMode, validateValue, propertyChanged, propertyChanging, coerceValue, null, false);
    175         }
    176 
    177         public static BindableProperty Create<TDeclarer, TPropertyType> (Expression<Func<TDeclarer, TPropertyType>> getter, TPropertyType defaultValue, BindingMode defaultBindingMode = BindingMode.OneWay, BindableProperty.ValidateValueDelegate<TPropertyType> validateValue = null, BindableProperty.BindingPropertyChangedDelegate<TPropertyType> propertyChanged = null, BindableProperty.BindingPropertyChangingDelegate<TPropertyType> propertyChanging = null, BindableProperty.CoerceValueDelegate<TPropertyType> coerceValue = null) where TDeclarer : BindableObject
    178         {
    179             return BindableProperty.Create<TDeclarer, TPropertyType> (getter, defaultValue, defaultBindingMode, validateValue, propertyChanged, propertyChanging, coerceValue, null, false);
    180         }
    181 
    182         internal static BindableProperty CreateAttached (string propertyName, Type returnType, Type declaringType, object defaultValue, BindingMode defaultBindingMode, BindableProperty.ValidateValueDelegate validateValue, BindableProperty.BindingPropertyChangedDelegate propertyChanged, BindableProperty.BindingPropertyChangingDelegate propertyChanging, BindableProperty.CoerceValueDelegate coerceValue, BindableProperty.BindablePropertyBindingChanging bindingChanging, bool isReadOnly)
    183         {
    184             return new BindableProperty (propertyName, returnType, declaringType, defaultValue, defaultBindingMode, validateValue, propertyChanged, propertyChanging, coerceValue, bindingChanging, isReadOnly);
    185         }
    186 
    187         public static BindableProperty CreateAttached (string propertyName, Type returnType, Type declaringType, object defaultValue, BindingMode defaultBindingMode = BindingMode.OneWay, BindableProperty.ValidateValueDelegate validateValue = null, BindableProperty.BindingPropertyChangedDelegate propertyChanged = null, BindableProperty.BindingPropertyChangingDelegate propertyChanging = null, BindableProperty.CoerceValueDelegate coerceValue = null)
    188         {
    189             return BindableProperty.CreateAttached (propertyName, returnType, declaringType, defaultValue, defaultBindingMode, validateValue, propertyChanged, propertyChanging, coerceValue, null, false);
    190         }
    191 
    192         internal static BindableProperty CreateAttached<TDeclarer, TPropertyType> (Expression<Func<BindableObject, TPropertyType>> staticgetter, TPropertyType defaultValue, BindingMode defaultBindingMode, BindableProperty.ValidateValueDelegate<TPropertyType> validateValue, BindableProperty.BindingPropertyChangedDelegate<TPropertyType> propertyChanged, BindableProperty.BindingPropertyChangingDelegate<TPropertyType> propertyChanging, BindableProperty.CoerceValueDelegate<TPropertyType> coerceValue, BindableProperty.BindablePropertyBindingChanging bindingChanging, bool isReadOnly = false)
    193         {
    194             if (staticgetter == null) {
    195                 throw new ArgumentNullException ("staticgetter");
    196             }
    197             Expression expression = staticgetter.Body;
    198             UnaryExpression unaryExpression = expression as UnaryExpression;
    199             if (unaryExpression != null) {
    200                 expression = unaryExpression.Operand;
    201             }
    202             MethodCallExpression methodCallExpression = expression as MethodCallExpression;
    203             if (methodCallExpression == null) {
    204                 throw new ArgumentException ("staticgetter must be a MethodCallExpression", "staticgetter");
    205             }
    206             MethodInfo method = methodCallExpression.Method;
    207             if (!method.Name.StartsWith ("Get", StringComparison.Ordinal)) {
    208                 throw new ArgumentException ("staticgetter name must start with Get", "staticgetter");
    209             }
    210             string propertyName = method.Name.Substring (3);
    211             BindableProperty.ValidateValueDelegate validateValue2 = null;
    212             BindableProperty.BindingPropertyChangedDelegate propertyChanged2 = null;
    213             BindableProperty.BindingPropertyChangingDelegate propertyChanging2 = null;
    214             BindableProperty.CoerceValueDelegate coerceValue2 = null;
    215             if (validateValue != null) {
    216                 validateValue2 = ((BindableObject bindable, object value) => validateValue (bindable, (TPropertyType)((object)value)));
    217             }
    218             if (propertyChanged != null) {
    219                 propertyChanged2 = delegate (BindableObject bindable, object oldValue, object newValue) {
    220                     propertyChanged (bindable, (TPropertyType)((object)oldValue), (TPropertyType)((object)newValue));
    221                 };
    222             }
    223             if (propertyChanging != null) {
    224                 propertyChanging2 = delegate (BindableObject bindable, object oldValue, object newValue) {
    225                     propertyChanging (bindable, (TPropertyType)((object)oldValue), (TPropertyType)((object)newValue));
    226                 };
    227             }
    228             if (coerceValue != null) {
    229                 coerceValue2 = ((BindableObject bindable, object value) => coerceValue (bindable, (TPropertyType)((object)value)));
    230             }
    231             return new BindableProperty (propertyName, method.ReturnType, typeof(TDeclarer), defaultValue, defaultBindingMode, validateValue2, propertyChanged2, propertyChanging2, coerceValue2, bindingChanging, isReadOnly);
    232         }
    233 
    234         public static BindableProperty CreateAttached<TDeclarer, TPropertyType> (Expression<Func<BindableObject, TPropertyType>> staticgetter, TPropertyType defaultValue, BindingMode defaultBindingMode = BindingMode.OneWay, BindableProperty.ValidateValueDelegate<TPropertyType> validateValue = null, BindableProperty.BindingPropertyChangedDelegate<TPropertyType> propertyChanged = null, BindableProperty.BindingPropertyChangingDelegate<TPropertyType> propertyChanging = null, BindableProperty.CoerceValueDelegate<TPropertyType> coerceValue = null)
    235         {
    236             return BindableProperty.CreateAttached<TDeclarer, TPropertyType> (staticgetter, defaultValue, defaultBindingMode, validateValue, propertyChanged, propertyChanging, coerceValue, null, false);
    237         }
    238 
    239         public static BindablePropertyKey CreateAttachedReadOnly<TDeclarer, TPropertyType> (Expression<Func<BindableObject, TPropertyType>> staticgetter, TPropertyType defaultValue, BindingMode defaultBindingMode = BindingMode.OneWayToSource, BindableProperty.ValidateValueDelegate<TPropertyType> validateValue = null, BindableProperty.BindingPropertyChangedDelegate<TPropertyType> propertyChanged = null, BindableProperty.BindingPropertyChangingDelegate<TPropertyType> propertyChanging = null, BindableProperty.CoerceValueDelegate<TPropertyType> coerceValue = null)
    240         {
    241             return new BindablePropertyKey (BindableProperty.CreateAttached<TDeclarer, TPropertyType> (staticgetter, defaultValue, defaultBindingMode, validateValue, propertyChanged, propertyChanging, coerceValue, null, true));
    242         }
    243 
    244         public static BindablePropertyKey CreateAttachedReadOnly (string propertyName, Type returnType, Type declaringType, object defaultValue, BindingMode defaultBindingMode = BindingMode.OneWayToSource, BindableProperty.ValidateValueDelegate validateValue = null, BindableProperty.BindingPropertyChangedDelegate propertyChanged = null, BindableProperty.BindingPropertyChangingDelegate propertyChanging = null, BindableProperty.CoerceValueDelegate coerceValue = null)
    245         {
    246             return new BindablePropertyKey (BindableProperty.CreateAttached (propertyName, returnType, declaringType, defaultValue, defaultBindingMode, validateValue, propertyChanged, propertyChanging, coerceValue, null, true));
    247         }
    248 
    249         public static BindablePropertyKey CreateReadOnly (string propertyName, Type returnType, Type declaringType, object defaultValue, BindingMode defaultBindingMode = BindingMode.OneWayToSource, BindableProperty.ValidateValueDelegate validateValue = null, BindableProperty.BindingPropertyChangedDelegate propertyChanged = null, BindableProperty.BindingPropertyChangingDelegate propertyChanging = null, BindableProperty.CoerceValueDelegate coerceValue = null)
    250         {
    251             return new BindablePropertyKey (new BindableProperty (propertyName, returnType, declaringType, defaultValue, defaultBindingMode, validateValue, propertyChanged, propertyChanging, coerceValue, null, true));
    252         }
    253 
    254         public static BindablePropertyKey CreateReadOnly<TDeclarer, TPropertyType> (Expression<Func<TDeclarer, TPropertyType>> getter, TPropertyType defaultValue, BindingMode defaultBindingMode = BindingMode.OneWayToSource, BindableProperty.ValidateValueDelegate<TPropertyType> validateValue = null, BindableProperty.BindingPropertyChangedDelegate<TPropertyType> propertyChanged = null, BindableProperty.BindingPropertyChangingDelegate<TPropertyType> propertyChanging = null, BindableProperty.CoerceValueDelegate<TPropertyType> coerceValue = null) where TDeclarer : BindableObject
    255         {
    256             return new BindablePropertyKey (BindableProperty.Create<TDeclarer, TPropertyType> (getter, defaultValue, defaultBindingMode, validateValue, propertyChanged, propertyChanging, coerceValue, null, true));
    257         }
    258     }
    259 }
    View Code

    好的,看到BindableObject中的GetValue()方法了吧:

     1 public object GetValue (BindableProperty property)
     2 {
     3     if (property == null) {
     4         throw new ArgumentNullException ("property");
     5     }
     6     object defaultValue;
     7     if (!this.values.TryGetValue (property, out defaultValue)) {
     8         defaultValue = property.DefaultValue;
     9     }
    10     return defaultValue;
    11 }

    7~9行是核心的获取方法,通过查看到非公共部分的代码,可以看到这个values为:

    private readonly Dictionary<BindableProperty, object> values;
    

    我们可能关心的是这个方法返回后的Object对象是什么?其实我们可以先把程序跑起来看下具体的返回值:

     

    看到是什么东西了吧,是一个NavigationProxy对象,那么我们在找到这个对象看下它的内容吧:

    具体代码如下:

      1 using System;
      2 using System.Collections.Generic;
      3 using System.Linq;
      4 using System.Threading.Tasks;
      5 
      6 namespace Xamarin.Forms
      7 {
      8     internal class NavigationProxy : INavigation
      9     {
     10         //
     11         // Fields
     12         //
     13         private INavigation inner;
     14 
     15         private Lazy<Stack<Page>> pushStack = new Lazy<Stack<Page>> (() => new Stack<Page> ());
     16 
     17         private Lazy<Stack<Page>> modalStack = new Lazy<Stack<Page>> (() => new Stack<Page> ());
     18 
     19         //
     20         // Properties
     21         //
     22         public INavigation Inner {
     23             get {
     24                 return this.inner;
     25             }
     26             set {
     27                 if (this.inner == value) {
     28                     return;
     29                 }
     30                 this.inner = value;
     31                 if (object.ReferenceEquals (this.inner, null)) {
     32                     this.pushStack = new Lazy<Stack<Page>> (() => new Stack<Page> ());
     33                     this.modalStack = new Lazy<Stack<Page>> (() => new Stack<Page> ());
     34                     return;
     35                 }
     36                 if (this.pushStack.IsValueCreated) {
     37                     foreach (Page current in this.pushStack.Value.Reverse<Page> ()) {
     38                         this.inner.PushAsync (current);
     39                     }
     40                 }
     41                 if (this.modalStack.IsValueCreated) {
     42                     foreach (Page current2 in this.modalStack.Value.Reverse<Page> ()) {
     43                         this.inner.PushModalAsync (current2);
     44                     }
     45                 }
     46                 this.pushStack = null;
     47                 this.modalStack = null;
     48             }
     49         }
     50 
     51         //
     52         // Methods
     53         //
     54         protected virtual Task<Page> OnPopAsync ()
     55         {
     56             INavigation navigation = this.Inner;
     57             if (navigation != null) {
     58                 return navigation.PopAsync ();
     59             }
     60             return Task.FromResult<Page> (this.pushStack.Value.Pop ());
     61         }
     62 
     63         protected virtual Task<Page> OnPopModal ()
     64         {
     65             INavigation navigation = this.Inner;
     66             if (navigation != null) {
     67                 return navigation.PopModalAsync ();
     68             }
     69             return Task.FromResult<Page> (this.modalStack.Value.Pop ());
     70         }
     71 
     72         protected virtual Task OnPopToRootAsync ()
     73         {
     74             INavigation navigation = this.Inner;
     75             if (navigation == null) {
     76                 Page page = this.pushStack.Value.Last<Page> ();
     77                 this.pushStack.Value.Clear ();
     78                 this.pushStack.Value.Push (page);
     79                 return Task.FromResult<Page> (page);
     80             }
     81             return navigation.PopToRootAsync ();
     82         }
     83 
     84         protected virtual Task OnPushAsync (Page page)
     85         {
     86             INavigation navigation = this.Inner;
     87             if (navigation == null) {
     88                 this.pushStack.Value.Push (page);
     89                 return Task.FromResult<Page> (page);
     90             }
     91             return navigation.PushAsync (page);
     92         }
     93 
     94         protected virtual Task OnPushModal (Page modal)
     95         {
     96             INavigation navigation = this.Inner;
     97             if (navigation == null) {
     98                 this.modalStack.Value.Push (modal);
     99                 return Task.FromResult<object> (null);
    100             }
    101             return navigation.PushModalAsync (modal);
    102         }
    103 
    104         public Task<Page> PopAsync ()
    105         {
    106             return this.OnPopAsync ();
    107         }
    108 
    109         public Task<Page> PopModalAsync ()
    110         {
    111             return this.OnPopModal ();
    112         }
    113 
    114         public Task PopToRootAsync ()
    115         {
    116             return this.OnPopToRootAsync ();
    117         }
    118 
    119         public Task PushAsync (Page root)
    120         {
    121             if (root.Parent != null) {
    122                 throw new InvalidOperationException ("Page must not already have a parent.");
    123             }
    124             return this.OnPushAsync (root);
    125         }
    126 
    127         public Task PushModalAsync (Page modal)
    128         {
    129             if (modal.Parent != null) {
    130                 throw new InvalidOperationException ("Page must not already have a parent.");
    131             }
    132             return this.OnPushModal (modal);
    133         }
    134     }
    135 }
    View Code

    怎么样,看到里面就是我们要找的东西,那5个方法的具体实现。

    可能我们还有一个疑问,那就是在BindableObject中通过 Dictionary<BindableProperty,Object>是如何获取到NavigationProxy的?

    在BindableObject中我们查找下所有使用values的地方,发现只有一个地方给这个对象赋值了:方法 SetValueActual

     1 private void SetValueActual (BindableProperty property, object value, bool currentlyApplying, bool clearBindings, bool raiseOnEqual)
     2         {
     3             object defaultValue;
     4             if (!this.values.TryGetValue (property, out defaultValue)) {
     5                 defaultValue = property.DefaultValue;
     6             }
     7             bool flag = object.Equals (value, defaultValue);
     8             if (!flag || raiseOnEqual) {
     9                 if (property.PropertyChanging != null) {
    10                     property.PropertyChanging (this, defaultValue, value);
    11                 }
    12                 this.OnPropertyChanging (property.PropertyName);
    13                 this.values [property] = value;
    14             }
    15             BindingBase bindingBase;
    16             if (this.bindings.TryGetValue (property, out bindingBase) && clearBindings && bindingBase.GetRealizedMode (property) == BindingMode.OneWay) {
    17                 this.RemoveBinding (property);
    18             }
    19             if (!flag || raiseOnEqual) {
    20                 if (bindingBase != null && !currentlyApplying) {
    21                     this.applying = true;
    22                     bindingBase.Apply (true);
    23                     this.applying = false;
    24                 }
    25                 this.OnPropertyChanged (property.PropertyName);
    26                 if (property.PropertyChanged != null) {
    27                     property.PropertyChanged (this, defaultValue, value);
    28                 }
    29             }
    30         }
    View Code

    这个方法被SetValueCore方法引用:

     1 internal void SetValueCore (BindableProperty property, object value, bool clearBindings, bool raiseOnEqual = false, bool checkaccess = true)
     2         {
     3             if (property == null) {
     4                 throw new ArgumentNullException ("property");
     5             }
     6             if (checkaccess && property.IsReadOnly) {
     7                 return;
     8             }
     9             if (value != null && !property.ReturnType.IsInstanceOfType (value)) {
    10                 MethodInfo runtimeMethod = property.ReturnType.GetRuntimeMethod ("op_Implicit", new Type[] {
    11                     value.GetType ()
    12                 });
    13                 if (runtimeMethod == null || runtimeMethod.ReturnType != property.ReturnType) {
    14                     Log.Warning ("SetValue", "Can not convert {0} to type '{1}'", new object[] {
    15                         value,
    16                         property.ReturnType
    17                     });
    18                     return;
    19                 }
    20                 value = runtimeMethod.Invoke (null, new object[] {
    21                     value
    22                 });
    23             }
    24             if (property.ValidateValue != null && !property.ValidateValue (this, value)) {
    25                 throw new ArgumentException ("Value was an invalid value for " + property.PropertyName, "value");
    26             }
    27             if (property.CoerceValue != null) {
    28                 value = property.CoerceValue (this, value);
    29             }
    30             bool currentlyApplying = this.applying;
    31             Queue<Action> queue;
    32             if (this.delayedSetters.TryGetValue (property, out queue)) {
    33                 queue.Enqueue (delegate {
    34                     this.SetValueActual (property, value, currentlyApplying, clearBindings, raiseOnEqual);
    35                 });
    36                 return;
    37             }
    38             queue = new Queue<Action> ();
    39             this.delayedSetters.Add (property, queue);
    40             this.SetValueActual (property, value, currentlyApplying, clearBindings, raiseOnEqual);
    41             while (queue.Count > 0) {
    42                 Action action = queue.Dequeue ();
    43                 action ();
    44             }
    45             this.delayedSetters.Remove (property);
    46         }
    View Code

    而SetValueCore又被SetValue方法引用:

     1 private void SetValue (BindableProperty property, object value, bool checkaccess)
     2         {
     3             if (property == null) {
     4                 throw new ArgumentNullException ("property");
     5             }
     6             if (checkaccess && property.IsReadOnly) {
     7                 throw new InvalidOperationException (string.Format ("The BindableProperty "{0}" is readonly.", new object[] {
     8                     property.PropertyName
     9                 }));
    10             }
    11             if (!this.manuallySetValues.Contains (property)) {
    12                 this.manuallySetValues.Add (property);
    13             }
    14             this.SetValueCore (property, value, true, false, checkaccess);
    15         }
    View Code

    但是我们一直没有找到谁在调用SetValue方法,那就往下看看子类中是否调用了,在子类中搜索base.SetValue,结果找到了下面的代码

     1 public INavigation Navigation {
     2             get {
     3                 return (INavigation)base.GetValue  (VisualElement.NavigationProperty);
     4             }
     5             internal set {
     6                 base.SetValue (VisualElement.NavigationPropertyKey, value);
     7             }
     8 }
     9 
    10 internal NavigationProxy NavigationProxy {
    11   get {
    12         return this.Navigation as NavigationProxy;
    13     }
    14 }
    View Code

    我们发现了Navigation和NavigationProxy的关系,这个NavigationProxy是由Navigation转过来的。

    但是我们如何理解在Navigation属性的set访问器中是如何将 NavigationProxy对象赋值的?这个还需要再详细的梳理下。

    下面我们看下这个Navigation.PushAsync()是如何实现页面跳转的。

  • 相关阅读:
    31天重构学习笔记23. 引入参数对象
    31天重构学习笔记31. 使用多态代替条件判断
    31天重构学习笔记25. 引入契约式设计
    新加坡面试经历
    Release a AutoUpdater tool
    31天重构学习笔记24. 分解复杂判断
    31天重构学习笔记29. 去除中间人对象
    自动更新组件分享
    WPF基础到企业应用系列7——深入剖析依赖属性(WPF/Silverlight核心)
    (收藏)2010年度十大杰出IT博客
  • 原文地址:https://www.cnblogs.com/lvfeilong/p/4654gfhg.html
Copyright © 2020-2023  润新知