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的具体内容:
这个对象中有一个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 }
其中有必要说下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 }
好的,看到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 }
怎么样,看到里面就是我们要找的东西,那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 }
这个方法被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 }
而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 }
但是我们一直没有找到谁在调用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 }
我们发现了Navigation和NavigationProxy的关系,这个NavigationProxy是由Navigation转过来的。
但是我们如何理解在Navigation属性的set访问器中是如何将 NavigationProxy对象赋值的?这个还需要再详细的梳理下。
下面我们看下这个Navigation.PushAsync()是如何实现页面跳转的。