summaryrefslogtreecommitdiff
path: root/Xamarin.Forms.Core
diff options
context:
space:
mode:
Diffstat (limited to 'Xamarin.Forms.Core')
-rw-r--r--Xamarin.Forms.Core/AbsoluteLayout.cs313
-rw-r--r--Xamarin.Forms.Core/AbsoluteLayoutFlags.cs17
-rw-r--r--Xamarin.Forms.Core/ActionSheetArguments.cs45
-rw-r--r--Xamarin.Forms.Core/ActivityIndicator.cs24
-rw-r--r--Xamarin.Forms.Core/AlertArguments.cs43
-rw-r--r--Xamarin.Forms.Core/AnimatableKey.cs82
-rw-r--r--Xamarin.Forms.Core/Animation.cs138
-rw-r--r--Xamarin.Forms.Core/AnimationExtensions.cs259
-rw-r--r--Xamarin.Forms.Core/Application.cs308
-rw-r--r--Xamarin.Forms.Core/Aspect.cs9
-rw-r--r--Xamarin.Forms.Core/BackButtonPressedEventArgs.cs9
-rw-r--r--Xamarin.Forms.Core/BaseMenuItem.cs6
-rw-r--r--Xamarin.Forms.Core/BindableObject.cs647
-rw-r--r--Xamarin.Forms.Core/BindableObjectExtensions.cs34
-rw-r--r--Xamarin.Forms.Core/BindableProperty.cs331
-rw-r--r--Xamarin.Forms.Core/BindablePropertyConverter.cs105
-rw-r--r--Xamarin.Forms.Core/BindablePropertyKey.cs17
-rw-r--r--Xamarin.Forms.Core/Binding.cs233
-rw-r--r--Xamarin.Forms.Core/BindingBase.cs111
-rw-r--r--Xamarin.Forms.Core/BindingBaseExtensions.cs17
-rw-r--r--Xamarin.Forms.Core/BindingExpression.cs506
-rw-r--r--Xamarin.Forms.Core/BindingMode.cs10
-rw-r--r--Xamarin.Forms.Core/BindingTypeConverter.cs10
-rw-r--r--Xamarin.Forms.Core/BoundsConstraint.cs34
-rw-r--r--Xamarin.Forms.Core/BoundsTypeConverter.cs42
-rw-r--r--Xamarin.Forms.Core/BoxView.cs23
-rw-r--r--Xamarin.Forms.Core/Button.cs251
-rw-r--r--Xamarin.Forms.Core/CarouselPage.cs17
-rw-r--r--Xamarin.Forms.Core/CarouselView.cs86
-rw-r--r--Xamarin.Forms.Core/CastingEnumerator.cs46
-rw-r--r--Xamarin.Forms.Core/Cells/Cell.cs209
-rw-r--r--Xamarin.Forms.Core/Cells/EntryCell.cs80
-rw-r--r--Xamarin.Forms.Core/Cells/INativeElementView.cs7
-rw-r--r--Xamarin.Forms.Core/Cells/ImageCell.cs56
-rw-r--r--Xamarin.Forms.Core/Cells/SwitchCell.cs31
-rw-r--r--Xamarin.Forms.Core/Cells/TextCell.cs94
-rw-r--r--Xamarin.Forms.Core/Cells/ViewCell.cs48
-rw-r--r--Xamarin.Forms.Core/ChatKeyboard.cs6
-rw-r--r--Xamarin.Forms.Core/ChildCollectionChangedEventArgs.cs15
-rw-r--r--Xamarin.Forms.Core/CollectionSynchronizationCallback.cs7
-rw-r--r--Xamarin.Forms.Core/CollectionSynchronizationContext.cs22
-rw-r--r--Xamarin.Forms.Core/Color.cs375
-rw-r--r--Xamarin.Forms.Core/ColorTypeConverter.cs72
-rw-r--r--Xamarin.Forms.Core/ColumnDefinition.cs34
-rw-r--r--Xamarin.Forms.Core/ColumnDefinitionCollection.cs6
-rw-r--r--Xamarin.Forms.Core/Command.cs80
-rw-r--r--Xamarin.Forms.Core/ConcurrentDictionary.cs426
-rw-r--r--Xamarin.Forms.Core/Constraint.cs56
-rw-r--r--Xamarin.Forms.Core/ConstraintExpression.cs53
-rw-r--r--Xamarin.Forms.Core/ConstraintType.cs9
-rw-r--r--Xamarin.Forms.Core/ConstraintTypeConverter.cs17
-rw-r--r--Xamarin.Forms.Core/ContentPage.cs26
-rw-r--r--Xamarin.Forms.Core/ContentPresenter.cs91
-rw-r--r--Xamarin.Forms.Core/ContentPropertyAttribute.cs26
-rw-r--r--Xamarin.Forms.Core/ContentView.cs26
-rw-r--r--Xamarin.Forms.Core/ControlTemplate.cs15
-rw-r--r--Xamarin.Forms.Core/CustomKeyboard.cs12
-rw-r--r--Xamarin.Forms.Core/DataTemplate.cs80
-rw-r--r--Xamarin.Forms.Core/DataTemplateExtensions.cs15
-rw-r--r--Xamarin.Forms.Core/DataTemplateSelector.cs17
-rw-r--r--Xamarin.Forms.Core/DateChangedEventArgs.cs17
-rw-r--r--Xamarin.Forms.Core/DatePicker.cs99
-rw-r--r--Xamarin.Forms.Core/DefinitionCollection.cs109
-rw-r--r--Xamarin.Forms.Core/DelegateLogListener.cs22
-rw-r--r--Xamarin.Forms.Core/DependencyAttribute.cs15
-rw-r--r--Xamarin.Forms.Core/DependencyFetchTarget.cs8
-rw-r--r--Xamarin.Forms.Core/DependencyService.cs102
-rw-r--r--Xamarin.Forms.Core/Device.cs159
-rw-r--r--Xamarin.Forms.Core/DeviceInfo.cs51
-rw-r--r--Xamarin.Forms.Core/DeviceOrientation.cs13
-rw-r--r--Xamarin.Forms.Core/DeviceOrientationExtensions.cs15
-rw-r--r--Xamarin.Forms.Core/Easing.cs98
-rw-r--r--Xamarin.Forms.Core/Editor.cs67
-rw-r--r--Xamarin.Forms.Core/Effect.cs70
-rw-r--r--Xamarin.Forms.Core/Element.cs584
-rw-r--r--Xamarin.Forms.Core/ElementCollection.cs11
-rw-r--r--Xamarin.Forms.Core/ElementEventArgs.cs17
-rw-r--r--Xamarin.Forms.Core/ElementTemplate.cs96
-rw-r--r--Xamarin.Forms.Core/EmailKeyboard.cs6
-rw-r--r--Xamarin.Forms.Core/Entry.cs99
-rw-r--r--Xamarin.Forms.Core/EnumerableExtensions.cs81
-rw-r--r--Xamarin.Forms.Core/EventArg.cs18
-rw-r--r--Xamarin.Forms.Core/ExportEffectAttribute.cs20
-rw-r--r--Xamarin.Forms.Core/ExpressionSearch.cs7
-rw-r--r--Xamarin.Forms.Core/FileAccess.cs9
-rw-r--r--Xamarin.Forms.Core/FileImageSource.cs38
-rw-r--r--Xamarin.Forms.Core/FileImageSourceConverter.cs15
-rw-r--r--Xamarin.Forms.Core/FileMode.cs12
-rw-r--r--Xamarin.Forms.Core/FileShare.cs15
-rw-r--r--Xamarin.Forms.Core/FocusEventArgs.cs20
-rw-r--r--Xamarin.Forms.Core/Font.cs145
-rw-r--r--Xamarin.Forms.Core/FontAttributes.cs12
-rw-r--r--Xamarin.Forms.Core/FontSizeConverter.cs48
-rw-r--r--Xamarin.Forms.Core/FontTypeConverter.cs88
-rw-r--r--Xamarin.Forms.Core/FormattedString.cs99
-rw-r--r--Xamarin.Forms.Core/Frame.cs30
-rw-r--r--Xamarin.Forms.Core/GestureRecognizer.cs9
-rw-r--r--Xamarin.Forms.Core/GestureState.cs12
-rw-r--r--Xamarin.Forms.Core/GestureStatus.cs10
-rw-r--r--Xamarin.Forms.Core/Grid.cs359
-rw-r--r--Xamarin.Forms.Core/GridCalc.cs698
-rw-r--r--Xamarin.Forms.Core/GridLength.cs74
-rw-r--r--Xamarin.Forms.Core/GridLengthTypeConverter.cs27
-rw-r--r--Xamarin.Forms.Core/GridUnitType.cs9
-rw-r--r--Xamarin.Forms.Core/HandlerAttribute.cs23
-rw-r--r--Xamarin.Forms.Core/HtmlWebViewSource.cs28
-rw-r--r--Xamarin.Forms.Core/IAnimatable.cs34
-rw-r--r--Xamarin.Forms.Core/IApplicationController.cs6
-rw-r--r--Xamarin.Forms.Core/IButtonController.cs7
-rw-r--r--Xamarin.Forms.Core/ICarouselViewController.cs10
-rw-r--r--Xamarin.Forms.Core/IControlTemplated.cs11
-rw-r--r--Xamarin.Forms.Core/IDefinition.cs9
-rw-r--r--Xamarin.Forms.Core/IDeserializer.cs11
-rw-r--r--Xamarin.Forms.Core/IEffectControlProvider.cs7
-rw-r--r--Xamarin.Forms.Core/IElement.cs13
-rw-r--r--Xamarin.Forms.Core/IElementController.cs10
-rw-r--r--Xamarin.Forms.Core/IExpressionSearch.cs10
-rw-r--r--Xamarin.Forms.Core/IExtendedTypeConverter.cs13
-rw-r--r--Xamarin.Forms.Core/IFontElement.cs11
-rw-r--r--Xamarin.Forms.Core/IGestureRecognizer.cs8
-rw-r--r--Xamarin.Forms.Core/IIsolatedStorageFile.cs18
-rw-r--r--Xamarin.Forms.Core/IItemViewController.cs10
-rw-r--r--Xamarin.Forms.Core/IItemsView.cs9
-rw-r--r--Xamarin.Forms.Core/ILayout.cs9
-rw-r--r--Xamarin.Forms.Core/ILayoutController.cs9
-rw-r--r--Xamarin.Forms.Core/IListViewController.cs15
-rw-r--r--Xamarin.Forms.Core/IMarkupExtension.cs14
-rw-r--r--Xamarin.Forms.Core/INavigation.cs28
-rw-r--r--Xamarin.Forms.Core/IOpenGlViewController.cs9
-rw-r--r--Xamarin.Forms.Core/IPageContainer.cs7
-rw-r--r--Xamarin.Forms.Core/IPanGestureController.cs13
-rw-r--r--Xamarin.Forms.Core/IPinchGestureController.cs15
-rw-r--r--Xamarin.Forms.Core/IPlatform.cs7
-rw-r--r--Xamarin.Forms.Core/IPlatformServices.cs36
-rw-r--r--Xamarin.Forms.Core/IProvideParentValues.cs9
-rw-r--r--Xamarin.Forms.Core/IProvideValueTarget.cs9
-rw-r--r--Xamarin.Forms.Core/IRegisterable.cs6
-rw-r--r--Xamarin.Forms.Core/IResourceDictionary.cs12
-rw-r--r--Xamarin.Forms.Core/IResourcesProvider.cs7
-rw-r--r--Xamarin.Forms.Core/IRootObjectProvider.cs7
-rw-r--r--Xamarin.Forms.Core/IScrollViewController.cs15
-rw-r--r--Xamarin.Forms.Core/IStyle.cs12
-rw-r--r--Xamarin.Forms.Core/ISystemResourcesProvider.cs7
-rw-r--r--Xamarin.Forms.Core/ITimer.cs13
-rw-r--r--Xamarin.Forms.Core/IValueConverter.cs11
-rw-r--r--Xamarin.Forms.Core/IValueConverterProvider.cs10
-rw-r--r--Xamarin.Forms.Core/IValueProvider.cs9
-rw-r--r--Xamarin.Forms.Core/IViewContainer.cs9
-rw-r--r--Xamarin.Forms.Core/IViewController.cs6
-rw-r--r--Xamarin.Forms.Core/IVisualElementController.cs7
-rw-r--r--Xamarin.Forms.Core/IWebViewRenderer.cs8
-rw-r--r--Xamarin.Forms.Core/IXamlTypeResolver.cs10
-rw-r--r--Xamarin.Forms.Core/IXmlLineInfoProvider.cs9
-rw-r--r--Xamarin.Forms.Core/Image.cs145
-rw-r--r--Xamarin.Forms.Core/ImageSource.cs142
-rw-r--r--Xamarin.Forms.Core/ImageSourceConverter.cs18
-rw-r--r--Xamarin.Forms.Core/InputView.cs18
-rw-r--r--Xamarin.Forms.Core/Interactivity/AttachedCollection.cs127
-rw-r--r--Xamarin.Forms.Core/Interactivity/Behavior.cs65
-rw-r--r--Xamarin.Forms.Core/Interactivity/BindingCondition.cs100
-rw-r--r--Xamarin.Forms.Core/Interactivity/Condition.cs51
-rw-r--r--Xamarin.Forms.Core/Interactivity/DataTrigger.cs57
-rw-r--r--Xamarin.Forms.Core/Interactivity/EventTrigger.cs91
-rw-r--r--Xamarin.Forms.Core/Interactivity/IAttachedObject.cs8
-rw-r--r--Xamarin.Forms.Core/Interactivity/MultiCondition.cs66
-rw-r--r--Xamarin.Forms.Core/Interactivity/MultiTrigger.cs23
-rw-r--r--Xamarin.Forms.Core/Interactivity/PropertyCondition.cs100
-rw-r--r--Xamarin.Forms.Core/Interactivity/Trigger.cs60
-rw-r--r--Xamarin.Forms.Core/Interactivity/TriggerAction.cs37
-rw-r--r--Xamarin.Forms.Core/Interactivity/TriggerBase.cs212
-rw-r--r--Xamarin.Forms.Core/Internals/DynamicResource.cs12
-rw-r--r--Xamarin.Forms.Core/Internals/IDataTemplate.cs10
-rw-r--r--Xamarin.Forms.Core/Internals/IDynamicResourceHandler.cs7
-rw-r--r--Xamarin.Forms.Core/Internals/INameScope.cs12
-rw-r--r--Xamarin.Forms.Core/Internals/NameScope.cs56
-rw-r--r--Xamarin.Forms.Core/InvalidNavigationException.cs11
-rw-r--r--Xamarin.Forms.Core/InvalidationEventArgs.cs14
-rw-r--r--Xamarin.Forms.Core/InvalidationTrigger.cs16
-rw-r--r--Xamarin.Forms.Core/ItemTappedEventArgs.cs17
-rw-r--r--Xamarin.Forms.Core/ItemVisibilityEventArgs.cs14
-rw-r--r--Xamarin.Forms.Core/ItemsView.cs92
-rw-r--r--Xamarin.Forms.Core/ItemsViewSimple.cs335
-rw-r--r--Xamarin.Forms.Core/Keyboard.cs64
-rw-r--r--Xamarin.Forms.Core/KeyboardFlags.cs13
-rw-r--r--Xamarin.Forms.Core/KeyboardTypeConverter.cs29
-rw-r--r--Xamarin.Forms.Core/Label.cs270
-rw-r--r--Xamarin.Forms.Core/Layout.cs433
-rw-r--r--Xamarin.Forms.Core/LayoutAlignment.cs13
-rw-r--r--Xamarin.Forms.Core/LayoutAlignmentExtensions.cs21
-rw-r--r--Xamarin.Forms.Core/LayoutConstraint.cs13
-rw-r--r--Xamarin.Forms.Core/LayoutExpandFlag.cs10
-rw-r--r--Xamarin.Forms.Core/LayoutOptions.cs39
-rw-r--r--Xamarin.Forms.Core/LayoutOptionsConverter.cs25
-rw-r--r--Xamarin.Forms.Core/LineBreakMode.cs12
-rw-r--r--Xamarin.Forms.Core/ListProxy.cs488
-rw-r--r--Xamarin.Forms.Core/ListView.cs540
-rw-r--r--Xamarin.Forms.Core/ListViewCachingStrategy.cs8
-rw-r--r--Xamarin.Forms.Core/LockingSemaphore.cs51
-rw-r--r--Xamarin.Forms.Core/Log.cs25
-rw-r--r--Xamarin.Forms.Core/LogListener.cs7
-rw-r--r--Xamarin.Forms.Core/MasterBehavior.cs11
-rw-r--r--Xamarin.Forms.Core/MasterDetailPage.cs229
-rw-r--r--Xamarin.Forms.Core/MeasureFlags.cs11
-rw-r--r--Xamarin.Forms.Core/MenuItem.cs117
-rw-r--r--Xamarin.Forms.Core/MergedStyle.cs162
-rw-r--r--Xamarin.Forms.Core/MessagingCenter.cs131
-rw-r--r--Xamarin.Forms.Core/ModalEventArgs.cs14
-rw-r--r--Xamarin.Forms.Core/ModalPoppedEventArgs.cs9
-rw-r--r--Xamarin.Forms.Core/ModalPoppingEventArgs.cs11
-rw-r--r--Xamarin.Forms.Core/ModalPushedEventArgs.cs9
-rw-r--r--Xamarin.Forms.Core/ModalPushingEventArgs.cs9
-rw-r--r--Xamarin.Forms.Core/MultiPage.cs359
-rw-r--r--Xamarin.Forms.Core/NameScopeExtensions.cs17
-rw-r--r--Xamarin.Forms.Core/NamedSize.cs11
-rw-r--r--Xamarin.Forms.Core/NavigationEventArgs.cs17
-rw-r--r--Xamarin.Forms.Core/NavigationMenu.cs71
-rw-r--r--Xamarin.Forms.Core/NavigationModel.cs177
-rw-r--r--Xamarin.Forms.Core/NavigationPage.cs411
-rw-r--r--Xamarin.Forms.Core/NavigationProxy.cs233
-rw-r--r--Xamarin.Forms.Core/NavigationRequestedEventArgs.cs26
-rw-r--r--Xamarin.Forms.Core/NotifyCollectionChangedEventArgsEx.cs65
-rw-r--r--Xamarin.Forms.Core/NotifyCollectionChangedEventArgsExtensions.cs105
-rw-r--r--Xamarin.Forms.Core/NullEffect.cs13
-rw-r--r--Xamarin.Forms.Core/NumericExtensions.cs17
-rw-r--r--Xamarin.Forms.Core/NumericKeyboard.cs6
-rw-r--r--Xamarin.Forms.Core/ObservableList.cs112
-rw-r--r--Xamarin.Forms.Core/ObservableWrapper.cs256
-rw-r--r--Xamarin.Forms.Core/OnIdiom.cs21
-rw-r--r--Xamarin.Forms.Core/OnPlatform.cs27
-rw-r--r--Xamarin.Forms.Core/OpenGLView.cs38
-rw-r--r--Xamarin.Forms.Core/OrderedDictionary.cs451
-rw-r--r--Xamarin.Forms.Core/Page.cs403
-rw-r--r--Xamarin.Forms.Core/PanGestureRecognizer.cs37
-rw-r--r--Xamarin.Forms.Core/PanUpdatedEventArgs.cs27
-rw-r--r--Xamarin.Forms.Core/ParameterAttribute.cs15
-rw-r--r--Xamarin.Forms.Core/Performance.cs93
-rw-r--r--Xamarin.Forms.Core/Picker.cs54
-rw-r--r--Xamarin.Forms.Core/PinchGestureRecognizer.cs51
-rw-r--r--Xamarin.Forms.Core/PinchGestureUpdatedEventArgs.cs24
-rw-r--r--Xamarin.Forms.Core/PlatformEffect.cs28
-rw-r--r--Xamarin.Forms.Core/Point.cs95
-rw-r--r--Xamarin.Forms.Core/PointTypeConverter.cs21
-rw-r--r--Xamarin.Forms.Core/PreserveAttribute.cs23
-rw-r--r--Xamarin.Forms.Core/ProgressBar.cs26
-rw-r--r--Xamarin.Forms.Core/Properties/AssemblyInfo.cs56
-rw-r--r--Xamarin.Forms.Core/Properties/GlobalAssemblyInfo.cs8
-rw-r--r--Xamarin.Forms.Core/PropertyChangingEventArgs.cs14
-rw-r--r--Xamarin.Forms.Core/PropertyChangingEventHandler.cs4
-rw-r--r--Xamarin.Forms.Core/ReadOnlyCastingList.cs35
-rw-r--r--Xamarin.Forms.Core/ReadOnlyListAdapter.cs100
-rw-r--r--Xamarin.Forms.Core/Rectangle.cs244
-rw-r--r--Xamarin.Forms.Core/RectangleTypeConverter.cs22
-rw-r--r--Xamarin.Forms.Core/ReflectionExtensions.cs68
-rw-r--r--Xamarin.Forms.Core/Registrar.cs147
-rw-r--r--Xamarin.Forms.Core/RelativeLayout.cs317
-rw-r--r--Xamarin.Forms.Core/RenderWithAttribute.cs15
-rw-r--r--Xamarin.Forms.Core/ResolutionGroupNameAttribute.cs15
-rw-r--r--Xamarin.Forms.Core/ResourceDictionary.cs135
-rw-r--r--Xamarin.Forms.Core/ResourcesChangedEventArgs.cs15
-rw-r--r--Xamarin.Forms.Core/ResourcesExtensions.cs62
-rw-r--r--Xamarin.Forms.Core/RoutingEffect.cs42
-rw-r--r--Xamarin.Forms.Core/RowDefinition.cs34
-rw-r--r--Xamarin.Forms.Core/RowDefinitionCollection.cs6
-rw-r--r--Xamarin.Forms.Core/ScrollOrientation.cs9
-rw-r--r--Xamarin.Forms.Core/ScrollToMode.cs10
-rw-r--r--Xamarin.Forms.Core/ScrollToPosition.cs10
-rw-r--r--Xamarin.Forms.Core/ScrollToRequestedEventArgs.cs56
-rw-r--r--Xamarin.Forms.Core/ScrollView.cs287
-rw-r--r--Xamarin.Forms.Core/ScrolledEventArgs.cs17
-rw-r--r--Xamarin.Forms.Core/SearchBar.cs156
-rw-r--r--Xamarin.Forms.Core/SelectedItemChangedEventArgs.cs14
-rw-r--r--Xamarin.Forms.Core/SelectedPositionChangedEventArgs.cs14
-rw-r--r--Xamarin.Forms.Core/SeparatorMenuItem.cs6
-rw-r--r--Xamarin.Forms.Core/SeparatorVisibility.cs8
-rw-r--r--Xamarin.Forms.Core/Setter.cs88
-rw-r--r--Xamarin.Forms.Core/SettersExtensions.cs29
-rw-r--r--Xamarin.Forms.Core/Size.cs110
-rw-r--r--Xamarin.Forms.Core/SizeRequest.cs29
-rw-r--r--Xamarin.Forms.Core/Slider.cs85
-rw-r--r--Xamarin.Forms.Core/Span.cs169
-rw-r--r--Xamarin.Forms.Core/SplitOrderedList.cs497
-rw-r--r--Xamarin.Forms.Core/StackLayout.cs460
-rw-r--r--Xamarin.Forms.Core/StackOrientation.cs8
-rw-r--r--Xamarin.Forms.Core/Stepper.cs94
-rw-r--r--Xamarin.Forms.Core/StreamImageSource.cs47
-rw-r--r--Xamarin.Forms.Core/StreamWrapper.cs81
-rw-r--r--Xamarin.Forms.Core/Style.cs184
-rw-r--r--Xamarin.Forms.Core/Switch.cs24
-rw-r--r--Xamarin.Forms.Core/SynchronizedList.cs130
-rw-r--r--Xamarin.Forms.Core/TabbedPage.cs17
-rw-r--r--Xamarin.Forms.Core/TableIntent.cs10
-rw-r--r--Xamarin.Forms.Core/TableModel.cs76
-rw-r--r--Xamarin.Forms.Core/TableRoot.cs60
-rw-r--r--Xamarin.Forms.Core/TableSection.cs137
-rw-r--r--Xamarin.Forms.Core/TableSectionBase.cs33
-rw-r--r--Xamarin.Forms.Core/TableView.cs228
-rw-r--r--Xamarin.Forms.Core/TapGestureRecognizer.cs95
-rw-r--r--Xamarin.Forms.Core/TappedEventArgs.cs14
-rw-r--r--Xamarin.Forms.Core/TargetIdiom.cs10
-rw-r--r--Xamarin.Forms.Core/TargetPlatform.cs11
-rw-r--r--Xamarin.Forms.Core/TelephoneKeyboard.cs6
-rw-r--r--Xamarin.Forms.Core/TemplateBinding.cs132
-rw-r--r--Xamarin.Forms.Core/TemplateExtensions.cs15
-rw-r--r--Xamarin.Forms.Core/TemplateUtilities.cs123
-rw-r--r--Xamarin.Forms.Core/TemplatedItemsList.cs1325
-rw-r--r--Xamarin.Forms.Core/TemplatedPage.cs38
-rw-r--r--Xamarin.Forms.Core/TemplatedView.cs68
-rw-r--r--Xamarin.Forms.Core/TextAlignment.cs9
-rw-r--r--Xamarin.Forms.Core/TextChangedEventArgs.cs17
-rw-r--r--Xamarin.Forms.Core/TextKeyboard.cs6
-rw-r--r--Xamarin.Forms.Core/Thickness.cs92
-rw-r--r--Xamarin.Forms.Core/ThicknessTypeConverter.cs35
-rw-r--r--Xamarin.Forms.Core/Ticker.cs117
-rw-r--r--Xamarin.Forms.Core/TimePicker.cs29
-rw-r--r--Xamarin.Forms.Core/ToggledEventArgs.cs14
-rw-r--r--Xamarin.Forms.Core/Toolbar.cs46
-rw-r--r--Xamarin.Forms.Core/ToolbarItem.cs57
-rw-r--r--Xamarin.Forms.Core/ToolbarItemEventArgs.cs14
-rw-r--r--Xamarin.Forms.Core/ToolbarItemOrder.cs9
-rw-r--r--Xamarin.Forms.Core/ToolbarTracker.cs184
-rw-r--r--Xamarin.Forms.Core/TrackableCollection.cs16
-rw-r--r--Xamarin.Forms.Core/Tweener.cs124
-rw-r--r--Xamarin.Forms.Core/TypeConverter.cs33
-rw-r--r--Xamarin.Forms.Core/TypeConverterAttribute.cs74
-rw-r--r--Xamarin.Forms.Core/TypeTypeConverter.cs31
-rw-r--r--Xamarin.Forms.Core/UnsolvableConstraintsException.cs11
-rw-r--r--Xamarin.Forms.Core/UriImageSource.cs223
-rw-r--r--Xamarin.Forms.Core/UriTypeConverter.cs24
-rw-r--r--Xamarin.Forms.Core/UrlKeyboard.cs6
-rw-r--r--Xamarin.Forms.Core/UrlWebViewSource.cs19
-rw-r--r--Xamarin.Forms.Core/ValueChangedEventArgs.cs17
-rw-r--r--Xamarin.Forms.Core/Vec2.cs14
-rw-r--r--Xamarin.Forms.Core/View.cs114
-rw-r--r--Xamarin.Forms.Core/ViewExtensions.cs197
-rw-r--r--Xamarin.Forms.Core/ViewState.cs12
-rw-r--r--Xamarin.Forms.Core/VisualElement.cs764
-rw-r--r--Xamarin.Forms.Core/WeakReferenceExtensions.cs16
-rw-r--r--Xamarin.Forms.Core/WebNavigatedEventArgs.cs12
-rw-r--r--Xamarin.Forms.Core/WebNavigatingEventArgs.cs11
-rw-r--r--Xamarin.Forms.Core/WebNavigationEvent.cs10
-rw-r--r--Xamarin.Forms.Core/WebNavigationEventArgs.cs20
-rw-r--r--Xamarin.Forms.Core/WebNavigationResult.cs10
-rw-r--r--Xamarin.Forms.Core/WebView.cs126
-rw-r--r--Xamarin.Forms.Core/WebViewSource.cs28
-rw-r--r--Xamarin.Forms.Core/WebViewSourceTypeConverter.cs15
-rw-r--r--Xamarin.Forms.Core/Xamarin.Forms.Core.csproj408
-rw-r--r--Xamarin.Forms.Core/Xamarin.Forms.Core.nuspec14
-rw-r--r--Xamarin.Forms.Core/XamlParseException.cs30
-rw-r--r--Xamarin.Forms.Core/XmlLineInfo.cs29
349 files changed, 27299 insertions, 0 deletions
diff --git a/Xamarin.Forms.Core/AbsoluteLayout.cs b/Xamarin.Forms.Core/AbsoluteLayout.cs
new file mode 100644
index 00000000..51b5ca12
--- /dev/null
+++ b/Xamarin.Forms.Core/AbsoluteLayout.cs
@@ -0,0 +1,313 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.ComponentModel;
+
+namespace Xamarin.Forms
+{
+ public class AbsoluteLayout : Layout<View>
+ {
+ public static readonly BindableProperty LayoutFlagsProperty = BindableProperty.CreateAttached("LayoutFlags", typeof(AbsoluteLayoutFlags), typeof(AbsoluteLayout), AbsoluteLayoutFlags.None);
+
+ public static readonly BindableProperty LayoutBoundsProperty = BindableProperty.CreateAttached("LayoutBounds", typeof(Rectangle), typeof(AbsoluteLayout), new Rectangle(0, 0, AutoSize, AutoSize));
+
+ readonly AbsoluteElementCollection _children;
+
+ public AbsoluteLayout()
+ {
+ _children = new AbsoluteElementCollection(InternalChildren, this);
+ }
+
+ public static double AutoSize
+ {
+ get { return -1; }
+ }
+
+ public new IAbsoluteList<View> Children
+ {
+ get { return _children; }
+ }
+
+ [TypeConverter(typeof(BoundsTypeConverter))]
+ public static Rectangle GetLayoutBounds(BindableObject bindable)
+ {
+ return (Rectangle)bindable.GetValue(LayoutBoundsProperty);
+ }
+
+ public static AbsoluteLayoutFlags GetLayoutFlags(BindableObject bindable)
+ {
+ return (AbsoluteLayoutFlags)bindable.GetValue(LayoutFlagsProperty);
+ }
+
+ public static void SetLayoutBounds(BindableObject bindable, Rectangle bounds)
+ {
+ bindable.SetValue(LayoutBoundsProperty, bounds);
+ }
+
+ public static void SetLayoutFlags(BindableObject bindable, AbsoluteLayoutFlags flags)
+ {
+ bindable.SetValue(LayoutFlagsProperty, flags);
+ }
+
+ protected override void LayoutChildren(double x, double y, double width, double height)
+ {
+ foreach (View child in LogicalChildren)
+ {
+ Rectangle rect = ComputeLayoutForRegion(child, new Size(width, height));
+ rect.X += x;
+ rect.Y += y;
+
+ LayoutChildIntoBoundingRegion(child, rect);
+ }
+ }
+
+ protected override void OnChildAdded(Element child)
+ {
+ base.OnChildAdded(child);
+ child.PropertyChanged += ChildOnPropertyChanged;
+ }
+
+ protected override void OnChildRemoved(Element child)
+ {
+ child.PropertyChanged -= ChildOnPropertyChanged;
+ base.OnChildRemoved(child);
+ }
+
+ [Obsolete("Use OnMeasure")]
+ protected override SizeRequest OnSizeRequest(double widthConstraint, double heightConstraint)
+ {
+ var bestFitSize = new Size();
+ var minimum = new Size();
+ foreach (View child in LogicalChildren)
+ {
+ SizeRequest desiredSize = ComputeBoundingRegionDesiredSize(child);
+
+ bestFitSize.Width = Math.Max(bestFitSize.Width, desiredSize.Request.Width);
+ bestFitSize.Height = Math.Max(bestFitSize.Height, desiredSize.Request.Height);
+ minimum.Width = Math.Max(minimum.Width, desiredSize.Minimum.Width);
+ minimum.Height = Math.Max(minimum.Height, desiredSize.Minimum.Height);
+ }
+
+ return new SizeRequest(bestFitSize, minimum);
+ }
+
+ internal override void ComputeConstraintForView(View view)
+ {
+ AbsoluteLayoutFlags layoutFlags = GetLayoutFlags(view);
+
+ if ((layoutFlags & AbsoluteLayoutFlags.SizeProportional) == AbsoluteLayoutFlags.SizeProportional)
+ {
+ view.ComputedConstraint = Constraint;
+ return;
+ }
+
+ var result = LayoutConstraint.None;
+ Rectangle layoutBounds = GetLayoutBounds(view);
+ if ((layoutFlags & AbsoluteLayoutFlags.HeightProportional) != 0)
+ {
+ bool widthLocked = layoutBounds.Width != AutoSize;
+ result = Constraint & LayoutConstraint.VerticallyFixed;
+ if (widthLocked)
+ result |= LayoutConstraint.HorizontallyFixed;
+ }
+ else if ((layoutFlags & AbsoluteLayoutFlags.WidthProportional) != 0)
+ {
+ bool heightLocked = layoutBounds.Height != AutoSize;
+ result = Constraint & LayoutConstraint.HorizontallyFixed;
+ if (heightLocked)
+ result |= LayoutConstraint.VerticallyFixed;
+ }
+ else
+ {
+ if (layoutBounds.Width != AutoSize)
+ result |= LayoutConstraint.HorizontallyFixed;
+ if (layoutBounds.Height != AutoSize)
+ result |= LayoutConstraint.VerticallyFixed;
+ }
+
+ view.ComputedConstraint = result;
+ }
+
+ void ChildOnPropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ if (e.PropertyName == LayoutFlagsProperty.PropertyName || e.PropertyName == LayoutBoundsProperty.PropertyName)
+ {
+ InvalidateMeasure(InvalidationTrigger.MeasureChanged);
+ UpdateChildrenLayout();
+ }
+ }
+
+ static SizeRequest ComputeBoundingRegionDesiredSize(View view)
+ {
+ var width = 0.0;
+ var height = 0.0;
+
+ var sizeRequest = new Lazy<SizeRequest>(() => view.Measure(double.PositiveInfinity, double.PositiveInfinity, MeasureFlags.IncludeMargins));
+
+ Rectangle bounds = GetLayoutBounds(view);
+ AbsoluteLayoutFlags absFlags = GetLayoutFlags(view);
+ bool widthIsProportional = (absFlags & AbsoluteLayoutFlags.WidthProportional) != 0;
+ bool heightIsProportional = (absFlags & AbsoluteLayoutFlags.HeightProportional) != 0;
+ bool xIsProportional = (absFlags & AbsoluteLayoutFlags.XProportional) != 0;
+ bool yIsProportional = (absFlags & AbsoluteLayoutFlags.YProportional) != 0;
+
+ // add in required x values
+ if (!xIsProportional)
+ {
+ width += bounds.X;
+ }
+
+ if (!yIsProportional)
+ {
+ height += bounds.Y;
+ }
+
+ double minWidth = width;
+ double minHeight = height;
+
+ if (!widthIsProportional && bounds.Width != AutoSize)
+ {
+ // fixed size
+ width += bounds.Width;
+ minWidth += bounds.Width;
+ }
+ else if (!widthIsProportional)
+ {
+ // auto size
+ width += sizeRequest.Value.Request.Width;
+ minWidth += sizeRequest.Value.Minimum.Width;
+ }
+ else
+ {
+ // proportional size
+ width += sizeRequest.Value.Request.Width / Math.Max(0.25, bounds.Width);
+ //minWidth += 0;
+ }
+
+ if (!heightIsProportional && bounds.Height != AutoSize)
+ {
+ // fixed size
+ height += bounds.Height;
+ minHeight += bounds.Height;
+ }
+ else if (!heightIsProportional)
+ {
+ // auto size
+ height += sizeRequest.Value.Request.Height;
+ minHeight += sizeRequest.Value.Minimum.Height;
+ }
+ else
+ {
+ // proportional size
+ height += sizeRequest.Value.Request.Height / Math.Max(0.25, bounds.Height);
+ //minHeight += 0;
+ }
+
+ return new SizeRequest(new Size(width, height), new Size(minWidth, minHeight));
+ }
+
+ static Rectangle ComputeLayoutForRegion(View view, Size region)
+ {
+ var result = new Rectangle();
+
+ SizeRequest sizeRequest;
+ Rectangle bounds = GetLayoutBounds(view);
+ AbsoluteLayoutFlags absFlags = GetLayoutFlags(view);
+ bool widthIsProportional = (absFlags & AbsoluteLayoutFlags.WidthProportional) != 0;
+ bool heightIsProportional = (absFlags & AbsoluteLayoutFlags.HeightProportional) != 0;
+ bool xIsProportional = (absFlags & AbsoluteLayoutFlags.XProportional) != 0;
+ bool yIsProportional = (absFlags & AbsoluteLayoutFlags.YProportional) != 0;
+
+ if (widthIsProportional)
+ {
+ result.Width = Math.Round(region.Width * bounds.Width);
+ }
+ else if (bounds.Width != AutoSize)
+ {
+ result.Width = bounds.Width;
+ }
+
+ if (heightIsProportional)
+ {
+ result.Height = Math.Round(region.Height * bounds.Height);
+ }
+ else if (bounds.Height != AutoSize)
+ {
+ result.Height = bounds.Height;
+ }
+
+ if (!widthIsProportional && bounds.Width == AutoSize)
+ {
+ if (!heightIsProportional && bounds.Width == AutoSize)
+ {
+ // Width and Height are auto
+ sizeRequest = view.Measure(region.Width, region.Height, MeasureFlags.IncludeMargins);
+ result.Width = sizeRequest.Request.Width;
+ result.Height = sizeRequest.Request.Height;
+ }
+ else
+ {
+ // Only width is auto
+ sizeRequest = view.Measure(region.Width, result.Height, MeasureFlags.IncludeMargins);
+ result.Width = sizeRequest.Request.Width;
+ }
+ }
+ else if (!heightIsProportional && bounds.Height == AutoSize)
+ {
+ // Only height is auto
+ sizeRequest = view.Measure(result.Width, region.Height, MeasureFlags.IncludeMargins);
+ result.Height = sizeRequest.Request.Height;
+ }
+
+ if (xIsProportional)
+ {
+ result.X = Math.Round((region.Width - result.Width) * bounds.X);
+ }
+ else
+ {
+ result.X = bounds.X;
+ }
+
+ if (yIsProportional)
+ {
+ result.Y = Math.Round((region.Height - result.Height) * bounds.Y);
+ }
+ else
+ {
+ result.Y = bounds.Y;
+ }
+
+ return result;
+ }
+
+ public interface IAbsoluteList<T> : IList<T> where T : View
+ {
+ void Add(View view, Rectangle bounds, AbsoluteLayoutFlags flags = AbsoluteLayoutFlags.None);
+
+ void Add(View view, Point position);
+ }
+
+ class AbsoluteElementCollection : ElementCollection<View>, IAbsoluteList<View>
+ {
+ public AbsoluteElementCollection(ObservableCollection<Element> inner, AbsoluteLayout parent) : base(inner)
+ {
+ Parent = parent;
+ }
+
+ internal AbsoluteLayout Parent { get; set; }
+
+ public void Add(View view, Rectangle bounds, AbsoluteLayoutFlags flags = AbsoluteLayoutFlags.None)
+ {
+ SetLayoutBounds(view, bounds);
+ SetLayoutFlags(view, flags);
+ Add(view);
+ }
+
+ public void Add(View view, Point position)
+ {
+ SetLayoutBounds(view, new Rectangle(position.X, position.Y, AutoSize, AutoSize));
+ Add(view);
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/AbsoluteLayoutFlags.cs b/Xamarin.Forms.Core/AbsoluteLayoutFlags.cs
new file mode 100644
index 00000000..dcc81c10
--- /dev/null
+++ b/Xamarin.Forms.Core/AbsoluteLayoutFlags.cs
@@ -0,0 +1,17 @@
+using System;
+
+namespace Xamarin.Forms
+{
+ [Flags]
+ public enum AbsoluteLayoutFlags
+ {
+ None = 0,
+ XProportional = 1 << 0,
+ YProportional = 1 << 1,
+ WidthProportional = 1 << 2,
+ HeightProportional = 1 << 3,
+ PositionProportional = 1 | 1 << 1,
+ SizeProportional = 1 << 2 | 1 << 3,
+ All = ~0
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/ActionSheetArguments.cs b/Xamarin.Forms.Core/ActionSheetArguments.cs
new file mode 100644
index 00000000..3417ed61
--- /dev/null
+++ b/Xamarin.Forms.Core/ActionSheetArguments.cs
@@ -0,0 +1,45 @@
+using System.Collections.Generic;
+using System.Threading.Tasks;
+
+namespace Xamarin.Forms
+{
+ internal class ActionSheetArguments
+ {
+ public ActionSheetArguments(string title, string cancel, string destruction, IEnumerable<string> buttons)
+ {
+ Title = title;
+ Cancel = cancel;
+ Destruction = destruction;
+ Buttons = buttons;
+ Result = new TaskCompletionSource<string>();
+ }
+
+ /// <summary>
+ /// Gets titles of any buttons on the action sheet that aren't <see cref="Cancel" /> or <see cref="Destruction" />. Can
+ /// be <c>null</c>.
+ /// </summary>
+ public IEnumerable<string> Buttons { get; private set; }
+
+ /// <summary>
+ /// Gets the text for a cancel button. Can be null.
+ /// </summary>
+ public string Cancel { get; private set; }
+
+ /// <summary>
+ /// Gets the text for a destructive button. Can be null.
+ /// </summary>
+ public string Destruction { get; private set; }
+
+ public TaskCompletionSource<string> Result { get; }
+
+ /// <summary>
+ /// Gets the title for the action sheet. Can be null.
+ /// </summary>
+ public string Title { get; private set; }
+
+ public void SetResult(string result)
+ {
+ Result.TrySetResult(result);
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/ActivityIndicator.cs b/Xamarin.Forms.Core/ActivityIndicator.cs
new file mode 100644
index 00000000..3689609a
--- /dev/null
+++ b/Xamarin.Forms.Core/ActivityIndicator.cs
@@ -0,0 +1,24 @@
+using Xamarin.Forms.Platform;
+
+namespace Xamarin.Forms
+{
+ [RenderWith(typeof(_ActivityIndicatorRenderer))]
+ public class ActivityIndicator : View
+ {
+ public static readonly BindableProperty IsRunningProperty = BindableProperty.Create("IsRunning", typeof(bool), typeof(ActivityIndicator), default(bool));
+
+ public static readonly BindableProperty ColorProperty = BindableProperty.Create("Color", typeof(Color), typeof(ActivityIndicator), Color.Default);
+
+ public Color Color
+ {
+ get { return (Color)GetValue(ColorProperty); }
+ set { SetValue(ColorProperty, value); }
+ }
+
+ public bool IsRunning
+ {
+ get { return (bool)GetValue(IsRunningProperty); }
+ set { SetValue(IsRunningProperty, value); }
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/AlertArguments.cs b/Xamarin.Forms.Core/AlertArguments.cs
new file mode 100644
index 00000000..87224cb4
--- /dev/null
+++ b/Xamarin.Forms.Core/AlertArguments.cs
@@ -0,0 +1,43 @@
+using System.Threading.Tasks;
+
+namespace Xamarin.Forms
+{
+ internal class AlertArguments
+ {
+ public AlertArguments(string title, string message, string accept, string cancel)
+ {
+ Title = title;
+ Message = message;
+ Accept = accept;
+ Cancel = cancel;
+ Result = new TaskCompletionSource<bool>();
+ }
+
+ /// <summary>
+ /// Gets the text for the accept button. Can be null.
+ /// </summary>
+ public string Accept { get; private set; }
+
+ /// <summary>
+ /// Gets the text of the cancel button.
+ /// </summary>
+ public string Cancel { get; private set; }
+
+ /// <summary>
+ /// Gets the message for the alert. Can be null.
+ /// </summary>
+ public string Message { get; private set; }
+
+ public TaskCompletionSource<bool> Result { get; }
+
+ /// <summary>
+ /// Gets the title for the alert. Can be null.
+ /// </summary>
+ public string Title { get; private set; }
+
+ public void SetResult(bool result)
+ {
+ Result.TrySetResult(result);
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/AnimatableKey.cs b/Xamarin.Forms.Core/AnimatableKey.cs
new file mode 100644
index 00000000..2a73ef6d
--- /dev/null
+++ b/Xamarin.Forms.Core/AnimatableKey.cs
@@ -0,0 +1,82 @@
+using System;
+
+namespace Xamarin.Forms
+{
+ internal class AnimatableKey
+ {
+ public AnimatableKey(IAnimatable animatable, string handle)
+ {
+ if (animatable == null)
+ {
+ throw new ArgumentNullException(nameof(animatable));
+ }
+
+ if (string.IsNullOrEmpty(handle))
+ {
+ throw new ArgumentException("Argument is null or empty", nameof(handle));
+ }
+
+ Animatable = new WeakReference<IAnimatable>(animatable);
+ Handle = handle;
+ }
+
+ public WeakReference<IAnimatable> Animatable { get; }
+
+ public string Handle { get; }
+
+ public override bool Equals(object obj)
+ {
+ if (ReferenceEquals(null, obj))
+ {
+ return false;
+ }
+ if (ReferenceEquals(this, obj))
+ {
+ return true;
+ }
+ if (obj.GetType() != GetType())
+ {
+ return false;
+ }
+ return Equals((AnimatableKey)obj);
+ }
+
+ public override int GetHashCode()
+ {
+ unchecked
+ {
+ IAnimatable target;
+ if (!Animatable.TryGetTarget(out target))
+ {
+ return Handle?.GetHashCode() ?? 0;
+ }
+
+ return ((target?.GetHashCode() ?? 0) * 397) ^ (Handle?.GetHashCode() ?? 0);
+ }
+ }
+
+ protected bool Equals(AnimatableKey other)
+ {
+ if (!string.Equals(Handle, other.Handle))
+ {
+ return false;
+ }
+
+ IAnimatable thisAnimatable;
+
+ if (!Animatable.TryGetTarget(out thisAnimatable))
+ {
+ return false;
+ }
+
+ IAnimatable thatAnimatable;
+
+ if (!other.Animatable.TryGetTarget(out thatAnimatable))
+ {
+ return false;
+ }
+
+ return Equals(thisAnimatable, thatAnimatable);
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/Animation.cs b/Xamarin.Forms.Core/Animation.cs
new file mode 100644
index 00000000..03cded1e
--- /dev/null
+++ b/Xamarin.Forms.Core/Animation.cs
@@ -0,0 +1,138 @@
+//
+// Tweener.cs
+//
+// Author:
+// Jason Smith <jason.smith@xamarin.com>
+//
+// Copyright (c) 2012 Xamarin Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+
+namespace Xamarin.Forms
+{
+ public class Animation : IEnumerable
+ {
+ readonly List<Animation> _children;
+ readonly Easing _easing;
+ readonly Action _finished;
+ readonly Action<double> _step;
+ double _beginAt;
+ double _finishAt;
+ bool _finishedTriggered;
+
+ public Animation()
+ {
+ _children = new List<Animation>();
+ _easing = Easing.Linear;
+ _step = f => { };
+ }
+
+ public Animation(Action<double> callback, double start = 0.0f, double end = 1.0f, Easing easing = null, Action finished = null)
+ {
+ _children = new List<Animation>();
+ _easing = easing ?? Easing.Linear;
+ _finished = finished;
+
+ Func<double, double> transform = AnimationExtensions.Interpolate(start, end);
+ _step = f => callback(transform(f));
+ }
+
+ public IEnumerator GetEnumerator()
+ {
+ return _children.GetEnumerator();
+ }
+
+ public void Add(double beginAt, double finishAt, Animation animation)
+ {
+ if (beginAt < 0 || beginAt > 1)
+ throw new ArgumentOutOfRangeException("beginAt");
+
+ if (finishAt < 0 || finishAt > 1)
+ throw new ArgumentOutOfRangeException("finishAt");
+
+ if (finishAt <= beginAt)
+ throw new ArgumentException("finishAt must be greater than beginAt");
+
+ animation._beginAt = beginAt;
+ animation._finishAt = finishAt;
+ _children.Add(animation);
+ }
+
+ public void Commit(IAnimatable owner, string name, uint rate = 16, uint length = 250, Easing easing = null, Action<double, bool> finished = null, Func<bool> repeat = null)
+ {
+ owner.Animate(name, this, rate, length, easing, finished, repeat);
+ }
+
+ public Action<double> GetCallback()
+ {
+ Action<double> result = f =>
+ {
+ _step(_easing.Ease(f));
+ foreach (Animation animation in _children)
+ {
+ if (animation._finishedTriggered)
+ continue;
+
+ double val = Math.Max(0.0f, Math.Min(1.0f, (f - animation._beginAt) / (animation._finishAt - animation._beginAt)));
+
+ if (val <= 0.0f) // not ready to process yet
+ continue;
+
+ Action<double> callback = animation.GetCallback();
+ callback(val);
+
+ if (val >= 1.0f)
+ {
+ animation._finishedTriggered = true;
+ if (animation._finished != null)
+ animation._finished();
+ }
+ }
+ };
+ return result;
+ }
+
+ public Animation Insert(double beginAt, double finishAt, Animation animation)
+ {
+ Add(beginAt, finishAt, animation);
+ return this;
+ }
+
+ public Animation WithConcurrent(Animation animation, double beginAt = 0.0f, double finishAt = 1.0f)
+ {
+ animation._beginAt = beginAt;
+ animation._finishAt = finishAt;
+ _children.Add(animation);
+ return this;
+ }
+
+ public Animation WithConcurrent(Action<double> callback, double start = 0.0f, double end = 1.0f, Easing easing = null, double beginAt = 0.0f, double finishAt = 1.0f)
+ {
+ var child = new Animation(callback, start, end, easing);
+ child._beginAt = beginAt;
+ child._finishAt = finishAt;
+ _children.Add(child);
+ return this;
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/AnimationExtensions.cs b/Xamarin.Forms.Core/AnimationExtensions.cs
new file mode 100644
index 00000000..efc3e405
--- /dev/null
+++ b/Xamarin.Forms.Core/AnimationExtensions.cs
@@ -0,0 +1,259 @@
+//
+// Tweener.cs
+//
+// Author:
+// Jason Smith <jason.smith@xamarin.com>
+//
+// Copyright (c) 2012 Xamarin Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+using System;
+using System.Collections.Generic;
+
+namespace Xamarin.Forms
+{
+ public static class AnimationExtensions
+ {
+ static readonly Dictionary<AnimatableKey, Info> s_animations;
+ static readonly Dictionary<AnimatableKey, int> s_kinetics;
+
+ static AnimationExtensions()
+ {
+ s_animations = new Dictionary<AnimatableKey, Info>();
+ s_kinetics = new Dictionary<AnimatableKey, int>();
+ }
+
+ public static bool AbortAnimation(this IAnimatable self, string handle)
+ {
+ CheckAccess();
+
+ var key = new AnimatableKey(self, handle);
+
+ return AbortAnimation(key) && AbortKinetic(key);
+ }
+
+ public static void Animate(this IAnimatable self, string name, Animation animation, uint rate = 16, uint length = 250, Easing easing = null, Action<double, bool> finished = null,
+ Func<bool> repeat = null)
+ {
+ self.Animate(name, animation.GetCallback(), rate, length, easing, finished, repeat);
+ }
+
+ public static void Animate(this IAnimatable self, string name, Action<double> callback, double start, double end, uint rate = 16, uint length = 250, Easing easing = null,
+ Action<double, bool> finished = null, Func<bool> repeat = null)
+ {
+ self.Animate(name, Interpolate(start, end), callback, rate, length, easing, finished, repeat);
+ }
+
+ public static void Animate(this IAnimatable self, string name, Action<double> callback, uint rate = 16, uint length = 250, Easing easing = null, Action<double, bool> finished = null,
+ Func<bool> repeat = null)
+ {
+ self.Animate(name, x => x, callback, rate, length, easing, finished, repeat);
+ }
+
+ public static void Animate<T>(this IAnimatable self, string name, Func<double, T> transform, Action<T> callback, uint rate = 16, uint length = 250, Easing easing = null,
+ Action<T, bool> finished = null, Func<bool> repeat = null)
+ {
+ if (transform == null)
+ throw new ArgumentNullException(nameof(transform));
+ if (callback == null)
+ throw new ArgumentNullException(nameof(callback));
+ if (self == null)
+ throw new ArgumentNullException(nameof(self));
+
+ CheckAccess();
+
+ var key = new AnimatableKey(self, name);
+
+ AbortAnimation(key);
+
+ Action<double> step = f => callback(transform(f));
+ Action<double, bool> final = null;
+ if (finished != null)
+ final = (f, b) => finished(transform(f), b);
+
+ var info = new Info { Rate = rate, Length = length, Easing = easing ?? Easing.Linear };
+
+ var tweener = new Tweener(info.Length);
+ tweener.Handle = key;
+ tweener.ValueUpdated += HandleTweenerUpdated;
+ tweener.Finished += HandleTweenerFinished;
+
+ info.Tweener = tweener;
+ info.Callback = step;
+ info.Finished = final;
+ info.Repeat = repeat;
+ info.Owner = new WeakReference<IAnimatable>(self);
+
+ s_animations[key] = info;
+
+ info.Callback(0.0f);
+ tweener.Start();
+ }
+
+ public static void AnimateKinetic(this IAnimatable self, string name, Func<double, double, bool> callback, double velocity, double drag, Action finished = null)
+ {
+ CheckAccess();
+
+ var key = new AnimatableKey(self, name);
+
+ AbortKinetic(key);
+
+ double sign = velocity / Math.Abs(velocity);
+ velocity = Math.Abs(velocity);
+
+ int tick = Ticker.Default.Insert(step =>
+ {
+ long ms = step;
+
+ velocity -= drag * ms;
+ velocity = Math.Max(0, velocity);
+
+ var result = false;
+ if (velocity > 0)
+ {
+ result = callback(sign * velocity * ms, velocity);
+ }
+
+ if (!result)
+ {
+ finished?.Invoke();
+ s_kinetics.Remove(key);
+ }
+ return result;
+ });
+
+ s_kinetics[key] = tick;
+ }
+
+ public static bool AnimationIsRunning(this IAnimatable self, string handle)
+ {
+ CheckAccess();
+
+ var key = new AnimatableKey(self, handle);
+
+ return s_animations.ContainsKey(key);
+ }
+
+ public static Func<double, double> Interpolate(double start, double end = 1.0f, double reverseVal = 0.0f, bool reverse = false)
+ {
+ double target = reverse ? reverseVal : end;
+ return x => start + (target - start) * x;
+ }
+
+ static bool AbortAnimation(AnimatableKey key)
+ {
+ if (!s_animations.ContainsKey(key))
+ {
+ return false;
+ }
+
+ Info info = s_animations[key];
+ info.Tweener.ValueUpdated -= HandleTweenerUpdated;
+ info.Tweener.Finished -= HandleTweenerFinished;
+ info.Tweener.Stop();
+ info.Finished?.Invoke(1.0f, true);
+
+ return s_animations.Remove(key);
+ }
+
+ static bool AbortKinetic(AnimatableKey key)
+ {
+ if (!s_kinetics.ContainsKey(key))
+ {
+ return false;
+ }
+
+ Ticker.Default.Remove(s_kinetics[key]);
+ return s_kinetics.Remove(key);
+ }
+
+ static void CheckAccess()
+ {
+ if (Device.IsInvokeRequired)
+ {
+ throw new InvalidOperationException("Animation operations must be invoked on the UI thread");
+ }
+ }
+
+ static void HandleTweenerFinished(object o, EventArgs args)
+ {
+ var tweener = o as Tweener;
+ Info info;
+ if (tweener != null && s_animations.TryGetValue(tweener.Handle, out info))
+ {
+ var repeat = false;
+ if (info.Repeat != null)
+ repeat = info.Repeat();
+
+ IAnimatable owner;
+ if (info.Owner.TryGetTarget(out owner))
+ owner.BatchBegin();
+ info.Callback(tweener.Value);
+
+ if (!repeat)
+ {
+ s_animations.Remove(tweener.Handle);
+ tweener.ValueUpdated -= HandleTweenerUpdated;
+ tweener.Finished -= HandleTweenerFinished;
+ }
+
+ info.Finished?.Invoke(tweener.Value, false);
+
+ if (info.Owner.TryGetTarget(out owner))
+ owner.BatchCommit();
+
+ if (repeat)
+ {
+ tweener.Start();
+ }
+ }
+ }
+
+ static void HandleTweenerUpdated(object o, EventArgs args)
+ {
+ var tweener = o as Tweener;
+ Info info;
+ IAnimatable owner;
+
+ if (tweener != null && s_animations.TryGetValue(tweener.Handle, out info) && info.Owner.TryGetTarget(out owner))
+ {
+ owner.BatchBegin();
+ info.Callback(info.Easing.Ease(tweener.Value));
+ owner.BatchCommit();
+ }
+ }
+
+ class Info
+ {
+ public Action<double> Callback;
+ public Action<double, bool> Finished;
+ public Func<bool> Repeat;
+ public Tweener Tweener;
+
+ public Easing Easing { get; set; }
+
+ public uint Length { get; set; }
+
+ public WeakReference<IAnimatable> Owner { get; set; }
+
+ public uint Rate { get; set; }
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/Application.cs b/Xamarin.Forms.Core/Application.cs
new file mode 100644
index 00000000..1dfe258b
--- /dev/null
+++ b/Xamarin.Forms.Core/Application.cs
@@ -0,0 +1,308 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Threading.Tasks;
+using Xamarin.Forms.Platform;
+
+namespace Xamarin.Forms
+{
+ public class Application : Element, IResourcesProvider, IApplicationController
+ {
+ static Application s_current;
+ readonly Task<IDictionary<string, object>> _propertiesTask;
+
+ bool _isSaving;
+
+ ReadOnlyCollection<Element> _logicalChildren;
+
+ Page _mainPage;
+
+ ResourceDictionary _resources;
+ bool _saveAgain;
+
+ protected Application()
+ {
+ var f = false;
+ if (f)
+ Loader.Load();
+ NavigationProxy = new NavigationImpl(this);
+ Current = this;
+ _propertiesTask = GetPropertiesAsync();
+
+ SystemResources = DependencyService.Get<ISystemResourcesProvider>().GetSystemResources();
+ SystemResources.ValuesChanged += OnParentResourcesChanged;
+ }
+
+ public static Application Current
+ {
+ get { return s_current; }
+ internal set
+ {
+ if (s_current == value)
+ return;
+ if (value == null)
+ s_current = null; //Allow to reset current for unittesting
+ s_current = value;
+ }
+ }
+
+ public Page MainPage
+ {
+ get { return _mainPage; }
+ set
+ {
+ if (value == null)
+ throw new ArgumentNullException("value");
+
+ if (_mainPage == value)
+ return;
+
+ OnPropertyChanging();
+ if (_mainPage != null)
+ {
+ InternalChildren.Remove(_mainPage);
+ _mainPage.Parent = null;
+ }
+
+ _mainPage = value;
+
+ if (_mainPage != null)
+ {
+ _mainPage.Parent = this;
+ _mainPage.NavigationProxy.Inner = NavigationProxy;
+ InternalChildren.Add(_mainPage);
+ }
+ OnPropertyChanged();
+ }
+ }
+
+ public IDictionary<string, object> Properties
+ {
+ get { return _propertiesTask.Result; }
+ }
+
+ internal override ReadOnlyCollection<Element> LogicalChildren
+ {
+ get { return _logicalChildren ?? (_logicalChildren = new ReadOnlyCollection<Element>(InternalChildren)); }
+ }
+
+ internal NavigationProxy NavigationProxy { get; }
+
+ internal int PanGestureId { get; set; }
+
+ internal IResourceDictionary SystemResources { get; }
+
+ ObservableCollection<Element> InternalChildren { get; } = new ObservableCollection<Element>();
+
+ public ResourceDictionary Resources
+ {
+ get { return _resources; }
+ set
+ {
+ if (_resources == value)
+ return;
+ OnPropertyChanging();
+ if (_resources != null)
+ ((IResourceDictionary)_resources).ValuesChanged -= OnResourcesChanged;
+ _resources = value;
+ OnResourcesChanged(value);
+ if (_resources != null)
+ ((IResourceDictionary)_resources).ValuesChanged += OnResourcesChanged;
+ OnPropertyChanged();
+ }
+ }
+
+ public event EventHandler<ModalPoppedEventArgs> ModalPopped;
+
+ public event EventHandler<ModalPoppingEventArgs> ModalPopping;
+
+ public event EventHandler<ModalPushedEventArgs> ModalPushed;
+
+ public event EventHandler<ModalPushingEventArgs> ModalPushing;
+
+ public async Task SavePropertiesAsync()
+ {
+ if (Device.IsInvokeRequired)
+ Device.BeginInvokeOnMainThread(async () => await SetPropertiesAsync());
+ else
+ await SetPropertiesAsync();
+ }
+
+ protected override void OnParentSet()
+ {
+ throw new InvalidOperationException("Setting a Parent on Application is invalid.");
+ }
+
+ protected virtual void OnResume()
+ {
+ }
+
+ protected virtual void OnSleep()
+ {
+ }
+
+ protected virtual void OnStart()
+ {
+ }
+
+ internal static void ClearCurrent()
+ {
+ s_current = null;
+ }
+
+ internal static bool IsApplicationOrNull(Element element)
+ {
+ return element == null || element is Application;
+ }
+
+ internal override void OnParentResourcesChanged(IEnumerable<KeyValuePair<string, object>> values)
+ {
+ if (Resources == null || Resources.Count == 0)
+ {
+ base.OnParentResourcesChanged(values);
+ return;
+ }
+
+ var innerKeys = new HashSet<string>();
+ var changedResources = new List<KeyValuePair<string, object>>();
+ foreach (KeyValuePair<string, object> c in Resources)
+ innerKeys.Add(c.Key);
+ foreach (KeyValuePair<string, object> value in values)
+ {
+ if (innerKeys.Add(value.Key))
+ changedResources.Add(value);
+ }
+ OnResourcesChanged(changedResources);
+ }
+
+ internal event EventHandler PopCanceled;
+
+ internal void SendResume()
+ {
+ s_current = this;
+ OnResume();
+ }
+
+ internal Task SendSleepAsync()
+ {
+ OnSleep();
+ return SavePropertiesAsync();
+ }
+
+ internal void SendStart()
+ {
+ OnStart();
+ }
+
+ async Task<IDictionary<string, object>> GetPropertiesAsync()
+ {
+ var deserializer = DependencyService.Get<IDeserializer>();
+ if (deserializer == null)
+ {
+ Log.Warning("Startup", "No IDeserialzier was found registered");
+ return new Dictionary<string, object>(4);
+ }
+
+ IDictionary<string, object> properties = await deserializer.DeserializePropertiesAsync().ConfigureAwait(false);
+ if (properties == null)
+ properties = new Dictionary<string, object>(4);
+
+ return properties;
+ }
+
+ void OnModalPopped(Page modalPage)
+ {
+ EventHandler<ModalPoppedEventArgs> handler = ModalPopped;
+ if (handler != null)
+ handler(this, new ModalPoppedEventArgs(modalPage));
+ }
+
+ bool OnModalPopping(Page modalPage)
+ {
+ EventHandler<ModalPoppingEventArgs> handler = ModalPopping;
+ var args = new ModalPoppingEventArgs(modalPage);
+ if (handler != null)
+ handler(this, args);
+ return args.Cancel;
+ }
+
+ void OnModalPushed(Page modalPage)
+ {
+ EventHandler<ModalPushedEventArgs> handler = ModalPushed;
+ if (handler != null)
+ handler(this, new ModalPushedEventArgs(modalPage));
+ }
+
+ void OnModalPushing(Page modalPage)
+ {
+ EventHandler<ModalPushingEventArgs> handler = ModalPushing;
+ if (handler != null)
+ handler(this, new ModalPushingEventArgs(modalPage));
+ }
+
+ void OnPopCanceled()
+ {
+ EventHandler handler = PopCanceled;
+ if (handler != null)
+ handler(this, EventArgs.Empty);
+ }
+
+ async Task SetPropertiesAsync()
+ {
+ if (_isSaving)
+ {
+ _saveAgain = true;
+ return;
+ }
+ _isSaving = true;
+ await DependencyService.Get<IDeserializer>().SerializePropertiesAsync(Properties);
+ if (_saveAgain)
+ await DependencyService.Get<IDeserializer>().SerializePropertiesAsync(Properties);
+ _isSaving = _saveAgain = false;
+ }
+
+ class NavigationImpl : NavigationProxy
+ {
+ readonly Application _owner;
+
+ public NavigationImpl(Application owner)
+ {
+ _owner = owner;
+ }
+
+ protected override async Task<Page> OnPopModal(bool animated)
+ {
+ Page modal = ModalStack[ModalStack.Count - 1];
+ if (_owner.OnModalPopping(modal))
+ {
+ _owner.OnPopCanceled();
+ return null;
+ }
+ Page result = await base.OnPopModal(animated);
+ result.Parent = null;
+ _owner.OnModalPopped(result);
+ return result;
+ }
+
+ protected override async Task OnPushModal(Page modal, bool animated)
+ {
+ _owner.OnModalPushing(modal);
+
+ modal.Parent = _owner;
+
+ if (modal.NavigationProxy.ModalStack.Count == 0)
+ {
+ modal.NavigationProxy.Inner = this;
+ await base.OnPushModal(modal, animated);
+ }
+ else
+ {
+ await base.OnPushModal(modal, animated);
+ modal.NavigationProxy.Inner = this;
+ }
+
+ _owner.OnModalPushed(modal);
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/Aspect.cs b/Xamarin.Forms.Core/Aspect.cs
new file mode 100644
index 00000000..6a0085ca
--- /dev/null
+++ b/Xamarin.Forms.Core/Aspect.cs
@@ -0,0 +1,9 @@
+namespace Xamarin.Forms
+{
+ public enum Aspect
+ {
+ AspectFit,
+ AspectFill,
+ Fill
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/BackButtonPressedEventArgs.cs b/Xamarin.Forms.Core/BackButtonPressedEventArgs.cs
new file mode 100644
index 00000000..2cc9a185
--- /dev/null
+++ b/Xamarin.Forms.Core/BackButtonPressedEventArgs.cs
@@ -0,0 +1,9 @@
+using System;
+
+namespace Xamarin.Forms
+{
+ public class BackButtonPressedEventArgs : EventArgs
+ {
+ public bool Handled { get; set; }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/BaseMenuItem.cs b/Xamarin.Forms.Core/BaseMenuItem.cs
new file mode 100644
index 00000000..58831aa5
--- /dev/null
+++ b/Xamarin.Forms.Core/BaseMenuItem.cs
@@ -0,0 +1,6 @@
+namespace Xamarin.Forms
+{
+ public abstract class BaseMenuItem : Element
+ {
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/BindableObject.cs b/Xamarin.Forms.Core/BindableObject.cs
new file mode 100644
index 00000000..683d390a
--- /dev/null
+++ b/Xamarin.Forms.Core/BindableObject.cs
@@ -0,0 +1,647 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Diagnostics;
+using System.Runtime.CompilerServices;
+using Xamarin.Forms.Internals;
+
+namespace Xamarin.Forms
+{
+ public abstract class BindableObject : INotifyPropertyChanged, IDynamicResourceHandler
+ {
+ public static readonly BindableProperty BindingContextProperty = BindableProperty.Create("BindingContext", typeof(object), typeof(BindableObject), default(object), BindingMode.OneWay, null,
+ BindingContextPropertyBindingPropertyChanged, null, null, BindingContextPropertyBindingChanging);
+
+ readonly List<BindablePropertyContext> _properties = new List<BindablePropertyContext>(4);
+
+ bool _applying;
+
+ object _inheritedContext;
+
+ public object BindingContext
+ {
+ get { return _inheritedContext ?? GetValue(BindingContextProperty); }
+ set { SetValue(BindingContextProperty, value); }
+ }
+
+ void IDynamicResourceHandler.SetDynamicResource(BindableProperty property, string key)
+ {
+ SetDynamicResource(property, key, false);
+ }
+
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ public event EventHandler BindingContextChanged;
+
+ public void ClearValue(BindableProperty property)
+ {
+ ClearValue(property, true);
+ }
+
+ public void ClearValue(BindablePropertyKey propertyKey)
+ {
+ if (propertyKey == null)
+ throw new ArgumentNullException("propertyKey");
+
+ ClearValue(propertyKey.BindableProperty, false);
+ }
+
+ public object GetValue(BindableProperty property)
+ {
+ if (property == null)
+ throw new ArgumentNullException("property");
+
+ BindablePropertyContext context = property.DefaultValueCreator != null ? GetOrCreateContext(property) : GetContext(property);
+
+ if (context == null)
+ return property.DefaultValue;
+
+ return context.Value;
+ }
+
+ public event PropertyChangingEventHandler PropertyChanging;
+
+ public void RemoveBinding(BindableProperty property)
+ {
+ if (property == null)
+ throw new ArgumentNullException("property");
+
+ BindablePropertyContext context = GetContext(property);
+ if (context == null || context.Binding == null)
+ return;
+
+ RemoveBinding(property, context);
+ }
+
+ public void SetBinding(BindableProperty targetProperty, BindingBase binding)
+ {
+ SetBinding(targetProperty, binding, false);
+ }
+
+ public void SetValue(BindableProperty property, object value)
+ {
+ SetValue(property, value, false, true);
+ }
+
+ public void SetValue(BindablePropertyKey propertyKey, object value)
+ {
+ if (propertyKey == null)
+ throw new ArgumentNullException("propertyKey");
+
+ SetValue(propertyKey.BindableProperty, value, false, false);
+ }
+
+ protected internal static void SetInheritedBindingContext(BindableObject bindable, object value)
+ {
+ BindablePropertyContext bpContext = bindable.GetContext(BindingContextProperty);
+ if (bpContext != null && ((bpContext.Attributes & BindableContextAttributes.IsManuallySet) != 0))
+ return;
+
+ object oldContext = bindable._inheritedContext;
+ if (bpContext != null && oldContext == null)
+ oldContext = bpContext.Value;
+
+ if (bpContext != null && bpContext.Binding != null)
+ {
+ bpContext.Binding.Context = value;
+ bindable._inheritedContext = null;
+ }
+ else
+ {
+ if (ReferenceEquals(oldContext, value))
+ return;
+
+ bindable._inheritedContext = value;
+ }
+
+ bindable.ApplyBindings(oldContext);
+ bindable.OnBindingContextChanged();
+ }
+
+ protected void ApplyBindings(object oldContext = null)
+ {
+ ApplyBindings(oldContext, false);
+ }
+
+ protected virtual void OnBindingContextChanged()
+ {
+ EventHandler change = BindingContextChanged;
+ if (change != null)
+ change(this, EventArgs.Empty);
+ }
+
+ protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
+ {
+ PropertyChangedEventHandler handler = PropertyChanged;
+ if (handler != null)
+ handler(this, new PropertyChangedEventArgs(propertyName));
+ }
+
+ protected virtual void OnPropertyChanging([CallerMemberName] string propertyName = null)
+ {
+ PropertyChangingEventHandler changing = PropertyChanging;
+ if (changing != null)
+ changing(this, new PropertyChangingEventArgs(propertyName));
+ }
+
+ protected void UnapplyBindings()
+ {
+ foreach (BindablePropertyContext context in _properties)
+ {
+ if (context.Binding == null)
+ continue;
+
+ context.Binding.Unapply();
+ }
+ }
+
+ internal bool GetIsBound(BindableProperty targetProperty)
+ {
+ if (targetProperty == null)
+ throw new ArgumentNullException("targetProperty");
+
+ BindablePropertyContext bpcontext = GetContext(targetProperty);
+ return bpcontext != null && bpcontext.Binding != null;
+ }
+
+ internal object[] GetValues(BindableProperty property0, BindableProperty property1)
+ {
+ var values = new object[2];
+
+ for (var i = 0; i < _properties.Count; i++)
+ {
+ BindablePropertyContext context = _properties[i];
+
+ if (ReferenceEquals(context.Property, property0))
+ {
+ values[0] = context.Value;
+ property0 = null;
+ }
+ else if (ReferenceEquals(context.Property, property1))
+ {
+ values[1] = context.Value;
+ property1 = null;
+ }
+
+ if (property0 == null && property1 == null)
+ return values;
+ }
+
+ if (!ReferenceEquals(property0, null))
+ values[0] = property0.DefaultValueCreator == null ? property0.DefaultValue : CreateAndAddContext(property0).Value;
+ if (!ReferenceEquals(property1, null))
+ values[1] = property1.DefaultValueCreator == null ? property1.DefaultValue : CreateAndAddContext(property1).Value;
+
+ return values;
+ }
+
+ internal object[] GetValues(BindableProperty property0, BindableProperty property1, BindableProperty property2)
+ {
+ var values = new object[3];
+
+ for (var i = 0; i < _properties.Count; i++)
+ {
+ BindablePropertyContext context = _properties[i];
+
+ if (ReferenceEquals(context.Property, property0))
+ {
+ values[0] = context.Value;
+ property0 = null;
+ }
+ else if (ReferenceEquals(context.Property, property1))
+ {
+ values[1] = context.Value;
+ property1 = null;
+ }
+ else if (ReferenceEquals(context.Property, property2))
+ {
+ values[2] = context.Value;
+ property2 = null;
+ }
+
+ if (property0 == null && property1 == null && property2 == null)
+ return values;
+ }
+
+ if (!ReferenceEquals(property0, null))
+ values[0] = property0.DefaultValueCreator == null ? property0.DefaultValue : CreateAndAddContext(property0).Value;
+ if (!ReferenceEquals(property1, null))
+ values[1] = property1.DefaultValueCreator == null ? property1.DefaultValue : CreateAndAddContext(property1).Value;
+ if (!ReferenceEquals(property2, null))
+ values[2] = property2.DefaultValueCreator == null ? property2.DefaultValue : CreateAndAddContext(property2).Value;
+
+ return values;
+ }
+
+ internal virtual void OnRemoveDynamicResource(BindableProperty property)
+ {
+ }
+
+ internal virtual void OnSetDynamicResource(BindableProperty property, string key)
+ {
+ }
+
+ internal void RemoveDynamicResource(BindableProperty property)
+ {
+ if (property == null)
+ throw new ArgumentNullException("property");
+
+ OnRemoveDynamicResource(property);
+ BindablePropertyContext context = GetOrCreateContext(property);
+ context.Attributes &= ~BindableContextAttributes.IsDynamicResource;
+ }
+
+ internal void SetBinding(BindableProperty targetProperty, BindingBase binding, bool fromStyle)
+ {
+ if (targetProperty == null)
+ throw new ArgumentNullException("targetProperty");
+ if (binding == null)
+ throw new ArgumentNullException("binding");
+
+ BindablePropertyContext context = null;
+ if (fromStyle && (context = GetContext(targetProperty)) != null && (context.Attributes & BindableContextAttributes.IsDefaultValue) == 0 &&
+ (context.Attributes & BindableContextAttributes.IsSetFromStyle) == 0)
+ return;
+
+ context = context ?? GetOrCreateContext(targetProperty);
+ if (fromStyle)
+ context.Attributes |= BindableContextAttributes.IsSetFromStyle;
+ else
+ context.Attributes &= ~BindableContextAttributes.IsSetFromStyle;
+
+ if (context.Binding != null)
+ context.Binding.Unapply();
+
+ BindingBase oldBinding = context.Binding;
+ context.Binding = binding;
+
+ if (targetProperty.BindingChanging != null)
+ targetProperty.BindingChanging(this, oldBinding, binding);
+
+ binding.Apply(BindingContext, this, targetProperty);
+ }
+
+ internal void SetDynamicResource(BindableProperty property, string key)
+ {
+ SetDynamicResource(property, key, false);
+ }
+
+ internal void SetDynamicResource(BindableProperty property, string key, bool fromStyle)
+ {
+ if (property == null)
+ throw new ArgumentNullException("property");
+ if (string.IsNullOrEmpty(key))
+ throw new ArgumentNullException("key");
+
+ BindablePropertyContext context = null;
+ if (fromStyle && (context = GetContext(property)) != null && (context.Attributes & BindableContextAttributes.IsDefaultValue) == 0 &&
+ (context.Attributes & BindableContextAttributes.IsSetFromStyle) == 0)
+ return;
+
+ context = context ?? GetOrCreateContext(property);
+
+ context.Attributes |= BindableContextAttributes.IsDynamicResource;
+ if (fromStyle)
+ context.Attributes |= BindableContextAttributes.IsSetFromStyle;
+ else
+ context.Attributes &= ~BindableContextAttributes.IsSetFromStyle;
+
+ OnSetDynamicResource(property, key);
+ }
+
+ internal void SetValue(BindableProperty property, object value, bool fromStyle)
+ {
+ SetValue(property, value, fromStyle, true);
+ }
+
+ internal void SetValueCore(BindablePropertyKey propertyKey, object value, SetValueFlags attributes = SetValueFlags.None)
+ {
+ SetValueCore(propertyKey.BindableProperty, value, attributes, SetValuePrivateFlags.None);
+ }
+
+ internal void SetValueCore(BindableProperty property, object value, SetValueFlags attributes = SetValueFlags.None)
+ {
+ SetValueCore(property, value, attributes, SetValuePrivateFlags.Default);
+ }
+
+ internal void SetValueCore(BindableProperty property, object value, SetValueFlags attributes, SetValuePrivateFlags privateAttributes)
+ {
+ bool checkAccess = (privateAttributes & SetValuePrivateFlags.CheckAccess) != 0;
+ bool manuallySet = (privateAttributes & SetValuePrivateFlags.ManuallySet) != 0;
+ bool silent = (privateAttributes & SetValuePrivateFlags.Silent) != 0;
+ bool fromStyle = (privateAttributes & SetValuePrivateFlags.FromStyle) != 0;
+ bool converted = (privateAttributes & SetValuePrivateFlags.Converted) != 0;
+
+ if (property == null)
+ throw new ArgumentNullException("property");
+ if (checkAccess && property.IsReadOnly)
+ {
+ Debug.WriteLine("Can not set the BindableProperty \"{0}\" because it is readonly.", property.PropertyName);
+ return;
+ }
+
+ if (!converted && !property.TryConvert(ref value))
+ {
+ Log.Warning("SetValue", "Can not convert {0} to type '{1}'", value, property.ReturnType);
+ return;
+ }
+
+ if (property.ValidateValue != null && !property.ValidateValue(this, value))
+ throw new ArgumentException("Value was an invalid value for " + property.PropertyName, "value");
+
+ if (property.CoerceValue != null)
+ value = property.CoerceValue(this, value);
+
+ BindablePropertyContext context = GetOrCreateContext(property);
+ if (manuallySet)
+ context.Attributes |= BindableContextAttributes.IsManuallySet;
+ else
+ context.Attributes &= ~BindableContextAttributes.IsManuallySet;
+
+ if (fromStyle)
+ context.Attributes |= BindableContextAttributes.IsSetFromStyle;
+ // else ommitted on purpose
+
+ bool currentlyApplying = _applying;
+
+ if ((context.Attributes & BindableContextAttributes.IsBeingSet) != 0)
+ {
+ Queue<SetValueArgs> delayQueue = context.DelayedSetters;
+ if (delayQueue == null)
+ context.DelayedSetters = delayQueue = new Queue<SetValueArgs>();
+
+ delayQueue.Enqueue(new SetValueArgs(property, context, value, currentlyApplying, attributes));
+ }
+ else
+ {
+ context.Attributes |= BindableContextAttributes.IsBeingSet;
+ SetValueActual(property, context, value, currentlyApplying, attributes, silent);
+
+ Queue<SetValueArgs> delayQueue = context.DelayedSetters;
+ if (delayQueue != null)
+ {
+ while (delayQueue.Count > 0)
+ {
+ SetValueArgs s = delayQueue.Dequeue();
+ SetValueActual(s.Property, s.Context, s.Value, s.CurrentlyApplying, s.Attributes);
+ }
+
+ context.DelayedSetters = null;
+ }
+
+ context.Attributes &= ~BindableContextAttributes.IsBeingSet;
+ }
+ }
+
+ void ApplyBindings(object oldContext, bool skipBindingContext)
+ {
+ foreach (BindablePropertyContext context in _properties.ToArray())
+ {
+ BindingBase binding = context.Binding;
+ if (binding == null)
+ continue;
+
+ if (skipBindingContext && context.Property == BindingContextProperty)
+ continue;
+
+ binding.Unapply();
+ binding.Apply(BindingContext, this, context.Property);
+ }
+ }
+
+ static void BindingContextPropertyBindingChanging(BindableObject bindable, BindingBase oldBindingBase, BindingBase newBindingBase)
+ {
+ object context = bindable._inheritedContext;
+ var oldBinding = oldBindingBase as Binding;
+ var newBinding = newBindingBase as Binding;
+
+ if (context == null && oldBinding != null)
+ context = oldBinding.Context;
+ if (context != null && newBinding != null)
+ newBinding.Context = context;
+ }
+
+ static void BindingContextPropertyBindingPropertyChanged(BindableObject bindable, object oldvalue, object newvalue)
+ {
+ object oldInheritedContext = bindable._inheritedContext;
+ bindable._inheritedContext = null;
+ bindable.ApplyBindings(oldInheritedContext ?? oldvalue, true);
+ bindable.OnBindingContextChanged();
+ }
+
+ void ClearValue(BindableProperty property, bool checkaccess)
+ {
+ if (property == null)
+ throw new ArgumentNullException("property");
+
+ if (checkaccess && property.IsReadOnly)
+ throw new InvalidOperationException(string.Format("The BindableProperty \"{0}\" is readonly.", property.PropertyName));
+
+ BindablePropertyContext bpcontext = GetContext(property);
+ if (bpcontext == null)
+ return;
+
+ object original = bpcontext.Value;
+
+ object newValue = property.GetDefaultValue(this);
+
+ bool same = Equals(original, newValue);
+ if (!same)
+ {
+ if (property.PropertyChanging != null)
+ property.PropertyChanging(this, original, newValue);
+
+ OnPropertyChanging(property.PropertyName);
+ }
+
+ bpcontext.Attributes &= ~BindableContextAttributes.IsManuallySet;
+ bpcontext.Value = newValue;
+ bpcontext.Attributes |= BindableContextAttributes.IsDefaultValue;
+
+ if (!same)
+ {
+ OnPropertyChanged(property.PropertyName);
+ if (property.PropertyChanged != null)
+ property.PropertyChanged(this, original, newValue);
+ }
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ BindablePropertyContext CreateAndAddContext(BindableProperty property)
+ {
+ var context = new BindablePropertyContext { Property = property, Value = property.DefaultValueCreator != null ? property.DefaultValueCreator(this) : property.DefaultValue };
+
+ if (property.DefaultValueCreator != null)
+ context.Attributes = BindableContextAttributes.IsDefaultValue;
+
+ _properties.Add(context);
+ return context;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ BindablePropertyContext GetContext(BindableProperty property)
+ {
+ List<BindablePropertyContext> properties = _properties;
+
+ for (var i = 0; i < properties.Count; i++)
+ {
+ BindablePropertyContext context = properties[i];
+ if (ReferenceEquals(context.Property, property))
+ return context;
+ }
+
+ return null;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ BindablePropertyContext GetOrCreateContext(BindableProperty property)
+ {
+ BindablePropertyContext context = GetContext(property);
+ if (context == null)
+ {
+ context = CreateAndAddContext(property);
+ }
+
+ return context;
+ }
+
+ void RemoveBinding(BindableProperty property, BindablePropertyContext context)
+ {
+ context.Binding.Unapply();
+
+ if (property.BindingChanging != null)
+ property.BindingChanging(this, context.Binding, null);
+
+ context.Binding = null;
+ }
+
+ void SetValue(BindableProperty property, object value, bool fromStyle, bool checkAccess)
+ {
+ if (property == null)
+ throw new ArgumentNullException("property");
+
+ if (checkAccess && property.IsReadOnly)
+ throw new InvalidOperationException(string.Format("The BindableProperty \"{0}\" is readonly.", property.PropertyName));
+
+ BindablePropertyContext context = null;
+ if (fromStyle && (context = GetContext(property)) != null && (context.Attributes & BindableContextAttributes.IsDefaultValue) == 0 &&
+ (context.Attributes & BindableContextAttributes.IsSetFromStyle) == 0)
+ return;
+
+ SetValueCore(property, value, SetValueFlags.ClearOneWayBindings | SetValueFlags.ClearDynamicResource,
+ (fromStyle ? SetValuePrivateFlags.FromStyle : SetValuePrivateFlags.ManuallySet) | (checkAccess ? SetValuePrivateFlags.CheckAccess : 0));
+ }
+
+ void SetValueActual(BindableProperty property, BindablePropertyContext context, object value, bool currentlyApplying, SetValueFlags attributes, bool silent = false)
+ {
+ object original = context.Value;
+ bool raiseOnEqual = (attributes & SetValueFlags.RaiseOnEqual) != 0;
+ bool clearDynamicResources = (attributes & SetValueFlags.ClearDynamicResource) != 0;
+ bool clearOneWayBindings = (attributes & SetValueFlags.ClearOneWayBindings) != 0;
+ bool clearTwoWayBindings = (attributes & SetValueFlags.ClearTwoWayBindings) != 0;
+
+ bool same = Equals(value, original);
+ if (!silent && (!same || raiseOnEqual))
+ {
+ if (property.PropertyChanging != null)
+ property.PropertyChanging(this, original, value);
+
+ OnPropertyChanging(property.PropertyName);
+ }
+
+ if (!same || raiseOnEqual)
+ {
+ context.Value = value;
+ }
+
+ context.Attributes &= ~BindableContextAttributes.IsDefaultValue;
+
+ if ((context.Attributes & BindableContextAttributes.IsDynamicResource) != 0 && clearDynamicResources)
+ RemoveDynamicResource(property);
+
+ BindingBase binding = context.Binding;
+ if (binding != null)
+ {
+ if (clearOneWayBindings && binding.GetRealizedMode(property) == BindingMode.OneWay || clearTwoWayBindings && binding.GetRealizedMode(property) == BindingMode.TwoWay)
+ {
+ RemoveBinding(property, context);
+ binding = null;
+ }
+ }
+
+ if (!silent && (!same || raiseOnEqual))
+ {
+ if (binding != null && !currentlyApplying)
+ {
+ _applying = true;
+ binding.Apply(true);
+ _applying = false;
+ }
+
+ OnPropertyChanged(property.PropertyName);
+
+ if (property.PropertyChanged != null)
+ property.PropertyChanged(this, original, value);
+ }
+ }
+
+ [Flags]
+ enum BindableContextAttributes
+ {
+ IsManuallySet = 1 << 0,
+ IsBeingSet = 1 << 1,
+ IsDynamicResource = 1 << 2,
+ IsSetFromStyle = 1 << 3,
+ IsDefaultValue = 1 << 4
+ }
+
+ class BindablePropertyContext
+ {
+ public BindableContextAttributes Attributes;
+ public BindingBase Binding;
+ public Queue<SetValueArgs> DelayedSetters;
+ public BindableProperty Property;
+ public object Value;
+ }
+
+ [Flags]
+ internal enum SetValueFlags
+ {
+ None = 0,
+ ClearOneWayBindings = 1 << 0,
+ ClearTwoWayBindings = 1 << 1,
+ ClearDynamicResource = 1 << 2,
+ RaiseOnEqual = 1 << 3
+ }
+
+ [Flags]
+ internal enum SetValuePrivateFlags
+ {
+ None = 0,
+ CheckAccess = 1 << 0,
+ Silent = 1 << 1,
+ ManuallySet = 1 << 2,
+ FromStyle = 1 << 3,
+ Converted = 1 << 4,
+ Default = CheckAccess
+ }
+
+ class SetValueArgs
+ {
+ public readonly SetValueFlags Attributes;
+ public readonly BindablePropertyContext Context;
+ public readonly bool CurrentlyApplying;
+ public readonly BindableProperty Property;
+ public readonly object Value;
+
+ public SetValueArgs(BindableProperty property, BindablePropertyContext context, object value, bool currentlyApplying, SetValueFlags attributes)
+ {
+ Property = property;
+ Context = context;
+ Value = value;
+ CurrentlyApplying = currentlyApplying;
+ Attributes = attributes;
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/BindableObjectExtensions.cs b/Xamarin.Forms.Core/BindableObjectExtensions.cs
new file mode 100644
index 00000000..2eab2380
--- /dev/null
+++ b/Xamarin.Forms.Core/BindableObjectExtensions.cs
@@ -0,0 +1,34 @@
+using System;
+using System.Linq.Expressions;
+
+namespace Xamarin.Forms
+{
+ public static class BindableObjectExtensions
+ {
+ public static void SetBinding(this BindableObject self, BindableProperty targetProperty, string path, BindingMode mode = BindingMode.Default, IValueConverter converter = null,
+ string stringFormat = null)
+ {
+ if (self == null)
+ throw new ArgumentNullException("self");
+ if (targetProperty == null)
+ throw new ArgumentNullException("targetProperty");
+
+ var binding = new Binding(path, mode, converter, stringFormat: stringFormat);
+ self.SetBinding(targetProperty, binding);
+ }
+
+ public static void SetBinding<TSource>(this BindableObject self, BindableProperty targetProperty, Expression<Func<TSource, object>> sourceProperty, BindingMode mode = BindingMode.Default,
+ IValueConverter converter = null, string stringFormat = null)
+ {
+ if (self == null)
+ throw new ArgumentNullException("self");
+ if (targetProperty == null)
+ throw new ArgumentNullException("targetProperty");
+ if (sourceProperty == null)
+ throw new ArgumentNullException("sourceProperty");
+
+ Binding binding = Binding.Create(sourceProperty, mode, converter, stringFormat: stringFormat);
+ self.SetBinding(targetProperty, binding);
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/BindableProperty.cs b/Xamarin.Forms.Core/BindableProperty.cs
new file mode 100644
index 00000000..f95d38ab
--- /dev/null
+++ b/Xamarin.Forms.Core/BindableProperty.cs
@@ -0,0 +1,331 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq.Expressions;
+using System.Reflection;
+
+namespace Xamarin.Forms
+{
+ [DebuggerDisplay("{PropertyName}")]
+ [TypeConverter(typeof(BindablePropertyConverter))]
+ public sealed class BindableProperty
+ {
+ public delegate void BindingPropertyChangedDelegate(BindableObject bindable, object oldValue, object newValue);
+
+ public delegate void BindingPropertyChangedDelegate<in TPropertyType>(BindableObject bindable, TPropertyType oldValue, TPropertyType newValue);
+
+ public delegate void BindingPropertyChangingDelegate(BindableObject bindable, object oldValue, object newValue);
+
+ public delegate void BindingPropertyChangingDelegate<in TPropertyType>(BindableObject bindable, TPropertyType oldValue, TPropertyType newValue);
+
+ public delegate object CoerceValueDelegate(BindableObject bindable, object value);
+
+ public delegate TPropertyType CoerceValueDelegate<TPropertyType>(BindableObject bindable, TPropertyType value);
+
+ public delegate object CreateDefaultValueDelegate(BindableObject bindable);
+
+ public delegate TPropertyType CreateDefaultValueDelegate<in TDeclarer, out TPropertyType>(TDeclarer bindable);
+
+ public delegate bool ValidateValueDelegate(BindableObject bindable, object value);
+
+ public delegate bool ValidateValueDelegate<in TPropertyType>(BindableObject bindable, TPropertyType value);
+
+ // more or less the encoding of this, without the need to reflect
+ // http://msdn.microsoft.com/en-us/library/y5b434w4.aspx
+ static readonly Dictionary<Type, Type[]> SimpleConvertTypes = new Dictionary<Type, Type[]>
+ {
+ { typeof(sbyte), new[] { typeof(string), typeof(short), typeof(int), typeof(long), typeof(float), typeof(double), typeof(decimal) } },
+ { typeof(byte), new[] { typeof(string), typeof(short), typeof(ushort), typeof(int), typeof(uint), typeof(ulong), typeof(float), typeof(double), typeof(decimal) } },
+ { typeof(short), new[] { typeof(string), typeof(int), typeof(long), typeof(float), typeof(double), typeof(decimal) } },
+ { typeof(ushort), new[] { typeof(string), typeof(int), typeof(uint), typeof(long), typeof(ulong), typeof(float), typeof(double), typeof(decimal) } },
+ { typeof(int), new[] { typeof(string), typeof(long), typeof(float), typeof(double), typeof(decimal) } },
+ { typeof(uint), new[] { typeof(string), typeof(long), typeof(ulong), typeof(float), typeof(double), typeof(decimal) } },
+ { typeof(long), new[] { typeof(string), typeof(float), typeof(double), typeof(decimal) } },
+ { typeof(char), new[] { typeof(string), typeof(ushort), typeof(int), typeof(uint), typeof(long), typeof(ulong), typeof(float), typeof(double), typeof(decimal) } },
+ { typeof(float), new[] { typeof(string), typeof(double) } },
+ { typeof(ulong), new[] { typeof(string), typeof(float), typeof(double), typeof(decimal) } }
+ };
+
+ BindableProperty(string propertyName, Type returnType, Type declaringType, object defaultValue, BindingMode defaultBindingMode = BindingMode.OneWay,
+ ValidateValueDelegate validateValue = null, BindingPropertyChangedDelegate propertyChanged = null, BindingPropertyChangingDelegate propertyChanging = null,
+ CoerceValueDelegate coerceValue = null, BindablePropertyBindingChanging bindingChanging = null, bool isReadOnly = false, CreateDefaultValueDelegate defaultValueCreator = null)
+ {
+ if (propertyName == null)
+ throw new ArgumentNullException("propertyName");
+ if (ReferenceEquals(returnType, null))
+ throw new ArgumentNullException("returnType");
+ if (ReferenceEquals(declaringType, null))
+ throw new ArgumentNullException("declaringType");
+
+ // don't use Enum.IsDefined as its redonkulously expensive for what it does
+ if (defaultBindingMode != BindingMode.Default && defaultBindingMode != BindingMode.OneWay && defaultBindingMode != BindingMode.OneWayToSource && defaultBindingMode != BindingMode.TwoWay)
+ throw new ArgumentException("Not a valid type of BindingMode", "defaultBindingMode");
+ if (defaultValue == null && Nullable.GetUnderlyingType(returnType) == null && returnType.GetTypeInfo().IsValueType)
+ throw new ArgumentException("Not a valid default value", "defaultValue");
+ if (defaultValue != null && !returnType.IsInstanceOfType(defaultValue))
+ throw new ArgumentException("Default value did not match return type", "defaultValue");
+ if (defaultBindingMode == BindingMode.Default)
+ defaultBindingMode = BindingMode.OneWay;
+
+ PropertyName = propertyName;
+ ReturnType = returnType;
+ ReturnTypeInfo = returnType.GetTypeInfo();
+ DeclaringType = declaringType;
+ DefaultValue = defaultValue;
+ DefaultBindingMode = defaultBindingMode;
+ PropertyChanged = propertyChanged;
+ PropertyChanging = propertyChanging;
+ ValidateValue = validateValue;
+ CoerceValue = coerceValue;
+ BindingChanging = bindingChanging;
+ IsReadOnly = isReadOnly;
+ DefaultValueCreator = defaultValueCreator;
+ }
+
+ public Type DeclaringType { get; private set; }
+
+ public BindingMode DefaultBindingMode { get; private set; }
+
+ public object DefaultValue { get; }
+
+ public bool IsReadOnly { get; private set; }
+
+ public string PropertyName { get; }
+
+ public Type ReturnType { get; }
+
+ internal BindablePropertyBindingChanging BindingChanging { get; private set; }
+
+ internal CoerceValueDelegate CoerceValue { get; private set; }
+
+ internal CreateDefaultValueDelegate DefaultValueCreator { get; }
+
+ internal BindingPropertyChangedDelegate PropertyChanged { get; private set; }
+
+ internal BindingPropertyChangingDelegate PropertyChanging { get; private set; }
+
+ internal TypeInfo ReturnTypeInfo { get; }
+
+ internal ValidateValueDelegate ValidateValue { get; private set; }
+
+ [Obsolete("Generic versions of Create () are no longer supported and deprecated. They will be removed soon.")]
+ public static BindableProperty Create<TDeclarer, TPropertyType>(Expression<Func<TDeclarer, TPropertyType>> getter, TPropertyType defaultValue, BindingMode defaultBindingMode = BindingMode.OneWay,
+ ValidateValueDelegate<TPropertyType> validateValue = null, BindingPropertyChangedDelegate<TPropertyType> propertyChanged = null,
+ BindingPropertyChangingDelegate<TPropertyType> propertyChanging = null, CoerceValueDelegate<TPropertyType> coerceValue = null,
+ CreateDefaultValueDelegate<TDeclarer, TPropertyType> defaultValueCreator = null) where TDeclarer : BindableObject
+ {
+ return Create(getter, defaultValue, defaultBindingMode, validateValue, propertyChanged, propertyChanging, coerceValue, null, defaultValueCreator: defaultValueCreator);
+ }
+
+ public static BindableProperty Create(string propertyName, Type returnType, Type declaringType, object defaultValue = null, BindingMode defaultBindingMode = BindingMode.OneWay,
+ ValidateValueDelegate validateValue = null, BindingPropertyChangedDelegate propertyChanged = null, BindingPropertyChangingDelegate propertyChanging = null,
+ CoerceValueDelegate coerceValue = null, CreateDefaultValueDelegate defaultValueCreator = null)
+ {
+ return new BindableProperty(propertyName, returnType, declaringType, defaultValue, defaultBindingMode, validateValue, propertyChanged, propertyChanging, coerceValue,
+ defaultValueCreator: defaultValueCreator);
+ }
+
+ [Obsolete("Generic versions of Create () are no longer supported and deprecated. They will be removed soon.")]
+ public static BindableProperty CreateAttached<TDeclarer, TPropertyType>(Expression<Func<BindableObject, TPropertyType>> staticgetter, TPropertyType defaultValue,
+ BindingMode defaultBindingMode = BindingMode.OneWay, ValidateValueDelegate<TPropertyType> validateValue = null, BindingPropertyChangedDelegate<TPropertyType> propertyChanged = null,
+ BindingPropertyChangingDelegate<TPropertyType> propertyChanging = null, CoerceValueDelegate<TPropertyType> coerceValue = null,
+ CreateDefaultValueDelegate<BindableObject, TPropertyType> defaultValueCreator = null)
+ {
+ return CreateAttached<TDeclarer, TPropertyType>(staticgetter, defaultValue, defaultBindingMode, validateValue, propertyChanged, propertyChanging, coerceValue, null,
+ defaultValueCreator: defaultValueCreator);
+ }
+
+ public static BindableProperty CreateAttached(string propertyName, Type returnType, Type declaringType, object defaultValue, BindingMode defaultBindingMode = BindingMode.OneWay,
+ ValidateValueDelegate validateValue = null, BindingPropertyChangedDelegate propertyChanged = null, BindingPropertyChangingDelegate propertyChanging = null,
+ CoerceValueDelegate coerceValue = null, CreateDefaultValueDelegate defaultValueCreator = null)
+ {
+ return CreateAttached(propertyName, returnType, declaringType, defaultValue, defaultBindingMode, validateValue, propertyChanged, propertyChanging, coerceValue, null, false, defaultValueCreator);
+ }
+
+ [Obsolete("Generic versions of Create () are no longer supported and deprecated. They will be removed soon.")]
+ public static BindablePropertyKey CreateAttachedReadOnly<TDeclarer, TPropertyType>(Expression<Func<BindableObject, TPropertyType>> staticgetter, TPropertyType defaultValue,
+ BindingMode defaultBindingMode = BindingMode.OneWayToSource, ValidateValueDelegate<TPropertyType> validateValue = null,
+ BindingPropertyChangedDelegate<TPropertyType> propertyChanged = null, BindingPropertyChangingDelegate<TPropertyType> propertyChanging = null,
+ CoerceValueDelegate<TPropertyType> coerceValue = null, CreateDefaultValueDelegate<BindableObject, TPropertyType> defaultValueCreator = null)
+
+ {
+ return
+ new BindablePropertyKey(CreateAttached<TDeclarer, TPropertyType>(staticgetter, defaultValue, defaultBindingMode, validateValue, propertyChanged, propertyChanging, coerceValue, null, true,
+ defaultValueCreator));
+ }
+
+ public static BindablePropertyKey CreateAttachedReadOnly(string propertyName, Type returnType, Type declaringType, object defaultValue, BindingMode defaultBindingMode = BindingMode.OneWayToSource,
+ ValidateValueDelegate validateValue = null, BindingPropertyChangedDelegate propertyChanged = null, BindingPropertyChangingDelegate propertyChanging = null,
+ CoerceValueDelegate coerceValue = null, CreateDefaultValueDelegate defaultValueCreator = null)
+ {
+ return
+ new BindablePropertyKey(CreateAttached(propertyName, returnType, declaringType, defaultValue, defaultBindingMode, validateValue, propertyChanged, propertyChanging, coerceValue, null, true,
+ defaultValueCreator));
+ }
+
+ [Obsolete("Generic versions of Create () are no longer supported and deprecated. They will be removed soon.")]
+ public static BindablePropertyKey CreateReadOnly<TDeclarer, TPropertyType>(Expression<Func<TDeclarer, TPropertyType>> getter, TPropertyType defaultValue,
+ BindingMode defaultBindingMode = BindingMode.OneWayToSource, ValidateValueDelegate<TPropertyType> validateValue = null,
+ BindingPropertyChangedDelegate<TPropertyType> propertyChanged = null, BindingPropertyChangingDelegate<TPropertyType> propertyChanging = null,
+ CoerceValueDelegate<TPropertyType> coerceValue = null, CreateDefaultValueDelegate<TDeclarer, TPropertyType> defaultValueCreator = null) where TDeclarer : BindableObject
+ {
+ return new BindablePropertyKey(Create(getter, defaultValue, defaultBindingMode, validateValue, propertyChanged, propertyChanging, coerceValue, null, true, defaultValueCreator));
+ }
+
+ public static BindablePropertyKey CreateReadOnly(string propertyName, Type returnType, Type declaringType, object defaultValue, BindingMode defaultBindingMode = BindingMode.OneWayToSource,
+ ValidateValueDelegate validateValue = null, BindingPropertyChangedDelegate propertyChanged = null, BindingPropertyChangingDelegate propertyChanging = null,
+ CoerceValueDelegate coerceValue = null, CreateDefaultValueDelegate defaultValueCreator = null)
+ {
+ return
+ new BindablePropertyKey(new BindableProperty(propertyName, returnType, declaringType, defaultValue, defaultBindingMode, validateValue, propertyChanged, propertyChanging, coerceValue,
+ isReadOnly: true, defaultValueCreator: defaultValueCreator));
+ }
+
+ [Obsolete("Generic versions of Create () are no longer supported and deprecated. They will be removed soon.")]
+ internal static BindableProperty Create<TDeclarer, TPropertyType>(Expression<Func<TDeclarer, TPropertyType>> getter, TPropertyType defaultValue, BindingMode defaultBindingMode,
+ ValidateValueDelegate<TPropertyType> validateValue, BindingPropertyChangedDelegate<TPropertyType> propertyChanged, BindingPropertyChangingDelegate<TPropertyType> propertyChanging,
+ CoerceValueDelegate<TPropertyType> coerceValue, BindablePropertyBindingChanging bindingChanging, bool isReadOnly = false,
+ CreateDefaultValueDelegate<TDeclarer, TPropertyType> defaultValueCreator = null) where TDeclarer : BindableObject
+ {
+ if (getter == null)
+ throw new ArgumentNullException("getter");
+
+ Expression expr = getter.Body;
+
+ var unary = expr as UnaryExpression;
+ if (unary != null)
+ expr = unary.Operand;
+
+ var member = expr as MemberExpression;
+ if (member == null)
+ throw new ArgumentException("getter must be a MemberExpression", "getter");
+
+ var property = (PropertyInfo)member.Member;
+
+ ValidateValueDelegate untypedValidateValue = null;
+ BindingPropertyChangedDelegate untypedBindingPropertyChanged = null;
+ BindingPropertyChangingDelegate untypedBindingPropertyChanging = null;
+ CoerceValueDelegate untypedCoerceValue = null;
+ CreateDefaultValueDelegate untypedDefaultValueCreator = null;
+ if (validateValue != null)
+ untypedValidateValue = (bindable, value) => validateValue(bindable, (TPropertyType)value);
+ if (propertyChanged != null)
+ untypedBindingPropertyChanged = (bindable, oldValue, newValue) => propertyChanged(bindable, (TPropertyType)oldValue, (TPropertyType)newValue);
+ if (propertyChanging != null)
+ untypedBindingPropertyChanging = (bindable, oldValue, newValue) => propertyChanging(bindable, (TPropertyType)oldValue, (TPropertyType)newValue);
+ if (coerceValue != null)
+ untypedCoerceValue = (bindable, value) => coerceValue(bindable, (TPropertyType)value);
+ if (defaultValueCreator != null)
+ untypedDefaultValueCreator = o => defaultValueCreator((TDeclarer)o);
+
+ return new BindableProperty(property.Name, property.PropertyType, typeof(TDeclarer), defaultValue, defaultBindingMode, untypedValidateValue, untypedBindingPropertyChanged,
+ untypedBindingPropertyChanging, untypedCoerceValue, bindingChanging, isReadOnly, untypedDefaultValueCreator);
+ }
+
+ internal static BindableProperty Create(string propertyName, Type returnType, Type declaringType, object defaultValue, BindingMode defaultBindingMode, ValidateValueDelegate validateValue,
+ BindingPropertyChangedDelegate propertyChanged, BindingPropertyChangingDelegate propertyChanging, CoerceValueDelegate coerceValue, BindablePropertyBindingChanging bindingChanging,
+ CreateDefaultValueDelegate defaultValueCreator = null)
+ {
+ return new BindableProperty(propertyName, returnType, declaringType, defaultValue, defaultBindingMode, validateValue, propertyChanged, propertyChanging, coerceValue, bindingChanging,
+ defaultValueCreator: defaultValueCreator);
+ }
+
+ [Obsolete("Generic versions of Create () are no longer supported and deprecated. They will be removed soon.")]
+ internal static BindableProperty CreateAttached<TDeclarer, TPropertyType>(Expression<Func<BindableObject, TPropertyType>> staticgetter, TPropertyType defaultValue, BindingMode defaultBindingMode,
+ ValidateValueDelegate<TPropertyType> validateValue, BindingPropertyChangedDelegate<TPropertyType> propertyChanged, BindingPropertyChangingDelegate<TPropertyType> propertyChanging,
+ CoerceValueDelegate<TPropertyType> coerceValue, BindablePropertyBindingChanging bindingChanging, bool isReadOnly = false,
+ CreateDefaultValueDelegate<BindableObject, TPropertyType> defaultValueCreator = null)
+ {
+ if (staticgetter == null)
+ throw new ArgumentNullException("staticgetter");
+
+ Expression expr = staticgetter.Body;
+
+ var unary = expr as UnaryExpression;
+ if (unary != null)
+ expr = unary.Operand;
+
+ var methodcall = expr as MethodCallExpression;
+ if (methodcall == null)
+ throw new ArgumentException("staticgetter must be a MethodCallExpression", "staticgetter");
+
+ MethodInfo method = methodcall.Method;
+ if (!method.Name.StartsWith("Get", StringComparison.Ordinal))
+ throw new ArgumentException("staticgetter name must start with Get", "staticgetter");
+
+ string propertyname = method.Name.Substring(3);
+
+ ValidateValueDelegate untypedValidateValue = null;
+ BindingPropertyChangedDelegate untypedBindingPropertyChanged = null;
+ BindingPropertyChangingDelegate untypedBindingPropertyChanging = null;
+ CoerceValueDelegate untypedCoerceValue = null;
+ CreateDefaultValueDelegate untypedDefaultValueCreator = null;
+ if (validateValue != null)
+ untypedValidateValue = (bindable, value) => validateValue(bindable, (TPropertyType)value);
+ if (propertyChanged != null)
+ untypedBindingPropertyChanged = (bindable, oldValue, newValue) => propertyChanged(bindable, (TPropertyType)oldValue, (TPropertyType)newValue);
+ if (propertyChanging != null)
+ untypedBindingPropertyChanging = (bindable, oldValue, newValue) => propertyChanging(bindable, (TPropertyType)oldValue, (TPropertyType)newValue);
+ if (coerceValue != null)
+ untypedCoerceValue = (bindable, value) => coerceValue(bindable, (TPropertyType)value);
+ if (defaultValueCreator != null)
+ untypedDefaultValueCreator = o => defaultValueCreator(o);
+
+ return new BindableProperty(propertyname, method.ReturnType, typeof(TDeclarer), defaultValue, defaultBindingMode, untypedValidateValue, untypedBindingPropertyChanged, untypedBindingPropertyChanging,
+ untypedCoerceValue, bindingChanging, isReadOnly, untypedDefaultValueCreator);
+ }
+
+ internal static BindableProperty CreateAttached(string propertyName, Type returnType, Type declaringType, object defaultValue, BindingMode defaultBindingMode, ValidateValueDelegate validateValue,
+ BindingPropertyChangedDelegate propertyChanged, BindingPropertyChangingDelegate propertyChanging, CoerceValueDelegate coerceValue, BindablePropertyBindingChanging bindingChanging,
+ bool isReadOnly, CreateDefaultValueDelegate defaultValueCreator = null)
+ {
+ return new BindableProperty(propertyName, returnType, declaringType, defaultValue, defaultBindingMode, validateValue, propertyChanged, propertyChanging, coerceValue, bindingChanging, isReadOnly,
+ defaultValueCreator);
+ }
+
+ internal object GetDefaultValue(BindableObject bindable)
+ {
+ if (DefaultValueCreator != null)
+ return DefaultValueCreator(bindable);
+
+ return DefaultValue;
+ }
+
+ internal bool TryConvert(ref object value)
+ {
+ if (value == null)
+ {
+ return !ReturnTypeInfo.IsValueType || ReturnTypeInfo.IsGenericType && ReturnTypeInfo.GetGenericTypeDefinition() == typeof(Nullable<>);
+ }
+
+ Type valueType = value.GetType();
+ Type type = ReturnType;
+
+ // Dont support arbitrary IConvertible by limiting which types can use this
+ Type[] convertableTo;
+ if (SimpleConvertTypes.TryGetValue(valueType, out convertableTo) && Array.IndexOf(convertableTo, type) != -1)
+ {
+ value = Convert.ChangeType(value, type);
+ }
+ else if (!ReturnTypeInfo.IsAssignableFrom(valueType.GetTypeInfo()))
+ {
+ // Is there an implicit cast operator ?
+ MethodInfo cast = type.GetRuntimeMethod("op_Implicit", new[] { valueType });
+ if (cast != null && cast.ReturnType != type)
+ cast = null;
+ if (cast == null)
+ cast = valueType.GetRuntimeMethod("op_Implicit", new[] { valueType });
+ if (cast != null && cast.ReturnType != type)
+ cast = null;
+ if (cast == null)
+ return false;
+
+ value = cast.Invoke(null, new[] { value });
+ }
+
+ return true;
+ }
+
+ internal delegate void BindablePropertyBindingChanging(BindableObject bindable, BindingBase oldValue, BindingBase newValue);
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/BindablePropertyConverter.cs b/Xamarin.Forms.Core/BindablePropertyConverter.cs
new file mode 100644
index 00000000..af3fd5b6
--- /dev/null
+++ b/Xamarin.Forms.Core/BindablePropertyConverter.cs
@@ -0,0 +1,105 @@
+using System;
+using System.Globalization;
+using System.Linq;
+using System.Reflection;
+using System.Xml;
+using Xamarin.Forms.Xaml;
+
+namespace Xamarin.Forms
+{
+ public sealed class BindablePropertyConverter : TypeConverter, IExtendedTypeConverter
+ {
+ object IExtendedTypeConverter.ConvertFrom(CultureInfo culture, object value, IServiceProvider serviceProvider)
+ {
+ return ((IExtendedTypeConverter)this).ConvertFromInvariantString(value as string, serviceProvider);
+ }
+
+ object IExtendedTypeConverter.ConvertFromInvariantString(string value, IServiceProvider serviceProvider)
+ {
+ if (string.IsNullOrWhiteSpace(value))
+ return null;
+ if (serviceProvider == null)
+ return null;
+ var parentValuesProvider = serviceProvider.GetService(typeof(IProvideValueTarget)) as IProvideParentValues;
+ var typeResolver = serviceProvider.GetService(typeof(IXamlTypeResolver)) as IXamlTypeResolver;
+ if (typeResolver == null)
+ return null;
+ IXmlLineInfo lineinfo = null;
+ var xmlLineInfoProvider = serviceProvider.GetService(typeof(IXmlLineInfoProvider)) as IXmlLineInfoProvider;
+ if (xmlLineInfoProvider != null)
+ lineinfo = xmlLineInfoProvider.XmlLineInfo;
+ string[] parts = value.Split('.');
+ Type type = null;
+ if (parts.Length == 1)
+ {
+ if (parentValuesProvider == null)
+ {
+ string msg = string.Format("Can't resolve {0}", parts[0]);
+ throw new XamlParseException(msg, lineinfo);
+ }
+ object parent = parentValuesProvider.ParentObjects.Skip(1).FirstOrDefault();
+ if (parentValuesProvider.TargetObject is Setter)
+ {
+ var style = parent as Style;
+ var triggerBase = parent as TriggerBase;
+ if (style != null)
+ type = style.TargetType;
+ else if (triggerBase != null)
+ type = triggerBase.TargetType;
+ }
+ else if (parentValuesProvider.TargetObject is Trigger)
+ type = (parentValuesProvider.TargetObject as Trigger).TargetType;
+
+ if (type == null)
+ {
+ string msg = string.Format("Can't resolve {0}", parts[0]);
+ throw new XamlParseException(msg, lineinfo);
+ }
+
+ return ConvertFrom(type, parts[0], lineinfo);
+ }
+ if (parts.Length == 2)
+ {
+ if (!typeResolver.TryResolve(parts[0], out type))
+ {
+ string msg = string.Format("Can't resolve {0}", parts[0]);
+ throw new XamlParseException(msg, lineinfo);
+ }
+ return ConvertFrom(type, parts[1], lineinfo);
+ }
+ string emsg = string.Format("Can't resolve {0}. Syntax is [[ns:]Type.]PropertyName.", value);
+ throw new XamlParseException(emsg, lineinfo);
+ }
+
+ public override object ConvertFromInvariantString(string value)
+ {
+ if (string.IsNullOrWhiteSpace(value))
+ return null;
+ if (value.Contains(":"))
+ {
+ Log.Warning(null, "Can't resolve properties with xml namespace prefix.");
+ return null;
+ }
+ string[] parts = value.Split('.');
+ if (parts.Length != 2)
+ {
+ Log.Warning(null, "Can't resolve {0}. Accepted syntax is Type.PropertyName.", value);
+ return null;
+ }
+ Type type = Type.GetType("Xamarin.Forms." + parts[0]);
+ return ConvertFrom(type, parts[1], null);
+ }
+
+ BindableProperty ConvertFrom(Type type, string propertyName, IXmlLineInfo lineinfo)
+ {
+ string name = propertyName + "Property";
+ FieldInfo bpinfo = type.GetField(fi => fi.Name == name && fi.IsStatic && fi.IsPublic && fi.FieldType == typeof(BindableProperty));
+ if (bpinfo == null)
+ throw new XamlParseException(string.Format("Can't resolve {0} on {1}", name, type.Name), lineinfo);
+ var bp = bpinfo.GetValue(null) as BindableProperty;
+ if (bp.PropertyName != propertyName)
+ throw new XamlParseException(string.Format("The PropertyName of {0}.{1} is not {2}", type.Name, name, propertyName), lineinfo);
+ return bp;
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/BindablePropertyKey.cs b/Xamarin.Forms.Core/BindablePropertyKey.cs
new file mode 100644
index 00000000..a4b9f4a4
--- /dev/null
+++ b/Xamarin.Forms.Core/BindablePropertyKey.cs
@@ -0,0 +1,17 @@
+using System;
+
+namespace Xamarin.Forms
+{
+ public sealed class BindablePropertyKey
+ {
+ internal BindablePropertyKey(BindableProperty property)
+ {
+ if (property == null)
+ throw new ArgumentNullException("property");
+
+ BindableProperty = property;
+ }
+
+ public BindableProperty BindableProperty { get; private set; }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/Binding.cs b/Xamarin.Forms.Core/Binding.cs
new file mode 100644
index 00000000..5fa1dd65
--- /dev/null
+++ b/Xamarin.Forms.Core/Binding.cs
@@ -0,0 +1,233 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
+using System.Linq.Expressions;
+using System.Reflection;
+using System.Text;
+
+namespace Xamarin.Forms
+{
+ public sealed class Binding : BindingBase
+ {
+ internal const string SelfPath = ".";
+ IValueConverter _converter;
+ object _converterParameter;
+
+ BindingExpression _expression;
+ string _path;
+ object _source;
+
+ public Binding()
+ {
+ }
+
+ public Binding(string path, BindingMode mode = BindingMode.Default, IValueConverter converter = null, object converterParameter = null, string stringFormat = null, object source = null)
+ {
+ if (path == null)
+ throw new ArgumentNullException("path");
+ if (string.IsNullOrWhiteSpace(path))
+ throw new ArgumentException("path can not be an empty string", "path");
+
+ Path = path;
+ Converter = converter;
+ ConverterParameter = converterParameter;
+ Mode = mode;
+ StringFormat = stringFormat;
+ Source = source;
+ }
+
+ public IValueConverter Converter
+ {
+ get { return _converter; }
+ set
+ {
+ ThrowIfApplied();
+
+ _converter = value;
+ }
+ }
+
+ public object ConverterParameter
+ {
+ get { return _converterParameter; }
+ set
+ {
+ ThrowIfApplied();
+
+ _converterParameter = value;
+ }
+ }
+
+ public string Path
+ {
+ get { return _path; }
+ set
+ {
+ ThrowIfApplied();
+
+ _path = value;
+ _expression = new BindingExpression(this, !string.IsNullOrWhiteSpace(value) ? value : SelfPath);
+ }
+ }
+
+ public object Source
+ {
+ get { return _source; }
+ set
+ {
+ ThrowIfApplied();
+ _source = value;
+ }
+ }
+
+ public static Binding Create<TSource>(Expression<Func<TSource, object>> propertyGetter, BindingMode mode = BindingMode.Default, IValueConverter converter = null, object converterParameter = null,
+ string stringFormat = null)
+ {
+ if (propertyGetter == null)
+ throw new ArgumentNullException("propertyGetter");
+
+ string path = GetBindingPath(propertyGetter);
+ return new Binding(path, mode, converter, converterParameter, stringFormat);
+ }
+
+ internal override void Apply(bool fromTarget)
+ {
+ base.Apply(fromTarget);
+
+ if (_expression == null)
+ _expression = new BindingExpression(this, SelfPath);
+
+ _expression.Apply(fromTarget);
+ }
+
+ internal override void Apply(object newContext, BindableObject bindObj, BindableProperty targetProperty)
+ {
+ object src = _source;
+ base.Apply(src ?? newContext, bindObj, targetProperty);
+
+ object bindingContext = src ?? Context ?? newContext;
+ if (_expression == null && bindingContext != null)
+ _expression = new BindingExpression(this, SelfPath);
+
+ _expression.Apply(bindingContext, bindObj, targetProperty);
+ }
+
+ internal override BindingBase Clone()
+ {
+ return new Binding(Path, Mode) { Converter = Converter, ConverterParameter = ConverterParameter, StringFormat = StringFormat, Source = Source };
+ }
+
+ internal override object GetSourceValue(object value, Type targetPropertyType)
+ {
+ if (Converter != null)
+ value = Converter.Convert(value, targetPropertyType, ConverterParameter, CultureInfo.CurrentUICulture);
+
+ return base.GetSourceValue(value, targetPropertyType);
+ }
+
+ internal override object GetTargetValue(object value, Type sourcePropertyType)
+ {
+ if (Converter != null)
+ value = Converter.ConvertBack(value, sourcePropertyType, ConverterParameter, CultureInfo.CurrentUICulture);
+
+ return base.GetTargetValue(value, sourcePropertyType);
+ }
+
+ internal override void Unapply()
+ {
+ base.Unapply();
+
+ if (_expression != null)
+ _expression.Unapply();
+ }
+
+ static string GetBindingPath<TSource>(Expression<Func<TSource, object>> propertyGetter)
+ {
+ Expression expr = propertyGetter.Body;
+
+ var unary = expr as UnaryExpression;
+ if (unary != null)
+ expr = unary.Operand;
+
+ var builder = new StringBuilder();
+
+ var indexed = false;
+
+ var member = expr as MemberExpression;
+ if (member == null)
+ {
+ var methodCall = expr as MethodCallExpression;
+ if (methodCall != null)
+ {
+ if (methodCall.Arguments.Count == 0)
+ throw new ArgumentException("Method calls are not allowed in binding expression");
+
+ var arguments = new List<string>(methodCall.Arguments.Count);
+ foreach (Expression arg in methodCall.Arguments)
+ {
+ if (arg.NodeType != ExpressionType.Constant)
+ throw new ArgumentException("Only constants can be used as indexer arguments");
+
+ object value = ((ConstantExpression)arg).Value;
+ arguments.Add(value != null ? value.ToString() : "null");
+ }
+
+ Type declarerType = methodCall.Method.DeclaringType;
+ DefaultMemberAttribute defaultMember = declarerType.GetTypeInfo().GetCustomAttributes(typeof(DefaultMemberAttribute), true).OfType<DefaultMemberAttribute>().FirstOrDefault();
+ string indexerName = defaultMember != null ? defaultMember.MemberName : "Item";
+
+ MethodInfo getterInfo =
+ declarerType.GetProperties().Where(pi => pi.Name == indexerName && pi.CanRead && pi.GetMethod.IsPublic && !pi.GetMethod.IsStatic).Select(pi => pi.GetMethod).FirstOrDefault();
+ if (getterInfo != null)
+ {
+ if (getterInfo == methodCall.Method)
+ {
+ indexed = true;
+ builder.Append("[");
+
+ var first = true;
+ foreach (string argument in arguments)
+ {
+ if (!first)
+ builder.Append(",");
+
+ builder.Append(argument);
+ first = false;
+ }
+
+ builder.Append("]");
+
+ member = methodCall.Object as MemberExpression;
+ }
+ else
+ throw new ArgumentException("Method calls are not allowed in binding expressions");
+ }
+ else
+ throw new ArgumentException("Public indexer not found");
+ }
+ else
+ throw new ArgumentException("Invalid expression type");
+ }
+
+ while (member != null)
+ {
+ var property = (PropertyInfo)member.Member;
+ if (builder.Length != 0)
+ {
+ if (!indexed)
+ builder.Insert(0, ".");
+ else
+ indexed = false;
+ }
+
+ builder.Insert(0, property.Name);
+
+ // member = member.Expression as MemberExpression ?? (member.Expression as UnaryExpression)?.Operand as MemberExpression;
+ member = member.Expression as MemberExpression ?? (member.Expression is UnaryExpression ? (member.Expression as UnaryExpression).Operand as MemberExpression : null);
+ }
+
+ return builder.ToString();
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/BindingBase.cs b/Xamarin.Forms.Core/BindingBase.cs
new file mode 100644
index 00000000..0810cbcf
--- /dev/null
+++ b/Xamarin.Forms.Core/BindingBase.cs
@@ -0,0 +1,111 @@
+using System;
+using System.Collections;
+using System.Runtime.CompilerServices;
+
+namespace Xamarin.Forms
+{
+ public abstract class BindingBase
+ {
+ static readonly ConditionalWeakTable<IEnumerable, CollectionSynchronizationContext> SynchronizedCollections = new ConditionalWeakTable<IEnumerable, CollectionSynchronizationContext>();
+
+ BindingMode _mode = BindingMode.Default;
+ string _stringFormat;
+
+ internal BindingBase()
+ {
+ }
+
+ public BindingMode Mode
+ {
+ get { return _mode; }
+ set
+ {
+ if (value != BindingMode.Default && value != BindingMode.OneWay && value != BindingMode.OneWayToSource && value != BindingMode.TwoWay)
+ throw new ArgumentException("mode is not a valid BindingMode", "mode");
+
+ ThrowIfApplied();
+
+ _mode = value;
+ }
+ }
+
+ public string StringFormat
+ {
+ get { return _stringFormat; }
+ set
+ {
+ ThrowIfApplied();
+
+ _stringFormat = value;
+ }
+ }
+
+ internal bool AllowChaining { get; set; }
+
+ internal object Context { get; set; }
+
+ internal bool IsApplied { get; private set; }
+
+ public static void DisableCollectionSynchronization(IEnumerable collection)
+ {
+ if (collection == null)
+ throw new ArgumentNullException("collection");
+
+ SynchronizedCollections.Remove(collection);
+ }
+
+ public static void EnableCollectionSynchronization(IEnumerable collection, object context, CollectionSynchronizationCallback callback)
+ {
+ if (collection == null)
+ throw new ArgumentNullException("collection");
+ if (callback == null)
+ throw new ArgumentNullException("callback");
+
+ SynchronizedCollections.Add(collection, new CollectionSynchronizationContext(context, callback));
+ }
+
+ protected void ThrowIfApplied()
+ {
+ if (IsApplied)
+ throw new InvalidOperationException("Can not change a binding while it's applied");
+ }
+
+ internal virtual void Apply(bool fromTarget)
+ {
+ IsApplied = true;
+ }
+
+ internal virtual void Apply(object context, BindableObject bindObj, BindableProperty targetProperty)
+ {
+ IsApplied = true;
+ }
+
+ internal abstract BindingBase Clone();
+
+ internal virtual object GetSourceValue(object value, Type targetPropertyType)
+ {
+ if (StringFormat != null)
+ return string.Format(StringFormat, value);
+
+ return value;
+ }
+
+ internal virtual object GetTargetValue(object value, Type sourcePropertyType)
+ {
+ return value;
+ }
+
+ internal static bool TryGetSynchronizedCollection(IEnumerable collection, out CollectionSynchronizationContext synchronizationContext)
+ {
+ if (collection == null)
+ throw new ArgumentNullException("collection");
+
+ return SynchronizedCollections.TryGetValue(collection, out synchronizationContext);
+ }
+
+ internal virtual void Unapply()
+ {
+ IsApplied = false;
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/BindingBaseExtensions.cs b/Xamarin.Forms.Core/BindingBaseExtensions.cs
new file mode 100644
index 00000000..a52c5cb1
--- /dev/null
+++ b/Xamarin.Forms.Core/BindingBaseExtensions.cs
@@ -0,0 +1,17 @@
+using System;
+
+namespace Xamarin.Forms
+{
+ internal static class BindingBaseExtensions
+ {
+ internal static BindingMode GetRealizedMode(this BindingBase self, BindableProperty property)
+ {
+ if (self == null)
+ throw new ArgumentNullException("self");
+ if (property == null)
+ throw new ArgumentNullException("property");
+
+ return self.Mode != BindingMode.Default ? self.Mode : property.DefaultBindingMode;
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/BindingExpression.cs b/Xamarin.Forms.Core/BindingExpression.cs
new file mode 100644
index 00000000..505fc584
--- /dev/null
+++ b/Xamarin.Forms.Core/BindingExpression.cs
@@ -0,0 +1,506 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Diagnostics;
+using System.Globalization;
+using System.Linq;
+using System.Reflection;
+
+namespace Xamarin.Forms
+{
+ internal class BindingExpression
+ {
+ internal const string PropertyNotFoundErrorMessage = "'{0}' property not found on '{1}', target property: '{2}.{3}'";
+
+ readonly List<BindingExpressionPart> _parts = new List<BindingExpressionPart>();
+
+ BindableProperty _targetProperty;
+ WeakReference<object> _weakSource;
+ WeakReference<BindableObject> _weakTarget;
+
+ internal BindingExpression(BindingBase binding, string path)
+ {
+ if (binding == null)
+ throw new ArgumentNullException("binding");
+ if (path == null)
+ throw new ArgumentNullException("path");
+
+ Binding = binding;
+ Path = path;
+
+ ParsePath();
+ }
+
+ internal BindingBase Binding { get; }
+
+ internal string Path { get; }
+
+ /// <summary>
+ /// Applies the binding expression to a previously set source and target.
+ /// </summary>
+ internal void Apply(bool fromTarget = false)
+ {
+ if (_weakSource == null || _weakTarget == null)
+ return;
+
+ BindableObject target;
+ if (!_weakTarget.TryGetTarget(out target))
+ {
+ Unapply();
+ return;
+ }
+
+ object source;
+ if (_weakSource.TryGetTarget(out source) && _targetProperty != null)
+ ApplyCore(source, target, _targetProperty, fromTarget);
+ }
+
+ /// <summary>
+ /// Applies the binding expression to a new source or target.
+ /// </summary>
+ internal void Apply(object sourceObject, BindableObject target, BindableProperty property)
+ {
+ _targetProperty = property;
+
+ BindableObject prevTarget;
+ if (_weakTarget != null && _weakTarget.TryGetTarget(out prevTarget) && !ReferenceEquals(prevTarget, target))
+ throw new InvalidOperationException("Binding instances can not be reused");
+
+ object previousSource;
+ if (_weakSource != null && _weakSource.TryGetTarget(out previousSource) && !ReferenceEquals(previousSource, sourceObject))
+ throw new InvalidOperationException("Binding instances can not be reused");
+
+ _weakSource = new WeakReference<object>(sourceObject);
+ _weakTarget = new WeakReference<BindableObject>(target);
+
+ ApplyCore(sourceObject, target, property);
+ }
+
+ internal void Unapply()
+ {
+ object sourceObject;
+ if (_weakSource != null && _weakSource.TryGetTarget(out sourceObject))
+ {
+ for (var i = 0; i < _parts.Count - 1; i++)
+ {
+ BindingExpressionPart part = _parts[i];
+
+ if (!part.IsSelf)
+ {
+ part.TryGetValue(sourceObject, out sourceObject);
+ }
+
+ var inpc = sourceObject as INotifyPropertyChanged;
+ if (inpc != null)
+ inpc.PropertyChanged -= part.ChangeHandler;
+ }
+ }
+
+ _weakSource = null;
+ _weakTarget = null;
+ }
+
+ /// <summary>
+ /// Applies the binding expression to a previously set source or target.
+ /// </summary>
+ void ApplyCore(object sourceObject, BindableObject target, BindableProperty property, bool fromTarget = false)
+ {
+ BindingMode mode = Binding.GetRealizedMode(_targetProperty);
+ if (mode == BindingMode.OneWay && fromTarget)
+ return;
+
+ bool needsGetter = (mode == BindingMode.TwoWay && !fromTarget) || mode == BindingMode.OneWay;
+ bool needsSetter = !needsGetter && ((mode == BindingMode.TwoWay && fromTarget) || mode == BindingMode.OneWayToSource);
+
+ object current = sourceObject;
+ object previous = null;
+ BindingExpressionPart part = null;
+
+ for (var i = 0; i < _parts.Count; i++)
+ {
+ part = _parts[i];
+ bool isLast = i + 1 == _parts.Count;
+
+ if (!part.IsSelf && current != null)
+ {
+ // Allow the object instance itself to provide its own TypeInfo
+ var reflectable = current as IReflectableType;
+ TypeInfo currentType = reflectable != null ? reflectable.GetTypeInfo() : current.GetType().GetTypeInfo();
+ if (part.LastGetter == null || !part.LastGetter.DeclaringType.GetTypeInfo().IsAssignableFrom(currentType))
+ SetupPart(currentType, part);
+
+ if (!isLast)
+ part.TryGetValue(current, out current);
+ }
+
+ if (!part.IsSelf && current != null)
+ {
+ if ((needsGetter && part.LastGetter == null) || (needsSetter && part.NextPart == null && part.LastSetter == null))
+ {
+ Log.Warning("Binding", PropertyNotFoundErrorMessage, part.Content, current, target.GetType(), property.PropertyName);
+ break;
+ }
+ }
+
+ if (mode == BindingMode.OneWay || mode == BindingMode.TwoWay)
+ {
+ var inpc = current as INotifyPropertyChanged;
+ if (inpc != null && !ReferenceEquals(current, previous))
+ {
+ // If we're reapplying, we don't want to double subscribe
+ inpc.PropertyChanged -= part.ChangeHandler;
+ inpc.PropertyChanged += part.ChangeHandler;
+ }
+ }
+
+ previous = current;
+ }
+
+ Debug.Assert(part != null, "There should always be at least the self part in the expression.");
+
+ if (needsGetter)
+ {
+ object value = property.DefaultValue;
+ if (part.TryGetValue(current, out value) || part.IsSelf)
+ {
+ value = Binding.GetSourceValue(value, property.ReturnType);
+ }
+ else
+ value = property.DefaultValue;
+
+ if (!TryConvert(part, ref value, property.ReturnType, true))
+ {
+ Log.Warning("Binding", "{0} can not be converted to type '{1}'", value, property.ReturnType);
+ return;
+ }
+
+ target.SetValueCore(property, value, BindableObject.SetValueFlags.ClearDynamicResource, BindableObject.SetValuePrivateFlags.Default | BindableObject.SetValuePrivateFlags.Converted);
+ }
+ else if (needsSetter && part.LastSetter != null && current != null)
+ {
+ object value = Binding.GetTargetValue(target.GetValue(property), part.SetterType);
+
+ if (!TryConvert(part, ref value, part.SetterType, false))
+ {
+ Log.Warning("Binding", "{0} can not be converted to type '{1}'", value, part.SetterType);
+ return;
+ }
+
+ object[] args;
+ if (part.IsIndexer)
+ {
+ args = new object[part.Arguments.Length + 1];
+ part.Arguments.CopyTo(args, 0);
+ args[args.Length - 1] = value;
+ }
+ else if (part.IsBindablePropertySetter)
+ {
+ args = new[] { part.BindablePropertyField, value };
+ }
+ else
+ {
+ args = new[] { value };
+ }
+
+ part.LastSetter.Invoke(current, args);
+ }
+ }
+
+ IEnumerable<BindingExpressionPart> GetPart(string part)
+ {
+ part = part.Trim();
+ if (part == string.Empty)
+ throw new FormatException("Path contains an empty part");
+
+ BindingExpressionPart indexer = null;
+
+ int lbIndex = part.IndexOf('[');
+ if (lbIndex != -1)
+ {
+ int rbIndex = part.LastIndexOf(']');
+ if (rbIndex == -1)
+ throw new FormatException("Indexer did not contain closing bracket");
+
+ int argLength = rbIndex - lbIndex - 1;
+ if (argLength == 0)
+ throw new FormatException("Indexer did not contain arguments");
+
+ string argString = part.Substring(lbIndex + 1, argLength);
+ indexer = new BindingExpressionPart(this, argString, true);
+
+ part = part.Substring(0, lbIndex);
+ part = part.Trim();
+ }
+
+ if (part.Length > 0)
+ yield return new BindingExpressionPart(this, part);
+ if (indexer != null)
+ yield return indexer;
+ }
+
+ void ParsePath()
+ {
+ string p = Path.Trim();
+
+ var last = new BindingExpressionPart(this, ".");
+ _parts.Add(last);
+
+ if (p[0] == '.')
+ {
+ if (p.Length == 1)
+ return;
+
+ p = p.Substring(1);
+ }
+
+ string[] pathParts = p.Split('.');
+ for (var i = 0; i < pathParts.Length; i++)
+ {
+ foreach (BindingExpressionPart part in GetPart(pathParts[i]))
+ {
+ last.NextPart = part;
+ _parts.Add(part);
+ last = part;
+ }
+ }
+ }
+
+ void SetupPart(TypeInfo sourceType, BindingExpressionPart part)
+ {
+ part.Arguments = null;
+ part.LastGetter = null;
+ part.LastSetter = null;
+
+ PropertyInfo property = null;
+ if (part.IsIndexer)
+ {
+ if (sourceType.IsArray)
+ {
+ int index;
+ if (!int.TryParse(part.Content, out index))
+ Log.Warning("Binding", "{0} could not be parsed as an index for a {1}", part.Content, sourceType);
+ else
+ part.Arguments = new object[] { index };
+
+ part.LastGetter = sourceType.GetDeclaredMethod("Get");
+ part.LastSetter = sourceType.GetDeclaredMethod("Set");
+ part.SetterType = sourceType.GetElementType();
+ }
+
+ DefaultMemberAttribute defaultMember = sourceType.GetCustomAttributes(typeof(DefaultMemberAttribute), true).OfType<DefaultMemberAttribute>().FirstOrDefault();
+ string indexerName = defaultMember != null ? defaultMember.MemberName : "Item";
+
+ part.IndexerName = indexerName;
+
+ property = sourceType.GetDeclaredProperty(indexerName);
+ if (property == null)
+ property = sourceType.BaseType.GetProperty(indexerName);
+
+ if (property != null)
+ {
+ ParameterInfo parameter = property.GetIndexParameters().FirstOrDefault();
+ if (parameter != null)
+ {
+ try
+ {
+ object arg = Convert.ChangeType(part.Content, parameter.ParameterType, CultureInfo.InvariantCulture);
+ part.Arguments = new[] { arg };
+ }
+ catch (FormatException)
+ {
+ }
+ catch (InvalidCastException)
+ {
+ }
+ catch (OverflowException)
+ {
+ }
+ }
+ }
+ }
+ else
+ {
+ property = sourceType.GetDeclaredProperty(part.Content);
+ if (property == null)
+ property = sourceType.BaseType.GetProperty(part.Content);
+ }
+
+ if (property != null)
+ {
+ if (property.CanRead && property.GetMethod.IsPublic && !property.GetMethod.IsStatic)
+ part.LastGetter = property.GetMethod;
+ if (property.CanWrite && property.SetMethod.IsPublic && !property.SetMethod.IsStatic)
+ {
+ part.LastSetter = property.SetMethod;
+ part.SetterType = part.LastSetter.GetParameters().Last().ParameterType;
+
+ if (Binding.AllowChaining)
+ {
+ FieldInfo bindablePropertyField = sourceType.GetDeclaredField(part.Content + "Property");
+ if (bindablePropertyField != null && bindablePropertyField.FieldType == typeof(BindableProperty) && sourceType.ImplementedInterfaces.Contains(typeof(IElementController)))
+ {
+ MethodInfo setValueMethod = null;
+ foreach (MethodInfo m in sourceType.AsType().GetRuntimeMethods())
+ {
+ if (m.Name.EndsWith("IElementController.SetValueFromRenderer"))
+ {
+ ParameterInfo[] parameters = m.GetParameters();
+ if (parameters.Length == 2 && parameters[0].ParameterType == typeof(BindableProperty))
+ {
+ setValueMethod = m;
+ break;
+ }
+ }
+ }
+ if (setValueMethod != null)
+ {
+ part.LastSetter = setValueMethod;
+ part.IsBindablePropertySetter = true;
+ part.BindablePropertyField = bindablePropertyField.GetValue(null);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ bool TryConvert(BindingExpressionPart part, ref object value, Type convertTo, bool toTarget)
+ {
+ if (value == null)
+ return true;
+ if ((toTarget && _targetProperty.TryConvert(ref value)) || (!toTarget && convertTo.IsInstanceOfType(value)))
+ return true;
+
+ object original = value;
+ try
+ {
+ value = Convert.ChangeType(value, convertTo, CultureInfo.InvariantCulture);
+ return true;
+ }
+ catch (InvalidCastException)
+ {
+ value = original;
+ return false;
+ }
+ catch (FormatException)
+ {
+ value = original;
+ return false;
+ }
+ catch (OverflowException)
+ {
+ value = original;
+ return false;
+ }
+ }
+
+ class BindingPair
+ {
+ public BindingPair(BindingExpressionPart part, object source, bool isLast)
+ {
+ Part = part;
+ Source = source;
+ IsLast = isLast;
+ }
+
+ public bool IsLast { get; set; }
+
+ public BindingExpressionPart Part { get; private set; }
+
+ public object Source { get; private set; }
+ }
+
+ class BindingExpressionPart
+ {
+ readonly BindingExpression _expression;
+
+ public readonly PropertyChangedEventHandler ChangeHandler;
+
+ public BindingExpressionPart(BindingExpression expression, string content, bool isIndexer = false)
+ {
+ _expression = expression;
+ IsSelf = content == Forms.Binding.SelfPath;
+ Content = content;
+ IsIndexer = isIndexer;
+
+ ChangeHandler = PropertyChanged;
+ }
+
+ public object[] Arguments { get; set; }
+
+ public object BindablePropertyField { get; set; }
+
+ public string Content { get; }
+
+ public string IndexerName { get; set; }
+
+ public bool IsBindablePropertySetter { get; set; }
+
+ public bool IsIndexer { get; }
+
+ public bool IsSelf { get; }
+
+ public MethodInfo LastGetter { get; set; }
+
+ public MethodInfo LastSetter { get; set; }
+
+ public BindingExpressionPart NextPart { get; set; }
+
+ public Type SetterType { get; set; }
+
+ public void PropertyChanged(object sender, PropertyChangedEventArgs args)
+ {
+ BindingExpressionPart part = NextPart ?? this;
+
+ string name = args.PropertyName;
+
+ if (!string.IsNullOrEmpty(name))
+ {
+ if (part.IsIndexer)
+ {
+ if (name.Contains("["))
+ {
+ if (name != string.Format("{0}[{1}]", part.IndexerName, part.Content))
+ return;
+ }
+ else if (name != part.IndexerName)
+ return;
+ }
+ else if (name != part.Content)
+ {
+ return;
+ }
+ }
+
+ Device.BeginInvokeOnMainThread(() => _expression.Apply());
+ }
+
+ public bool TryGetValue(object source, out object value)
+ {
+ value = source;
+
+ if (LastGetter != null && value != null)
+ {
+ if (IsIndexer)
+ {
+ try
+ {
+ value = LastGetter.Invoke(value, Arguments);
+ }
+ catch (TargetInvocationException ex)
+ {
+ if (!(ex.InnerException is KeyNotFoundException))
+ throw;
+ value = null;
+ }
+ return true;
+ }
+ value = LastGetter.Invoke(value, Arguments);
+ return true;
+ }
+
+ return false;
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/BindingMode.cs b/Xamarin.Forms.Core/BindingMode.cs
new file mode 100644
index 00000000..89396acb
--- /dev/null
+++ b/Xamarin.Forms.Core/BindingMode.cs
@@ -0,0 +1,10 @@
+namespace Xamarin.Forms
+{
+ public enum BindingMode
+ {
+ Default,
+ TwoWay,
+ OneWay,
+ OneWayToSource
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/BindingTypeConverter.cs b/Xamarin.Forms.Core/BindingTypeConverter.cs
new file mode 100644
index 00000000..07dda896
--- /dev/null
+++ b/Xamarin.Forms.Core/BindingTypeConverter.cs
@@ -0,0 +1,10 @@
+namespace Xamarin.Forms
+{
+ public sealed class BindingTypeConverter : TypeConverter
+ {
+ public override object ConvertFromInvariantString(string value)
+ {
+ return new Binding(value);
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/BoundsConstraint.cs b/Xamarin.Forms.Core/BoundsConstraint.cs
new file mode 100644
index 00000000..43be86eb
--- /dev/null
+++ b/Xamarin.Forms.Core/BoundsConstraint.cs
@@ -0,0 +1,34 @@
+using System;
+using System.Collections.Generic;
+using System.Linq.Expressions;
+
+namespace Xamarin.Forms
+{
+ public class BoundsConstraint
+ {
+ Func<Rectangle> _measureFunc;
+
+ BoundsConstraint()
+ {
+ }
+
+ internal IEnumerable<View> RelativeTo { get; set; }
+
+ public static BoundsConstraint FromExpression(Expression<Func<Rectangle>> expression, IEnumerable<View> parents = null)
+ {
+ Func<Rectangle> compiled = expression.Compile();
+ var result = new BoundsConstraint
+ {
+ _measureFunc = compiled,
+ RelativeTo = parents ?? ExpressionSearch.Default.FindObjects<View>(expression).ToArray() // make sure we have our own copy
+ };
+
+ return result;
+ }
+
+ internal Rectangle Compute()
+ {
+ return _measureFunc();
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/BoundsTypeConverter.cs b/Xamarin.Forms.Core/BoundsTypeConverter.cs
new file mode 100644
index 00000000..549b7b63
--- /dev/null
+++ b/Xamarin.Forms.Core/BoundsTypeConverter.cs
@@ -0,0 +1,42 @@
+using System;
+using System.Globalization;
+
+namespace Xamarin.Forms
+{
+ public class BoundsTypeConverter : TypeConverter
+ {
+ public override object ConvertFromInvariantString(string value)
+ {
+ if (value != null)
+ {
+ double x = -1, y = -1, w = -1, h = -1;
+ string[] xywh = value.Split(',');
+ bool hasX, hasY, hasW, hasH;
+
+ hasX = (xywh.Length == 2 || xywh.Length == 4) && double.TryParse(xywh[0], NumberStyles.Number, CultureInfo.InvariantCulture, out x);
+ hasY = (xywh.Length == 2 || xywh.Length == 4) && double.TryParse(xywh[1], NumberStyles.Number, CultureInfo.InvariantCulture, out y);
+ hasW = xywh.Length == 4 && double.TryParse(xywh[2], NumberStyles.Number, CultureInfo.InvariantCulture, out w);
+ hasH = xywh.Length == 4 && double.TryParse(xywh[3], NumberStyles.Number, CultureInfo.InvariantCulture, out h);
+
+ if (!hasW && xywh.Length == 4 && string.Compare("AutoSize", xywh[2].Trim(), StringComparison.OrdinalIgnoreCase) == 0)
+ {
+ hasW = true;
+ w = AbsoluteLayout.AutoSize;
+ }
+
+ if (!hasH && xywh.Length == 4 && string.Compare("AutoSize", xywh[3].Trim(), StringComparison.OrdinalIgnoreCase) == 0)
+ {
+ hasH = true;
+ h = AbsoluteLayout.AutoSize;
+ }
+
+ if (hasX && hasY && xywh.Length == 2)
+ return new Rectangle(x, y, AbsoluteLayout.AutoSize, AbsoluteLayout.AutoSize);
+ if (hasX && hasY && hasW && hasH && xywh.Length == 4)
+ return new Rectangle(x, y, w, h);
+ }
+
+ throw new InvalidOperationException(string.Format("Cannot convert \"{0}\" into {1}", value, typeof(Rectangle)));
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/BoxView.cs b/Xamarin.Forms.Core/BoxView.cs
new file mode 100644
index 00000000..79fef390
--- /dev/null
+++ b/Xamarin.Forms.Core/BoxView.cs
@@ -0,0 +1,23 @@
+using System;
+using Xamarin.Forms.Platform;
+
+namespace Xamarin.Forms
+{
+ [RenderWith(typeof(_BoxViewRenderer))]
+ public class BoxView : View
+ {
+ public static readonly BindableProperty ColorProperty = BindableProperty.Create("Color", typeof(Color), typeof(BoxView), Color.Default);
+
+ public Color Color
+ {
+ get { return (Color)GetValue(ColorProperty); }
+ set { SetValue(ColorProperty, value); }
+ }
+
+ [Obsolete("Use OnMeasure")]
+ protected override SizeRequest OnSizeRequest(double widthConstraint, double heightConstraint)
+ {
+ return new SizeRequest(new Size(40, 40));
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/Button.cs b/Xamarin.Forms.Core/Button.cs
new file mode 100644
index 00000000..4662105f
--- /dev/null
+++ b/Xamarin.Forms.Core/Button.cs
@@ -0,0 +1,251 @@
+using System;
+using System.Windows.Input;
+using Xamarin.Forms.Platform;
+
+namespace Xamarin.Forms
+{
+ [RenderWith(typeof(_ButtonRenderer))]
+ public class Button : View, IFontElement, IButtonController
+ {
+ public static readonly BindableProperty CommandProperty = BindableProperty.Create("Command", typeof(ICommand), typeof(Button), null, propertyChanged: (bo, o, n) => ((Button)bo).OnCommandChanged());
+
+ public static readonly BindableProperty CommandParameterProperty = BindableProperty.Create("CommandParameter", typeof(object), typeof(Button), null,
+ propertyChanged: (bindable, oldvalue, newvalue) => ((Button)bindable).CommandCanExecuteChanged(bindable, EventArgs.Empty));
+
+ public static readonly BindableProperty TextProperty = BindableProperty.Create("Text", typeof(string), typeof(Button), null,
+ propertyChanged: (bindable, oldVal, newVal) => ((Button)bindable).InvalidateMeasure(InvalidationTrigger.MeasureChanged));
+
+ public static readonly BindableProperty TextColorProperty = BindableProperty.Create("TextColor", typeof(Color), typeof(Button), Color.Default);
+
+ public static readonly BindableProperty FontProperty = BindableProperty.Create("Font", typeof(Font), typeof(Button), default(Font), propertyChanged: FontStructPropertyChanged);
+
+ public static readonly BindableProperty FontFamilyProperty = BindableProperty.Create("FontFamily", typeof(string), typeof(Button), default(string), propertyChanged: SpecificFontPropertyChanged);
+
+ public static readonly BindableProperty FontSizeProperty = BindableProperty.Create("FontSize", typeof(double), typeof(Button), -1.0, propertyChanged: SpecificFontPropertyChanged,
+ defaultValueCreator: bindable => Device.GetNamedSize(NamedSize.Default, (Button)bindable));
+
+ public static readonly BindableProperty FontAttributesProperty = BindableProperty.Create("FontAttributes", typeof(FontAttributes), typeof(Button), FontAttributes.None,
+ propertyChanged: SpecificFontPropertyChanged);
+
+ public static readonly BindableProperty BorderWidthProperty = BindableProperty.Create("BorderWidth", typeof(double), typeof(Button), 0d);
+
+ public static readonly BindableProperty BorderColorProperty = BindableProperty.Create("BorderColor", typeof(Color), typeof(Button), Color.Default);
+
+ public static readonly BindableProperty BorderRadiusProperty = BindableProperty.Create("BorderRadius", typeof(int), typeof(Button), 5);
+
+ public static readonly BindableProperty ImageProperty = BindableProperty.Create("Image", typeof(FileImageSource), typeof(Button), default(FileImageSource),
+ propertyChanging: (bindable, oldvalue, newvalue) => ((Button)bindable).OnSourcePropertyChanging((ImageSource)oldvalue, (ImageSource)newvalue),
+ propertyChanged: (bindable, oldvalue, newvalue) => ((Button)bindable).OnSourcePropertyChanged((ImageSource)oldvalue, (ImageSource)newvalue));
+
+ bool _cancelEvents;
+
+ public Color BorderColor
+ {
+ get { return (Color)GetValue(BorderColorProperty); }
+ set { SetValue(BorderColorProperty, value); }
+ }
+
+ public int BorderRadius
+ {
+ get { return (int)GetValue(BorderRadiusProperty); }
+ set { SetValue(BorderRadiusProperty, value); }
+ }
+
+ public double BorderWidth
+ {
+ get { return (double)GetValue(BorderWidthProperty); }
+ set { SetValue(BorderWidthProperty, value); }
+ }
+
+ public ICommand Command
+ {
+ get { return (ICommand)GetValue(CommandProperty); }
+ set { SetValue(CommandProperty, value); }
+ }
+
+ public object CommandParameter
+ {
+ get { return GetValue(CommandParameterProperty); }
+ set { SetValue(CommandParameterProperty, value); }
+ }
+
+ public Font Font
+ {
+ get { return (Font)GetValue(FontProperty); }
+ set { SetValue(FontProperty, value); }
+ }
+
+ public FileImageSource Image
+ {
+ get { return (FileImageSource)GetValue(ImageProperty); }
+ set { SetValue(ImageProperty, value); }
+ }
+
+ public string Text
+ {
+ get { return (string)GetValue(TextProperty); }
+ set { SetValue(TextProperty, value); }
+ }
+
+ public Color TextColor
+ {
+ get { return (Color)GetValue(TextColorProperty); }
+ set { SetValue(TextColorProperty, value); }
+ }
+
+ bool IsEnabledCore
+ {
+ set { SetValueCore(IsEnabledProperty, value); }
+ }
+
+ void IButtonController.SendClicked()
+ {
+ ICommand cmd = Command;
+ if (cmd != null)
+ cmd.Execute(CommandParameter);
+
+ EventHandler handler = Clicked;
+ if (handler != null)
+ handler(this, EventArgs.Empty);
+ }
+
+ public FontAttributes FontAttributes
+ {
+ get { return (FontAttributes)GetValue(FontAttributesProperty); }
+ set { SetValue(FontAttributesProperty, value); }
+ }
+
+ public string FontFamily
+ {
+ get { return (string)GetValue(FontFamilyProperty); }
+ set { SetValue(FontFamilyProperty, value); }
+ }
+
+ [TypeConverter(typeof(FontSizeConverter))]
+ public double FontSize
+ {
+ get { return (double)GetValue(FontSizeProperty); }
+ set { SetValue(FontSizeProperty, value); }
+ }
+
+ public event EventHandler Clicked;
+
+ protected override void OnBindingContextChanged()
+ {
+ FileImageSource image = Image;
+ if (image != null)
+ SetInheritedBindingContext(image, BindingContext);
+
+ base.OnBindingContextChanged();
+ }
+
+ protected override void OnPropertyChanging(string propertyName = null)
+ {
+ if (propertyName == CommandProperty.PropertyName)
+ {
+ ICommand cmd = Command;
+ if (cmd != null)
+ cmd.CanExecuteChanged -= CommandCanExecuteChanged;
+ }
+
+ base.OnPropertyChanging(propertyName);
+ }
+
+ void CommandCanExecuteChanged(object sender, EventArgs eventArgs)
+ {
+ ICommand cmd = Command;
+ if (cmd != null)
+ IsEnabledCore = cmd.CanExecute(CommandParameter);
+ }
+
+ static void FontStructPropertyChanged(BindableObject bindable, object oldValue, object newValue)
+ {
+ var button = (Button)bindable;
+
+ if (button._cancelEvents)
+ return;
+
+ button.InvalidateMeasure(InvalidationTrigger.MeasureChanged);
+
+ button._cancelEvents = true;
+
+ if (button.Font == Font.Default)
+ {
+ button.FontFamily = null;
+ button.FontSize = Device.GetNamedSize(NamedSize.Default, button);
+ button.FontAttributes = FontAttributes.None;
+ }
+ else
+ {
+ button.FontFamily = button.Font.FontFamily;
+ if (button.Font.UseNamedSize)
+ {
+ button.FontSize = Device.GetNamedSize(button.Font.NamedSize, button.GetType(), true);
+ }
+ else
+ {
+ button.FontSize = button.Font.FontSize;
+ }
+ button.FontAttributes = button.Font.FontAttributes;
+ }
+
+ button._cancelEvents = false;
+ }
+
+ void OnCommandChanged()
+ {
+ if (Command != null)
+ {
+ Command.CanExecuteChanged += CommandCanExecuteChanged;
+ CommandCanExecuteChanged(this, EventArgs.Empty);
+ }
+ else
+ IsEnabledCore = true;
+ }
+
+ void OnSourceChanged(object sender, EventArgs eventArgs)
+ {
+ OnPropertyChanged(ImageProperty.PropertyName);
+ InvalidateMeasure(InvalidationTrigger.MeasureChanged);
+ }
+
+ void OnSourcePropertyChanged(ImageSource oldvalue, ImageSource newvalue)
+ {
+ if (newvalue != null)
+ {
+ newvalue.SourceChanged += OnSourceChanged;
+ SetInheritedBindingContext(newvalue, BindingContext);
+ }
+ InvalidateMeasure(InvalidationTrigger.MeasureChanged);
+ }
+
+ void OnSourcePropertyChanging(ImageSource oldvalue, ImageSource newvalue)
+ {
+ if (oldvalue != null)
+ oldvalue.SourceChanged -= OnSourceChanged;
+ }
+
+ static void SpecificFontPropertyChanged(BindableObject bindable, object oldValue, object newValue)
+ {
+ var button = (Button)bindable;
+
+ if (button._cancelEvents)
+ return;
+
+ button.InvalidateMeasure(InvalidationTrigger.MeasureChanged);
+
+ button._cancelEvents = true;
+
+ if (button.FontFamily != null)
+ {
+ button.Font = Font.OfSize(button.FontFamily, button.FontSize).WithAttributes(button.FontAttributes);
+ }
+ else
+ {
+ button.Font = Font.SystemFontOfSize(button.FontSize, button.FontAttributes);
+ }
+
+ button._cancelEvents = false;
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/CarouselPage.cs b/Xamarin.Forms.Core/CarouselPage.cs
new file mode 100644
index 00000000..a6f769e5
--- /dev/null
+++ b/Xamarin.Forms.Core/CarouselPage.cs
@@ -0,0 +1,17 @@
+using Xamarin.Forms.Platform;
+
+namespace Xamarin.Forms
+{
+ [RenderWith(typeof(_CarouselPageRenderer))]
+ public class CarouselPage : MultiPage<ContentPage>
+ {
+ protected override ContentPage CreateDefault(object item)
+ {
+ var page = new ContentPage();
+ if (item != null)
+ page.Title = item.ToString();
+
+ return page;
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/CarouselView.cs b/Xamarin.Forms.Core/CarouselView.cs
new file mode 100644
index 00000000..62e9393e
--- /dev/null
+++ b/Xamarin.Forms.Core/CarouselView.cs
@@ -0,0 +1,86 @@
+using System;
+using Xamarin.Forms.Platform;
+
+namespace Xamarin.Forms
+{
+ [RenderWith(typeof(_CarouselViewRenderer))]
+ public class CarouselView : ItemsView, ICarouselViewController
+ {
+ public static readonly BindableProperty PositionProperty = BindableProperty.Create(nameof(Position), typeof(int), typeof(CarouselView), 0, BindingMode.TwoWay);
+
+ public static readonly BindableProperty ItemProperty = BindableProperty.Create(nameof(Item), typeof(object), typeof(CarouselView), 0, BindingMode.TwoWay);
+
+ object _lastItem;
+
+ int _lastPosition;
+
+ public CarouselView()
+ {
+ _lastPosition = 0;
+ _lastItem = null;
+ VerticalOptions = LayoutOptions.FillAndExpand;
+ HorizontalOptions = LayoutOptions.FillAndExpand;
+ }
+
+ public int Item
+ {
+ get { return (int)GetValue(ItemProperty); }
+ }
+
+ public int Position
+ {
+ get { return (int)GetValue(PositionProperty); }
+ set { SetValue(PositionProperty, value); }
+ }
+
+ void ICarouselViewController.SendPositionAppearing(int position)
+ {
+ ItemAppearing?.Invoke(this, new ItemVisibilityEventArgs(GetItem(position)));
+ }
+
+ void ICarouselViewController.SendPositionDisappearing(int position)
+ {
+ ItemDisappearing?.Invoke(this, new ItemVisibilityEventArgs(GetItem(position)));
+ }
+
+ void ICarouselViewController.SendSelectedItemChanged(object item)
+ {
+ if (item.Equals(_lastItem))
+ return;
+
+ ItemSelected?.Invoke(this, new SelectedItemChangedEventArgs(item));
+ _lastItem = item;
+ }
+
+ void ICarouselViewController.SendSelectedPositionChanged(int position)
+ {
+ if (_lastPosition == position)
+ return;
+
+ _lastPosition = position;
+ PositionSelected?.Invoke(this, new SelectedPositionChangedEventArgs(position));
+ }
+
+ public event EventHandler<SelectedItemChangedEventArgs> ItemSelected;
+
+ public event EventHandler<SelectedPositionChangedEventArgs> PositionSelected;
+
+ protected override SizeRequest OnMeasure(double widthConstraint, double heightConstraint)
+ {
+ var minimumSize = new Size(40, 40);
+ return new SizeRequest(minimumSize, minimumSize);
+ }
+
+ // non-public bc unable to implement on iOS
+ internal event EventHandler<ItemVisibilityEventArgs> ItemAppearing;
+
+ internal event EventHandler<ItemVisibilityEventArgs> ItemDisappearing;
+
+ object GetItem(int position)
+ {
+ var controller = (IItemViewController)this;
+ object item = controller.GetItem(position);
+ return item;
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/CastingEnumerator.cs b/Xamarin.Forms.Core/CastingEnumerator.cs
new file mode 100644
index 00000000..62a823b1
--- /dev/null
+++ b/Xamarin.Forms.Core/CastingEnumerator.cs
@@ -0,0 +1,46 @@
+using System.Collections;
+using System.Collections.Generic;
+
+namespace Xamarin.Forms
+{
+ internal class CastingEnumerator<T, TFrom> : IEnumerator<T> where T : class where TFrom : class
+ {
+ readonly IEnumerator<TFrom> _enumerator;
+
+ bool _disposed;
+
+ public CastingEnumerator(IEnumerator<TFrom> enumerator)
+ {
+ _enumerator = enumerator;
+ }
+
+ public void Dispose()
+ {
+ if (_disposed)
+ return;
+ _disposed = true;
+
+ _enumerator.Dispose();
+ }
+
+ object IEnumerator.Current
+ {
+ get { return Current; }
+ }
+
+ public bool MoveNext()
+ {
+ return _enumerator.MoveNext();
+ }
+
+ public void Reset()
+ {
+ _enumerator.Reset();
+ }
+
+ public T Current
+ {
+ get { return _enumerator.Current as T; }
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/Cells/Cell.cs b/Xamarin.Forms.Core/Cells/Cell.cs
new file mode 100644
index 00000000..3b16d06a
--- /dev/null
+++ b/Xamarin.Forms.Core/Cells/Cell.cs
@@ -0,0 +1,209 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Collections.Specialized;
+using System.ComponentModel;
+using System.Threading.Tasks;
+
+namespace Xamarin.Forms
+{
+ public abstract class Cell : Element
+ {
+ public static readonly BindableProperty IsEnabledProperty = BindableProperty.Create("IsEnabled", typeof(bool), typeof(Cell), true, propertyChanged: OnIsEnabledPropertyChanged);
+
+ ObservableCollection<MenuItem> _contextActions;
+
+ double _height = -1;
+
+ bool _nextCallToForceUpdateSizeQueued;
+
+ public IList<MenuItem> ContextActions
+ {
+ get
+ {
+ if (_contextActions == null)
+ {
+ _contextActions = new ObservableCollection<MenuItem>();
+ _contextActions.CollectionChanged += OnContextActionsChanged;
+ }
+
+ return _contextActions;
+ }
+ }
+
+ public bool HasContextActions
+ {
+ get { return _contextActions != null && _contextActions.Count > 0 && IsEnabled; }
+ }
+
+ public double Height
+ {
+ get { return _height; }
+ set
+ {
+ if (_height == value)
+ return;
+
+ OnPropertyChanging("Height");
+ OnPropertyChanging("RenderHeight");
+ _height = value;
+ OnPropertyChanged("Height");
+ OnPropertyChanged("RenderHeight");
+ }
+ }
+
+ public bool IsEnabled
+ {
+ get { return (bool)GetValue(IsEnabledProperty); }
+ set { SetValue(IsEnabledProperty, value); }
+ }
+
+ public double RenderHeight
+ {
+ get
+ {
+ var table = RealParent as TableView;
+ if (table != null)
+ return table.HasUnevenRows && Height > 0 ? Height : table.RowHeight;
+
+ var list = RealParent as ListView;
+ if (list != null)
+ return list.HasUnevenRows && Height > 0 ? Height : list.RowHeight;
+
+ return 40;
+ }
+ }
+
+ public event EventHandler Appearing;
+
+ public event EventHandler Disappearing;
+
+ public void ForceUpdateSize()
+ {
+ if (_nextCallToForceUpdateSizeQueued)
+ return;
+
+ if ((Parent as ListView)?.HasUnevenRows == true)
+ {
+ _nextCallToForceUpdateSizeQueued = true;
+ OnForceUpdateSizeRequested();
+ }
+ }
+
+ public event EventHandler Tapped;
+
+ protected internal virtual void OnTapped()
+ {
+ if (Tapped != null)
+ Tapped(this, EventArgs.Empty);
+ }
+
+ protected virtual void OnAppearing()
+ {
+ EventHandler handler = Appearing;
+ if (handler != null)
+ handler(this, EventArgs.Empty);
+ }
+
+ protected override void OnBindingContextChanged()
+ {
+ base.OnBindingContextChanged();
+
+ if (HasContextActions)
+ {
+ for (var i = 0; i < _contextActions.Count; i++)
+ SetInheritedBindingContext(_contextActions[i], BindingContext);
+ }
+ }
+
+ protected virtual void OnDisappearing()
+ {
+ EventHandler handler = Disappearing;
+ if (handler != null)
+ handler(this, EventArgs.Empty);
+ }
+
+ protected override void OnParentSet()
+ {
+ if (RealParent != null)
+ {
+ RealParent.PropertyChanged += OnParentPropertyChanged;
+ RealParent.PropertyChanging += OnParentPropertyChanging;
+ }
+
+ base.OnParentSet();
+ }
+
+ protected override void OnPropertyChanging(string propertyName = null)
+ {
+ if (propertyName == "Parent")
+ {
+ if (RealParent != null)
+ {
+ RealParent.PropertyChanged -= OnParentPropertyChanged;
+ RealParent.PropertyChanging -= OnParentPropertyChanging;
+ }
+ }
+
+ base.OnPropertyChanging(propertyName);
+ }
+
+ internal event EventHandler ForceUpdateSizeRequested;
+
+ internal void SendAppearing()
+ {
+ OnAppearing();
+
+ var container = RealParent as IListViewController;
+ if (container != null)
+ container.SendCellAppearing(this);
+ }
+
+ internal void SendDisappearing()
+ {
+ OnDisappearing();
+
+ var container = RealParent as IListViewController;
+ if (container != null)
+ container.SendCellDisappearing(this);
+ }
+
+ void OnContextActionsChanged(object sender, NotifyCollectionChangedEventArgs e)
+ {
+ for (var i = 0; i < _contextActions.Count; i++)
+ SetInheritedBindingContext(_contextActions[i], BindingContext);
+
+ OnPropertyChanged("HasContextActions");
+ }
+
+ async void OnForceUpdateSizeRequested()
+ {
+ // don't run more than once per 16 milliseconds
+ await Task.Delay(TimeSpan.FromMilliseconds(16));
+ EventHandler handler = ForceUpdateSizeRequested;
+ if (handler != null)
+ handler(this, null);
+
+ _nextCallToForceUpdateSizeQueued = false;
+ }
+
+ static void OnIsEnabledPropertyChanged(BindableObject bindable, object oldvalue, object newvalue)
+ {
+ (bindable as Cell).OnPropertyChanged("HasContextActions");
+ }
+
+ void OnParentPropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ // Technically we might be raising this even if it didn't change, but I'm taking the bet that
+ // its uncommon enough that we don't want to take the penalty of N GetValue calls to verify.
+ if (e.PropertyName == "RowHeight")
+ OnPropertyChanged("RenderHeight");
+ }
+
+ void OnParentPropertyChanging(object sender, PropertyChangingEventArgs e)
+ {
+ if (e.PropertyName == "RowHeight")
+ OnPropertyChanging("RenderHeight");
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/Cells/EntryCell.cs b/Xamarin.Forms.Core/Cells/EntryCell.cs
new file mode 100644
index 00000000..d74e365e
--- /dev/null
+++ b/Xamarin.Forms.Core/Cells/EntryCell.cs
@@ -0,0 +1,80 @@
+using System;
+
+namespace Xamarin.Forms
+{
+ public class EntryCell : Cell
+ {
+ public static readonly BindableProperty TextProperty = BindableProperty.Create("Text", typeof(string), typeof(EntryCell), null, BindingMode.TwoWay);
+
+ public static readonly BindableProperty LabelProperty = BindableProperty.Create("Label", typeof(string), typeof(EntryCell), null);
+
+ public static readonly BindableProperty PlaceholderProperty = BindableProperty.Create("Placeholder", typeof(string), typeof(EntryCell), null);
+
+ public static readonly BindableProperty LabelColorProperty = BindableProperty.Create("LabelColor", typeof(Color), typeof(EntryCell), Color.Default);
+
+ public static readonly BindableProperty KeyboardProperty = BindableProperty.Create("Keyboard", typeof(Keyboard), typeof(EntryCell), Keyboard.Default);
+
+ public static readonly BindableProperty HorizontalTextAlignmentProperty = BindableProperty.Create("HorizontalTextAlignment", typeof(TextAlignment), typeof(EntryCell), TextAlignment.Start,
+ propertyChanged: OnHorizontalTextAlignmentPropertyChanged);
+
+ [Obsolete("XAlignProperty is obsolete. Please use HorizontalTextAlignmentProperty instead.")] public static readonly BindableProperty XAlignProperty = HorizontalTextAlignmentProperty;
+
+ public TextAlignment HorizontalTextAlignment
+ {
+ get { return (TextAlignment)GetValue(HorizontalTextAlignmentProperty); }
+ set { SetValue(HorizontalTextAlignmentProperty, value); }
+ }
+
+ public Keyboard Keyboard
+ {
+ get { return (Keyboard)GetValue(KeyboardProperty); }
+ set { SetValue(KeyboardProperty, value); }
+ }
+
+ public string Label
+ {
+ get { return (string)GetValue(LabelProperty); }
+ set { SetValue(LabelProperty, value); }
+ }
+
+ public Color LabelColor
+ {
+ get { return (Color)GetValue(LabelColorProperty); }
+ set { SetValue(LabelColorProperty, value); }
+ }
+
+ public string Placeholder
+ {
+ get { return (string)GetValue(PlaceholderProperty); }
+ set { SetValue(PlaceholderProperty, value); }
+ }
+
+ public string Text
+ {
+ get { return (string)GetValue(TextProperty); }
+ set { SetValue(TextProperty, value); }
+ }
+
+ [Obsolete("XAlign is obsolete. Please use HorizontalTextAlignment instead.")]
+ public TextAlignment XAlign
+ {
+ get { return (TextAlignment)GetValue(XAlignProperty); }
+ set { SetValue(XAlignProperty, value); }
+ }
+
+ public event EventHandler Completed;
+
+ internal void SendCompleted()
+ {
+ EventHandler handler = Completed;
+ if (handler != null)
+ handler(this, EventArgs.Empty);
+ }
+
+ static void OnHorizontalTextAlignmentPropertyChanged(BindableObject bindable, object oldValue, object newValue)
+ {
+ var label = (EntryCell)bindable;
+ label.OnPropertyChanged(nameof(XAlign));
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/Cells/INativeElementView.cs b/Xamarin.Forms.Core/Cells/INativeElementView.cs
new file mode 100644
index 00000000..2d015282
--- /dev/null
+++ b/Xamarin.Forms.Core/Cells/INativeElementView.cs
@@ -0,0 +1,7 @@
+namespace Xamarin.Forms
+{
+ public interface INativeElementView
+ {
+ Element Element { get; }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/Cells/ImageCell.cs b/Xamarin.Forms.Core/Cells/ImageCell.cs
new file mode 100644
index 00000000..6d5ba714
--- /dev/null
+++ b/Xamarin.Forms.Core/Cells/ImageCell.cs
@@ -0,0 +1,56 @@
+using System;
+
+namespace Xamarin.Forms
+{
+ public class ImageCell : TextCell
+ {
+ public static readonly BindableProperty ImageSourceProperty = BindableProperty.Create("ImageSource", typeof(ImageSource), typeof(ImageCell), null,
+ propertyChanging: (bindable, oldvalue, newvalue) => ((ImageCell)bindable).OnSourcePropertyChanging((ImageSource)oldvalue, (ImageSource)newvalue),
+ propertyChanged: (bindable, oldvalue, newvalue) => ((ImageCell)bindable).OnSourcePropertyChanged((ImageSource)oldvalue, (ImageSource)newvalue));
+
+ public ImageCell()
+ {
+ Disappearing += (sender, e) =>
+ {
+ if (ImageSource == null)
+ return;
+ ImageSource.Cancel();
+ };
+ }
+
+ [TypeConverter(typeof(ImageSourceConverter))]
+ public ImageSource ImageSource
+ {
+ get { return (ImageSource)GetValue(ImageSourceProperty); }
+ set { SetValue(ImageSourceProperty, value); }
+ }
+
+ protected override void OnBindingContextChanged()
+ {
+ if (ImageSource != null)
+ SetInheritedBindingContext(ImageSource, BindingContext);
+
+ base.OnBindingContextChanged();
+ }
+
+ void OnSourceChanged(object sender, EventArgs eventArgs)
+ {
+ OnPropertyChanged(ImageSourceProperty.PropertyName);
+ }
+
+ void OnSourcePropertyChanged(ImageSource oldvalue, ImageSource newvalue)
+ {
+ if (newvalue != null)
+ {
+ newvalue.SourceChanged += OnSourceChanged;
+ SetInheritedBindingContext(newvalue, BindingContext);
+ }
+ }
+
+ void OnSourcePropertyChanging(ImageSource oldvalue, ImageSource newvalue)
+ {
+ if (oldvalue != null)
+ oldvalue.SourceChanged -= OnSourceChanged;
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/Cells/SwitchCell.cs b/Xamarin.Forms.Core/Cells/SwitchCell.cs
new file mode 100644
index 00000000..adab7f45
--- /dev/null
+++ b/Xamarin.Forms.Core/Cells/SwitchCell.cs
@@ -0,0 +1,31 @@
+using System;
+
+namespace Xamarin.Forms
+{
+ public class SwitchCell : Cell
+ {
+ public static readonly BindableProperty OnProperty = BindableProperty.Create("On", typeof(bool), typeof(SwitchCell), false, propertyChanged: (obj, oldValue, newValue) =>
+ {
+ var switchCell = (SwitchCell)obj;
+ EventHandler<ToggledEventArgs> handler = switchCell.OnChanged;
+ if (handler != null)
+ handler(obj, new ToggledEventArgs((bool)newValue));
+ }, defaultBindingMode: BindingMode.TwoWay);
+
+ public static readonly BindableProperty TextProperty = BindableProperty.Create("Text", typeof(string), typeof(SwitchCell), default(string));
+
+ public bool On
+ {
+ get { return (bool)GetValue(OnProperty); }
+ set { SetValue(OnProperty, value); }
+ }
+
+ public string Text
+ {
+ get { return (string)GetValue(TextProperty); }
+ set { SetValue(TextProperty, value); }
+ }
+
+ public event EventHandler<ToggledEventArgs> OnChanged;
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/Cells/TextCell.cs b/Xamarin.Forms.Core/Cells/TextCell.cs
new file mode 100644
index 00000000..01da6447
--- /dev/null
+++ b/Xamarin.Forms.Core/Cells/TextCell.cs
@@ -0,0 +1,94 @@
+using System;
+using System.Windows.Input;
+
+namespace Xamarin.Forms
+{
+ public class TextCell : Cell
+ {
+ public static readonly BindableProperty CommandProperty = BindableProperty.Create("Command", typeof(ICommand), typeof(TextCell), default(ICommand),
+ propertyChanging: (bindable, oldvalue, newvalue) =>
+ {
+ var textCell = (TextCell)bindable;
+ var oldcommand = (ICommand)oldvalue;
+ if (oldcommand != null)
+ oldcommand.CanExecuteChanged -= textCell.OnCommandCanExecuteChanged;
+ }, propertyChanged: (bindable, oldvalue, newvalue) =>
+ {
+ var textCell = (TextCell)bindable;
+ var newcommand = (ICommand)newvalue;
+ if (newcommand != null)
+ {
+ textCell.IsEnabled = newcommand.CanExecute(textCell.CommandParameter);
+ newcommand.CanExecuteChanged += textCell.OnCommandCanExecuteChanged;
+ }
+ });
+
+ public static readonly BindableProperty CommandParameterProperty = BindableProperty.Create("CommandParameter", typeof(object), typeof(TextCell), default(object),
+ propertyChanged: (bindable, oldvalue, newvalue) =>
+ {
+ var textCell = (TextCell)bindable;
+ if (textCell.Command != null)
+ {
+ textCell.IsEnabled = textCell.Command.CanExecute(newvalue);
+ }
+ });
+
+ public static readonly BindableProperty TextProperty = BindableProperty.Create("Text", typeof(string), typeof(TextCell), default(string));
+
+ public static readonly BindableProperty DetailProperty = BindableProperty.Create("Detail", typeof(string), typeof(TextCell), default(string));
+
+ public static readonly BindableProperty TextColorProperty = BindableProperty.Create("TextColor", typeof(Color), typeof(TextCell), Color.Default);
+
+ public static readonly BindableProperty DetailColorProperty = BindableProperty.Create("DetailColor", typeof(Color), typeof(TextCell), Color.Default);
+
+ public ICommand Command
+ {
+ get { return (ICommand)GetValue(CommandProperty); }
+ set { SetValue(CommandProperty, value); }
+ }
+
+ public object CommandParameter
+ {
+ get { return GetValue(CommandParameterProperty); }
+ set { SetValue(CommandParameterProperty, value); }
+ }
+
+ public string Detail
+ {
+ get { return (string)GetValue(DetailProperty); }
+ set { SetValue(DetailProperty, value); }
+ }
+
+ public Color DetailColor
+ {
+ get { return (Color)GetValue(DetailColorProperty); }
+ set { SetValue(DetailColorProperty, value); }
+ }
+
+ public string Text
+ {
+ get { return (string)GetValue(TextProperty); }
+ set { SetValue(TextProperty, value); }
+ }
+
+ public Color TextColor
+ {
+ get { return (Color)GetValue(TextColorProperty); }
+ set { SetValue(TextColorProperty, value); }
+ }
+
+ protected internal override void OnTapped()
+ {
+ base.OnTapped();
+
+ ICommand cmd = Command;
+ if (cmd != null)
+ cmd.Execute(CommandParameter);
+ }
+
+ void OnCommandCanExecuteChanged(object sender, EventArgs eventArgs)
+ {
+ IsEnabled = Command.CanExecute(CommandParameter);
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/Cells/ViewCell.cs b/Xamarin.Forms.Core/Cells/ViewCell.cs
new file mode 100644
index 00000000..334822f6
--- /dev/null
+++ b/Xamarin.Forms.Core/Cells/ViewCell.cs
@@ -0,0 +1,48 @@
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+
+namespace Xamarin.Forms
+{
+ [ContentProperty("View")]
+ public class ViewCell : Cell
+ {
+ ReadOnlyCollection<Element> _logicalChildren;
+
+ View _view;
+
+ public View View
+ {
+ get { return _view; }
+ set
+ {
+ if (_view == value)
+ return;
+
+ OnPropertyChanging();
+
+ if (_view != null)
+ {
+ OnChildRemoved(_view);
+ _view.ComputedConstraint = LayoutConstraint.None;
+ }
+
+ _view = value;
+
+ if (_view != null)
+ {
+ _view.ComputedConstraint = LayoutConstraint.Fixed;
+ OnChildAdded(_view);
+ _logicalChildren = new ReadOnlyCollection<Element>(new List<Element>(new[] { View }));
+ }
+ else
+ {
+ _logicalChildren = null;
+ }
+
+ OnPropertyChanged();
+ }
+ }
+
+ internal override ReadOnlyCollection<Element> LogicalChildren => _logicalChildren ?? base.LogicalChildren;
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/ChatKeyboard.cs b/Xamarin.Forms.Core/ChatKeyboard.cs
new file mode 100644
index 00000000..26a403a0
--- /dev/null
+++ b/Xamarin.Forms.Core/ChatKeyboard.cs
@@ -0,0 +1,6 @@
+namespace Xamarin.Forms
+{
+ internal sealed class ChatKeyboard : Keyboard
+ {
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/ChildCollectionChangedEventArgs.cs b/Xamarin.Forms.Core/ChildCollectionChangedEventArgs.cs
new file mode 100644
index 00000000..3c5e946a
--- /dev/null
+++ b/Xamarin.Forms.Core/ChildCollectionChangedEventArgs.cs
@@ -0,0 +1,15 @@
+using System;
+using System.Collections.Specialized;
+
+namespace Xamarin.Forms
+{
+ internal class ChildCollectionChangedEventArgs : EventArgs
+ {
+ public ChildCollectionChangedEventArgs(NotifyCollectionChangedEventArgs args)
+ {
+ Args = args;
+ }
+
+ public NotifyCollectionChangedEventArgs Args { get; private set; }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/CollectionSynchronizationCallback.cs b/Xamarin.Forms.Core/CollectionSynchronizationCallback.cs
new file mode 100644
index 00000000..e186e016
--- /dev/null
+++ b/Xamarin.Forms.Core/CollectionSynchronizationCallback.cs
@@ -0,0 +1,7 @@
+using System;
+using System.Collections;
+
+namespace Xamarin.Forms
+{
+ public delegate void CollectionSynchronizationCallback(IEnumerable collection, object context, Action accessMethod, bool writeAccess);
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/CollectionSynchronizationContext.cs b/Xamarin.Forms.Core/CollectionSynchronizationContext.cs
new file mode 100644
index 00000000..a0144260
--- /dev/null
+++ b/Xamarin.Forms.Core/CollectionSynchronizationContext.cs
@@ -0,0 +1,22 @@
+using System;
+
+namespace Xamarin.Forms
+{
+ internal sealed class CollectionSynchronizationContext
+ {
+ internal CollectionSynchronizationContext(object context, CollectionSynchronizationCallback callback)
+ {
+ ContextReference = new WeakReference(context);
+ Callback = callback;
+ }
+
+ internal CollectionSynchronizationCallback Callback { get; private set; }
+
+ internal object Context
+ {
+ get { return ContextReference != null ? ContextReference.Target : null; }
+ }
+
+ internal WeakReference ContextReference { get; }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/Color.cs b/Xamarin.Forms.Core/Color.cs
new file mode 100644
index 00000000..e9f2987d
--- /dev/null
+++ b/Xamarin.Forms.Core/Color.cs
@@ -0,0 +1,375 @@
+using System;
+using System.Diagnostics;
+using System.Globalization;
+
+namespace Xamarin.Forms
+{
+ [DebuggerDisplay("R={R}, G={G}, B={B}, A={A}, Hue={Hue}, Saturation={Saturation}, Luminosity={Luminosity}")]
+ [TypeConverter(typeof(ColorTypeConverter))]
+ public struct Color
+ {
+ readonly Mode _mode;
+
+ enum Mode
+ {
+ Default,
+ Rgb,
+ Hsl
+ }
+
+ public static Color Default
+ {
+ get { return new Color(-1d, -1d, -1d, -1d, Mode.Default); }
+ }
+
+ internal bool IsDefault
+ {
+ get { return _mode == Mode.Default; }
+ }
+
+ public static Color Accent { get; internal set; }
+
+ readonly float _a;
+
+ public double A
+ {
+ get { return _a; }
+ }
+
+ readonly float _r;
+
+ public double R
+ {
+ get { return _r; }
+ }
+
+ readonly float _g;
+
+ public double G
+ {
+ get { return _g; }
+ }
+
+ readonly float _b;
+
+ public double B
+ {
+ get { return _b; }
+ }
+
+ readonly float _hue;
+
+ public double Hue
+ {
+ get { return _hue; }
+ }
+
+ readonly float _saturation;
+
+ public double Saturation
+ {
+ get { return _saturation; }
+ }
+
+ readonly float _luminosity;
+
+ public double Luminosity
+ {
+ get { return _luminosity; }
+ }
+
+ public Color(double r, double g, double b, double a) : this(r, g, b, a, Mode.Rgb)
+ {
+ }
+
+ Color(double w, double x, double y, double z, Mode mode)
+ {
+ _mode = mode;
+ switch (mode)
+ {
+ default:
+ case Mode.Default:
+ _r = _g = _b = _a = -1;
+ _hue = _saturation = _luminosity = -1;
+ break;
+ case Mode.Rgb:
+ _r = (float)w.Clamp(0, 1);
+ _g = (float)x.Clamp(0, 1);
+ _b = (float)y.Clamp(0, 1);
+ _a = (float)z.Clamp(0, 1);
+ ConvertToHsl(_r, _g, _b, mode, out _hue, out _saturation, out _luminosity);
+ break;
+ case Mode.Hsl:
+ _hue = (float)w.Clamp(0, 1);
+ _saturation = (float)x.Clamp(0, 1);
+ _luminosity = (float)y.Clamp(0, 1);
+ _a = (float)z.Clamp(0, 1);
+ ConvertToRgb(_hue, _saturation, _luminosity, mode, out _r, out _g, out _b);
+ break;
+ }
+ }
+
+ public Color(double r, double g, double b) : this(r, g, b, 1)
+ {
+ }
+
+ public Color(double value) : this(value, value, value, 1)
+ {
+ }
+
+ public Color MultiplyAlpha(double alpha)
+ {
+ switch (_mode)
+ {
+ default:
+ case Mode.Default:
+ throw new InvalidOperationException("Invalid on Color.Default");
+ case Mode.Rgb:
+ return new Color(_r, _g, _b, _a * alpha, Mode.Rgb);
+ case Mode.Hsl:
+ return new Color(_hue, _saturation, _luminosity, _a * alpha, Mode.Hsl);
+ }
+ }
+
+ public Color AddLuminosity(double delta)
+ {
+ if (_mode == Mode.Default)
+ throw new InvalidOperationException("Invalid on Color.Default");
+
+ return new Color(_hue, _saturation, _luminosity + delta, _a, Mode.Hsl);
+ }
+
+ public Color WithHue(double hue)
+ {
+ if (_mode == Mode.Default)
+ throw new InvalidOperationException("Invalid on Color.Default");
+ return new Color(hue, _saturation, _luminosity, _a, Mode.Hsl);
+ }
+
+ public Color WithSaturation(double saturation)
+ {
+ if (_mode == Mode.Default)
+ throw new InvalidOperationException("Invalid on Color.Default");
+ return new Color(_hue, saturation, _luminosity, _a, Mode.Hsl);
+ }
+
+ public Color WithLuminosity(double luminosity)
+ {
+ if (_mode == Mode.Default)
+ throw new InvalidOperationException("Invalid on Color.Default");
+ return new Color(_hue, _saturation, luminosity, _a, Mode.Hsl);
+ }
+
+ static void ConvertToRgb(float hue, float saturation, float luminosity, Mode mode, out float r, out float g, out float b)
+ {
+ if (mode != Mode.Hsl)
+ throw new InvalidOperationException();
+
+ if (luminosity == 0)
+ {
+ r = g = b = 0;
+ return;
+ }
+
+ if (saturation == 0)
+ {
+ r = g = b = luminosity;
+ return;
+ }
+ float temp2 = luminosity <= 0.5f ? luminosity * (1.0f + saturation) : luminosity + saturation - luminosity * saturation;
+ float temp1 = 2.0f * luminosity - temp2;
+
+ var t3 = new[] { hue + 1.0f / 3.0f, hue, hue - 1.0f / 3.0f };
+ var clr = new float[] { 0, 0, 0 };
+ for (var i = 0; i < 3; i++)
+ {
+ if (t3[i] < 0)
+ t3[i] += 1.0f;
+ if (t3[i] > 1)
+ t3[i] -= 1.0f;
+ if (6.0 * t3[i] < 1.0)
+ clr[i] = temp1 + (temp2 - temp1) * t3[i] * 6.0f;
+ else if (2.0 * t3[i] < 1.0)
+ clr[i] = temp2;
+ else if (3.0 * t3[i] < 2.0)
+ clr[i] = temp1 + (temp2 - temp1) * (2.0f / 3.0f - t3[i]) * 6.0f;
+ else
+ clr[i] = temp1;
+ }
+
+ r = clr[0];
+ g = clr[1];
+ b = clr[2];
+ }
+
+ static void ConvertToHsl(float r, float g, float b, Mode mode, out float h, out float s, out float l)
+ {
+ float v = Math.Max(r, g);
+ v = Math.Max(v, b);
+
+ float m = Math.Min(r, g);
+ m = Math.Min(m, b);
+
+ l = (m + v) / 2.0f;
+ if (l <= 0.0)
+ {
+ h = s = l = 0;
+ return;
+ }
+ float vm = v - m;
+ s = vm;
+
+ if (s > 0.0)
+ {
+ s /= l <= 0.5f ? v + m : 2.0f - v - m;
+ }
+ else
+ {
+ h = 0;
+ s = 0;
+ return;
+ }
+
+ float r2 = (v - r) / vm;
+ float g2 = (v - g) / vm;
+ float b2 = (v - b) / vm;
+
+ if (r == v)
+ {
+ h = g == m ? 5.0f + b2 : 1.0f - g2;
+ }
+ else if (g == v)
+ {
+ h = b == m ? 1.0f + r2 : 3.0f - b2;
+ }
+ else
+ {
+ h = r == m ? 3.0f + g2 : 5.0f - r2;
+ }
+ h /= 6.0f;
+ }
+
+ public static bool operator ==(Color color1, Color color2)
+ {
+ return EqualsInner(color1, color2);
+ }
+
+ public static bool operator !=(Color color1, Color color2)
+ {
+ return !EqualsInner(color1, color2);
+ }
+
+ public override int GetHashCode()
+ {
+ unchecked
+ {
+ int hashcode = _r.GetHashCode();
+ hashcode = (hashcode * 397) ^ _g.GetHashCode();
+ hashcode = (hashcode * 397) ^ _b.GetHashCode();
+ hashcode = (hashcode * 397) ^ _a.GetHashCode();
+ return hashcode;
+ }
+ }
+
+ public override bool Equals(object obj)
+ {
+ if (obj is Color)
+ {
+ return EqualsInner(this, (Color)obj);
+ }
+ return base.Equals(obj);
+ }
+
+ static bool EqualsInner(Color color1, Color color2)
+ {
+ if (color1._mode == Mode.Default && color2._mode == Mode.Default)
+ return true;
+ if (color1._mode == Mode.Default || color2._mode == Mode.Default)
+ return false;
+ if (color1._mode == Mode.Hsl && color2._mode == Mode.Hsl)
+ return color1._hue == color2._hue && color1._saturation == color2._saturation && color1._luminosity == color2._luminosity && color1._a == color2._a;
+ return color1._r == color2._r && color1._g == color2._g && color1._b == color2._b && color1._a == color2._a;
+ }
+
+ public override string ToString()
+ {
+ return string.Format(CultureInfo.InvariantCulture, "[Color: A={0}, R={1}, G={2}, B={3}, Hue={4}, Saturation={5}, Luminosity={6}]", A, R, G, B, Hue, Saturation, Luminosity);
+ }
+
+ public static Color FromHex(string hex)
+ {
+ hex = hex.Replace("#", "");
+ switch (hex.Length)
+ {
+ case 3: //#rgb => ffrrggbb
+ hex = string.Format("ff{0}{1}{2}{3}{4}{5}", hex[0], hex[0], hex[1], hex[1], hex[2], hex[2]);
+ break;
+ case 4: //#argb => aarrggbb
+ hex = string.Format("{0}{1}{2}{3}{4}{5}{6}{7}", hex[0], hex[0], hex[1], hex[1], hex[2], hex[2], hex[3], hex[3]);
+ break;
+ case 6: //#rrggbb => ffrrggbb
+ hex = string.Format("ff{0}", hex);
+ break;
+ }
+ return FromUint(Convert.ToUInt32(hex.Replace("#", ""), 16));
+ }
+
+ public static Color FromUint(uint argb)
+ {
+ return FromRgba((byte)((argb & 0x00ff0000) >> 0x10), (byte)((argb & 0x0000ff00) >> 0x8), (byte)(argb & 0x000000ff), (byte)((argb & 0xff000000) >> 0x18));
+ }
+
+ public static Color FromRgba(int r, int g, int b, int a)
+ {
+ double red = (double)r / 255;
+ double green = (double)g / 255;
+ double blue = (double)b / 255;
+ double alpha = (double)a / 255;
+ return new Color(red, green, blue, alpha, Mode.Rgb);
+ }
+
+ public static Color FromRgb(int r, int g, int b)
+ {
+ return FromRgba(r, g, b, 255);
+ }
+
+ public static Color FromRgba(double r, double g, double b, double a)
+ {
+ return new Color(r, g, b, a);
+ }
+
+ public static Color FromRgb(double r, double g, double b)
+ {
+ return new Color(r, g, b, 1d, Mode.Rgb);
+ }
+
+ public static Color FromHsla(double h, double s, double l, double a = 1d)
+ {
+ return new Color(h, s, l, a, Mode.Hsl);
+ }
+
+ #region Color Definitions
+
+ public static readonly Color Transparent = FromRgba(0, 0, 0, 0);
+ public static readonly Color Aqua = FromRgb(0, 255, 255);
+ public static readonly Color Black = FromRgb(0, 0, 0);
+ public static readonly Color Blue = FromRgb(0, 0, 255);
+ public static readonly Color Fuchsia = FromRgb(255, 0, 255);
+ [Obsolete("Fuschia is obsolete as of version 1.3, please use the correct spelling of Fuchsia")] public static readonly Color Fuschia = FromRgb(255, 0, 255);
+ public static readonly Color Gray = FromRgb(128, 128, 128);
+ public static readonly Color Green = FromRgb(0, 128, 0);
+ public static readonly Color Lime = FromRgb(0, 255, 0);
+ public static readonly Color Maroon = FromRgb(128, 0, 0);
+ public static readonly Color Navy = FromRgb(0, 0, 128);
+ public static readonly Color Olive = FromRgb(128, 128, 0);
+ public static readonly Color Purple = FromRgb(128, 0, 128);
+ public static readonly Color Pink = FromRgb(255, 102, 255);
+ public static readonly Color Red = FromRgb(255, 0, 0);
+ public static readonly Color Silver = FromRgb(192, 192, 192);
+ public static readonly Color Teal = FromRgb(0, 128, 128);
+ public static readonly Color White = FromRgb(255, 255, 255);
+ public static readonly Color Yellow = FromRgb(255, 255, 0);
+
+ #endregion
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/ColorTypeConverter.cs b/Xamarin.Forms.Core/ColorTypeConverter.cs
new file mode 100644
index 00000000..547adf3b
--- /dev/null
+++ b/Xamarin.Forms.Core/ColorTypeConverter.cs
@@ -0,0 +1,72 @@
+using System;
+using System.Linq;
+using System.Reflection;
+
+namespace Xamarin.Forms
+{
+ public class ColorTypeConverter : TypeConverter
+ {
+ public override object ConvertFromInvariantString(string value)
+ {
+ if (value != null)
+ {
+ if (value.Trim().StartsWith("#", StringComparison.Ordinal))
+ return Color.FromHex(value);
+ string[] parts = value.Split('.');
+ if (parts.Length == 1 || (parts.Length == 2 && parts[0] == "Color"))
+ {
+ string color = parts[parts.Length - 1];
+ switch (color)
+ {
+ case "Default":
+ return Color.Default;
+ case "Transparent":
+ return Color.Transparent;
+ case "Aqua":
+ return Color.Aqua;
+ case "Black":
+ return Color.Black;
+ case "Blue":
+ return Color.Blue;
+ case "Fuchsia":
+ return Color.Fuchsia;
+ case "Gray":
+ return Color.Gray;
+ case "Green":
+ return Color.Green;
+ case "Lime":
+ return Color.Lime;
+ case "Maroon":
+ return Color.Maroon;
+ case "Navy":
+ return Color.Navy;
+ case "Olive":
+ return Color.Olive;
+ case "Purple":
+ return Color.Purple;
+ case "Pink":
+ return Color.Pink;
+ case "Red":
+ return Color.Red;
+ case "Silver":
+ return Color.Silver;
+ case "Teal":
+ return Color.Teal;
+ case "White":
+ return Color.White;
+ case "Yellow":
+ return Color.Yellow;
+ }
+ FieldInfo field = typeof(Color).GetFields().FirstOrDefault(fi => fi.IsStatic && fi.Name == color);
+ if (field != null)
+ return (Color)field.GetValue(null);
+ PropertyInfo property = typeof(Color).GetProperties().FirstOrDefault(pi => pi.Name == color && pi.CanRead && pi.GetMethod.IsStatic);
+ if (property != null)
+ return (Color)property.GetValue(null, null);
+ }
+ }
+
+ throw new InvalidOperationException(string.Format("Cannot convert \"{0}\" into {1}", value, typeof(Color)));
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/ColumnDefinition.cs b/Xamarin.Forms.Core/ColumnDefinition.cs
new file mode 100644
index 00000000..995f6c73
--- /dev/null
+++ b/Xamarin.Forms.Core/ColumnDefinition.cs
@@ -0,0 +1,34 @@
+using System;
+
+namespace Xamarin.Forms
+{
+ public sealed class ColumnDefinition : BindableObject, IDefinition
+ {
+ public static readonly BindableProperty WidthProperty = BindableProperty.Create("Width", typeof(GridLength), typeof(ColumnDefinition), new GridLength(1, GridUnitType.Star),
+ propertyChanged: (bindable, oldValue, newValue) => ((ColumnDefinition)bindable).OnSizeChanged());
+
+ public ColumnDefinition()
+ {
+ MinimumWidth = -1;
+ }
+
+ public GridLength Width
+ {
+ get { return (GridLength)GetValue(WidthProperty); }
+ set { SetValue(WidthProperty, value); }
+ }
+
+ internal double ActualWidth { get; set; }
+
+ internal double MinimumWidth { get; set; }
+
+ public event EventHandler SizeChanged;
+
+ void OnSizeChanged()
+ {
+ EventHandler eh = SizeChanged;
+ if (eh != null)
+ eh(this, EventArgs.Empty);
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/ColumnDefinitionCollection.cs b/Xamarin.Forms.Core/ColumnDefinitionCollection.cs
new file mode 100644
index 00000000..0ee0358b
--- /dev/null
+++ b/Xamarin.Forms.Core/ColumnDefinitionCollection.cs
@@ -0,0 +1,6 @@
+namespace Xamarin.Forms
+{
+ public sealed class ColumnDefinitionCollection : DefinitionCollection<ColumnDefinition>
+ {
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/Command.cs b/Xamarin.Forms.Core/Command.cs
new file mode 100644
index 00000000..73ae1b08
--- /dev/null
+++ b/Xamarin.Forms.Core/Command.cs
@@ -0,0 +1,80 @@
+using System;
+using System.Windows.Input;
+
+namespace Xamarin.Forms
+{
+ public sealed class Command<T> : Command
+ {
+ public Command(Action<T> execute) : base(o => execute((T)o))
+ {
+ if (execute == null)
+ throw new ArgumentNullException("execute");
+ }
+
+ public Command(Action<T> execute, Func<T, bool> canExecute) : base(o => execute((T)o), o => canExecute((T)o))
+ {
+ if (execute == null)
+ throw new ArgumentNullException("execute");
+ if (canExecute == null)
+ throw new ArgumentNullException("canExecute");
+ }
+ }
+
+ public class Command : ICommand
+ {
+ readonly Func<object, bool> _canExecute;
+ readonly Action<object> _execute;
+
+ public Command(Action<object> execute)
+ {
+ if (execute == null)
+ throw new ArgumentNullException("execute");
+
+ _execute = execute;
+ }
+
+ public Command(Action execute) : this(o => execute())
+ {
+ if (execute == null)
+ throw new ArgumentNullException("execute");
+ }
+
+ public Command(Action<object> execute, Func<object, bool> canExecute) : this(execute)
+ {
+ if (canExecute == null)
+ throw new ArgumentNullException("canExecute");
+
+ _canExecute = canExecute;
+ }
+
+ public Command(Action execute, Func<bool> canExecute) : this(o => execute(), o => canExecute())
+ {
+ if (execute == null)
+ throw new ArgumentNullException("execute");
+ if (canExecute == null)
+ throw new ArgumentNullException("canExecute");
+ }
+
+ public bool CanExecute(object parameter)
+ {
+ if (_canExecute != null)
+ return _canExecute(parameter);
+
+ return true;
+ }
+
+ public event EventHandler CanExecuteChanged;
+
+ public void Execute(object parameter)
+ {
+ _execute(parameter);
+ }
+
+ public void ChangeCanExecute()
+ {
+ EventHandler changed = CanExecuteChanged;
+ if (changed != null)
+ changed(this, EventArgs.Empty);
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/ConcurrentDictionary.cs b/Xamarin.Forms.Core/ConcurrentDictionary.cs
new file mode 100644
index 00000000..a229c6fe
--- /dev/null
+++ b/Xamarin.Forms.Core/ConcurrentDictionary.cs
@@ -0,0 +1,426 @@
+// ConcurrentDictionary.cs
+//
+// Copyright (c) 2009 Jérémie "Garuma" Laval
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+//
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+
+namespace Xamarin.Forms
+{
+ internal class ConcurrentDictionary<TKey, TValue> : IDictionary<TKey, TValue>, ICollection<KeyValuePair<TKey, TValue>>, IEnumerable<KeyValuePair<TKey, TValue>>, IDictionary, ICollection, IEnumerable
+ {
+ readonly IEqualityComparer<TKey> _comparer;
+
+ SplitOrderedList<TKey, KeyValuePair<TKey, TValue>> _internalDictionary;
+
+ public ConcurrentDictionary() : this(EqualityComparer<TKey>.Default)
+ {
+ }
+
+ public ConcurrentDictionary(IEnumerable<KeyValuePair<TKey, TValue>> collection) : this(collection, EqualityComparer<TKey>.Default)
+ {
+ }
+
+ public ConcurrentDictionary(IEqualityComparer<TKey> comparer)
+ {
+ _comparer = comparer;
+ _internalDictionary = new SplitOrderedList<TKey, KeyValuePair<TKey, TValue>>(comparer);
+ }
+
+ public ConcurrentDictionary(IEnumerable<KeyValuePair<TKey, TValue>> collection, IEqualityComparer<TKey> comparer) : this(comparer)
+ {
+ foreach (KeyValuePair<TKey, TValue> pair in collection)
+ Add(pair.Key, pair.Value);
+ }
+
+ // Parameters unused
+ public ConcurrentDictionary(int concurrencyLevel, int capacity) : this(EqualityComparer<TKey>.Default)
+ {
+ }
+
+ public ConcurrentDictionary(int concurrencyLevel, IEnumerable<KeyValuePair<TKey, TValue>> collection, IEqualityComparer<TKey> comparer) : this(collection, comparer)
+ {
+ }
+
+ // Parameters unused
+ public ConcurrentDictionary(int concurrencyLevel, int capacity, IEqualityComparer<TKey> comparer) : this(comparer)
+ {
+ }
+
+ public bool IsEmpty
+ {
+ get { return Count == 0; }
+ }
+
+ void ICollection.CopyTo(Array array, int startIndex)
+ {
+ var arr = array as KeyValuePair<TKey, TValue>[];
+ if (arr == null)
+ return;
+
+ CopyTo(arr, startIndex, Count);
+ }
+
+ bool ICollection.IsSynchronized
+ {
+ get { return true; }
+ }
+
+ object ICollection.SyncRoot
+ {
+ get { return this; }
+ }
+
+ void ICollection<KeyValuePair<TKey, TValue>>.Add(KeyValuePair<TKey, TValue> pair)
+ {
+ Add(pair.Key, pair.Value);
+ }
+
+ public void Clear()
+ {
+ // Pronk
+ _internalDictionary = new SplitOrderedList<TKey, KeyValuePair<TKey, TValue>>(_comparer);
+ }
+
+ bool ICollection<KeyValuePair<TKey, TValue>>.Contains(KeyValuePair<TKey, TValue> pair)
+ {
+ return ContainsKey(pair.Key);
+ }
+
+ void ICollection<KeyValuePair<TKey, TValue>>.CopyTo(KeyValuePair<TKey, TValue>[] array, int startIndex)
+ {
+ CopyTo(array, startIndex);
+ }
+
+ public int Count
+ {
+ get { return _internalDictionary.Count; }
+ }
+
+ bool ICollection<KeyValuePair<TKey, TValue>>.IsReadOnly
+ {
+ get { return false; }
+ }
+
+ bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> pair)
+ {
+ return Remove(pair.Key);
+ }
+
+ void IDictionary.Add(object key, object value)
+ {
+ if (!(key is TKey) || !(value is TValue))
+ throw new ArgumentException("key or value aren't of correct type");
+
+ Add((TKey)key, (TValue)value);
+ }
+
+ bool IDictionary.Contains(object key)
+ {
+ if (!(key is TKey))
+ return false;
+
+ return ContainsKey((TKey)key);
+ }
+
+ IDictionaryEnumerator IDictionary.GetEnumerator()
+ {
+ return new ConcurrentDictionaryEnumerator(GetEnumeratorInternal());
+ }
+
+ bool IDictionary.IsFixedSize
+ {
+ get { return false; }
+ }
+
+ bool IDictionary.IsReadOnly
+ {
+ get { return false; }
+ }
+
+ object IDictionary.this[object key]
+ {
+ get
+ {
+ if (!(key is TKey))
+ throw new ArgumentException("key isn't of correct type", "key");
+
+ return this[(TKey)key];
+ }
+ set
+ {
+ if (!(key is TKey) || !(value is TValue))
+ throw new ArgumentException("key or value aren't of correct type");
+
+ this[(TKey)key] = (TValue)value;
+ }
+ }
+
+ ICollection IDictionary.Keys
+ {
+ get { return (ICollection)Keys; }
+ }
+
+ void IDictionary.Remove(object key)
+ {
+ if (!(key is TKey))
+ return;
+
+ Remove((TKey)key);
+ }
+
+ ICollection IDictionary.Values
+ {
+ get { return (ICollection)Values; }
+ }
+
+ void IDictionary<TKey, TValue>.Add(TKey key, TValue value)
+ {
+ Add(key, value);
+ }
+
+ public bool ContainsKey(TKey key)
+ {
+ CheckKey(key);
+ KeyValuePair<TKey, TValue> dummy;
+ return _internalDictionary.Find(Hash(key), key, out dummy);
+ }
+
+ public TValue this[TKey key]
+ {
+ get { return GetValue(key); }
+ set { AddOrUpdate(key, value, value); }
+ }
+
+ public ICollection<TKey> Keys
+ {
+ get { return GetPart(kvp => kvp.Key); }
+ }
+
+ bool IDictionary<TKey, TValue>.Remove(TKey key)
+ {
+ return Remove(key);
+ }
+
+ public bool TryGetValue(TKey key, out TValue value)
+ {
+ CheckKey(key);
+ KeyValuePair<TKey, TValue> pair;
+ bool result = _internalDictionary.Find(Hash(key), key, out pair);
+ value = pair.Value;
+
+ return result;
+ }
+
+ public ICollection<TValue> Values
+ {
+ get { return GetPart(kvp => kvp.Value); }
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return GetEnumeratorInternal();
+ }
+
+ public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
+ {
+ return GetEnumeratorInternal();
+ }
+
+ public TValue AddOrUpdate(TKey key, Func<TKey, TValue> addValueFactory, Func<TKey, TValue, TValue> updateValueFactory)
+ {
+ CheckKey(key);
+ if (addValueFactory == null)
+ throw new ArgumentNullException("addValueFactory");
+ if (updateValueFactory == null)
+ throw new ArgumentNullException("updateValueFactory");
+ return _internalDictionary.InsertOrUpdate(Hash(key), key, () => Make(key, addValueFactory(key)), e => Make(key, updateValueFactory(key, e.Value))).Value;
+ }
+
+ public TValue AddOrUpdate(TKey key, TValue addValue, Func<TKey, TValue, TValue> updateValueFactory)
+ {
+ return AddOrUpdate(key, _ => addValue, updateValueFactory);
+ }
+
+ public TValue GetOrAdd(TKey key, Func<TKey, TValue> valueFactory)
+ {
+ CheckKey(key);
+ return _internalDictionary.InsertOrGet(Hash(key), key, Make(key, default(TValue)), () => Make(key, valueFactory(key))).Value;
+ }
+
+ public TValue GetOrAdd(TKey key, TValue value)
+ {
+ CheckKey(key);
+ return _internalDictionary.InsertOrGet(Hash(key), key, Make(key, value), null).Value;
+ }
+
+ public KeyValuePair<TKey, TValue>[] ToArray()
+ {
+ // This is most certainly not optimum but there is
+ // not a lot of possibilities
+
+ return new List<KeyValuePair<TKey, TValue>>(this).ToArray();
+ }
+
+ public bool TryAdd(TKey key, TValue value)
+ {
+ CheckKey(key);
+ return _internalDictionary.Insert(Hash(key), key, Make(key, value));
+ }
+
+ public bool TryRemove(TKey key, out TValue value)
+ {
+ CheckKey(key);
+ KeyValuePair<TKey, TValue> data;
+ bool result = _internalDictionary.Delete(Hash(key), key, out data);
+ value = data.Value;
+ return result;
+ }
+
+ public bool TryUpdate(TKey key, TValue newValue, TValue comparisonValue)
+ {
+ CheckKey(key);
+ return _internalDictionary.CompareExchange(Hash(key), key, Make(key, newValue), e => e.Value.Equals(comparisonValue));
+ }
+
+ void Add(TKey key, TValue value)
+ {
+ while (!TryAdd(key, value))
+ ;
+ }
+
+ TValue AddOrUpdate(TKey key, TValue addValue, TValue updateValue)
+ {
+ CheckKey(key);
+ return _internalDictionary.InsertOrUpdate(Hash(key), key, Make(key, addValue), Make(key, updateValue)).Value;
+ }
+
+ void CheckKey(TKey key)
+ {
+ if (key == null)
+ throw new ArgumentNullException("key");
+ }
+
+ void CopyTo(KeyValuePair<TKey, TValue>[] array, int startIndex)
+ {
+ CopyTo(array, startIndex, Count);
+ }
+
+ void CopyTo(KeyValuePair<TKey, TValue>[] array, int startIndex, int num)
+ {
+ foreach (KeyValuePair<TKey, TValue> kvp in this)
+ {
+ array[startIndex++] = kvp;
+
+ if (--num <= 0)
+ return;
+ }
+ }
+
+ IEnumerator<KeyValuePair<TKey, TValue>> GetEnumeratorInternal()
+ {
+ return _internalDictionary.GetEnumerator();
+ }
+
+ ICollection<T> GetPart<T>(Func<KeyValuePair<TKey, TValue>, T> extractor)
+ {
+ var temp = new List<T>();
+
+ foreach (KeyValuePair<TKey, TValue> kvp in this)
+ temp.Add(extractor(kvp));
+
+ return new ReadOnlyCollection<T>(temp);
+ }
+
+ TValue GetValue(TKey key)
+ {
+ TValue temp;
+ if (!TryGetValue(key, out temp))
+ throw new KeyNotFoundException(key.ToString());
+ return temp;
+ }
+
+ uint Hash(TKey key)
+ {
+ return (uint)_comparer.GetHashCode(key);
+ }
+
+ static KeyValuePair<T, V> Make<T, V>(T key, V value)
+ {
+ return new KeyValuePair<T, V>(key, value);
+ }
+
+ bool Remove(TKey key)
+ {
+ TValue dummy;
+
+ return TryRemove(key, out dummy);
+ }
+
+ class ConcurrentDictionaryEnumerator : IDictionaryEnumerator
+ {
+ readonly IEnumerator<KeyValuePair<TKey, TValue>> _internalEnum;
+
+ public ConcurrentDictionaryEnumerator(IEnumerator<KeyValuePair<TKey, TValue>> internalEnum)
+ {
+ _internalEnum = internalEnum;
+ }
+
+ public DictionaryEntry Entry
+ {
+ get
+ {
+ KeyValuePair<TKey, TValue> current = _internalEnum.Current;
+ return new DictionaryEntry(current.Key, current.Value);
+ }
+ }
+
+ public object Key
+ {
+ get { return _internalEnum.Current.Key; }
+ }
+
+ public object Value
+ {
+ get { return _internalEnum.Current.Value; }
+ }
+
+ public object Current
+ {
+ get { return Entry; }
+ }
+
+ public bool MoveNext()
+ {
+ return _internalEnum.MoveNext();
+ }
+
+ public void Reset()
+ {
+ _internalEnum.Reset();
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/Constraint.cs b/Xamarin.Forms.Core/Constraint.cs
new file mode 100644
index 00000000..bd219f0a
--- /dev/null
+++ b/Xamarin.Forms.Core/Constraint.cs
@@ -0,0 +1,56 @@
+using System;
+using System.Collections.Generic;
+using System.Linq.Expressions;
+
+namespace Xamarin.Forms
+{
+ [TypeConverter(typeof(ConstraintTypeConverter))]
+ public sealed class Constraint
+ {
+ Func<RelativeLayout, double> _measureFunc;
+
+ Constraint()
+ {
+ }
+
+ internal IEnumerable<View> RelativeTo { get; set; }
+
+ public static Constraint Constant(double size)
+ {
+ var result = new Constraint { _measureFunc = parent => size };
+
+ return result;
+ }
+
+ public static Constraint FromExpression(Expression<Func<double>> expression)
+ {
+ Func<double> compiled = expression.Compile();
+ var result = new Constraint
+ {
+ _measureFunc = layout => compiled(),
+ RelativeTo = ExpressionSearch.Default.FindObjects<View>(expression).ToArray() // make sure we have our own copy
+ };
+
+ return result;
+ }
+
+ public static Constraint RelativeToParent(Func<RelativeLayout, double> measure)
+ {
+ var result = new Constraint { _measureFunc = measure };
+
+ return result;
+ }
+
+ public static Constraint RelativeToView(View view, Func<RelativeLayout, View, double> measure)
+ {
+ var result = new Constraint { _measureFunc = layout => measure(layout, view), RelativeTo = new[] { view } };
+
+ return result;
+ }
+
+ internal double Compute(RelativeLayout parent)
+ {
+ return _measureFunc(parent);
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/ConstraintExpression.cs b/Xamarin.Forms.Core/ConstraintExpression.cs
new file mode 100644
index 00000000..b2ca4b8f
--- /dev/null
+++ b/Xamarin.Forms.Core/ConstraintExpression.cs
@@ -0,0 +1,53 @@
+using System;
+using System.Linq;
+using System.Reflection;
+using Xamarin.Forms.Internals;
+using Xamarin.Forms.Xaml;
+
+namespace Xamarin.Forms
+{
+ public class ConstraintExpression : IMarkupExtension
+ {
+ public ConstraintExpression()
+ {
+ Factor = 1.0;
+ }
+
+ public double Constant { get; set; }
+
+ public string ElementName { get; set; }
+
+ public double Factor { get; set; }
+
+ public string Property { get; set; }
+
+ public ConstraintType Type { get; set; }
+
+ public object ProvideValue(IServiceProvider serviceProvider)
+ {
+ MethodInfo minfo;
+ switch (Type)
+ {
+ default:
+ case ConstraintType.RelativeToParent:
+ if (string.IsNullOrEmpty(Property))
+ return null;
+ minfo = typeof(View).GetProperties().First(pi => pi.Name == Property && pi.CanRead && pi.GetMethod.IsPublic).GetMethod;
+ return Constraint.RelativeToParent(p => (double)minfo.Invoke(p, new object[] { }) * Factor + Constant);
+ case ConstraintType.Constant:
+ return Constraint.Constant(Constant);
+ case ConstraintType.RelativeToView:
+ if (string.IsNullOrEmpty(Property))
+ return null;
+ if (string.IsNullOrEmpty(ElementName))
+ return null;
+ minfo = typeof(View).GetProperties().First(pi => pi.Name == Property && pi.CanRead && pi.GetMethod.IsPublic).GetMethod;
+ var valueProvider = serviceProvider.GetService(typeof(IProvideValueTarget)) as IProvideValueTarget;
+ if (valueProvider == null || !(valueProvider.TargetObject is INameScope))
+ return null;
+ var view = ((INameScope)valueProvider.TargetObject).FindByName<View>(ElementName);
+ return Constraint.RelativeToView(view, delegate(RelativeLayout p, View v) { return (double)minfo.Invoke(v, new object[] { }) * Factor + Constant; });
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/ConstraintType.cs b/Xamarin.Forms.Core/ConstraintType.cs
new file mode 100644
index 00000000..5ee8bc9a
--- /dev/null
+++ b/Xamarin.Forms.Core/ConstraintType.cs
@@ -0,0 +1,9 @@
+namespace Xamarin.Forms
+{
+ public enum ConstraintType
+ {
+ RelativeToParent,
+ RelativeToView,
+ Constant
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/ConstraintTypeConverter.cs b/Xamarin.Forms.Core/ConstraintTypeConverter.cs
new file mode 100644
index 00000000..8cc45229
--- /dev/null
+++ b/Xamarin.Forms.Core/ConstraintTypeConverter.cs
@@ -0,0 +1,17 @@
+using System;
+using System.Globalization;
+
+namespace Xamarin.Forms
+{
+ public class ConstraintTypeConverter : TypeConverter
+ {
+ public override object ConvertFromInvariantString(string value)
+ {
+ double size;
+ if (value != null && double.TryParse(value, NumberStyles.Number, CultureInfo.InvariantCulture, out size))
+ return Constraint.Constant(size);
+
+ throw new InvalidOperationException(string.Format("Cannot convert \"{0}\" into {1}", value, typeof(Color)));
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/ContentPage.cs b/Xamarin.Forms.Core/ContentPage.cs
new file mode 100644
index 00000000..01957c39
--- /dev/null
+++ b/Xamarin.Forms.Core/ContentPage.cs
@@ -0,0 +1,26 @@
+namespace Xamarin.Forms
+{
+ [ContentProperty("Content")]
+ public class ContentPage : TemplatedPage
+ {
+ public static readonly BindableProperty ContentProperty = BindableProperty.Create(nameof(Content), typeof(View), typeof(ContentPage), null, propertyChanged: TemplateUtilities.OnContentChanged);
+
+ public View Content
+ {
+ get { return (View)GetValue(ContentProperty); }
+ set { SetValue(ContentProperty, value); }
+ }
+
+ protected override void OnBindingContextChanged()
+ {
+ base.OnBindingContextChanged();
+
+ View content = Content;
+ ControlTemplate controlTemplate = ControlTemplate;
+ if (content != null && controlTemplate != null)
+ {
+ SetInheritedBindingContext(content, BindingContext);
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/ContentPresenter.cs b/Xamarin.Forms.Core/ContentPresenter.cs
new file mode 100644
index 00000000..a99a048b
--- /dev/null
+++ b/Xamarin.Forms.Core/ContentPresenter.cs
@@ -0,0 +1,91 @@
+using System;
+
+namespace Xamarin.Forms
+{
+ public class ContentPresenter : Layout
+ {
+ public static BindableProperty ContentProperty = BindableProperty.Create("Content", typeof(View), typeof(ContentPresenter), null, propertyChanged: OnContentChanged);
+
+ public ContentPresenter()
+ {
+ SetBinding(ContentProperty, new TemplateBinding("Content"));
+ }
+
+ public View Content
+ {
+ get { return (View)GetValue(ContentProperty); }
+ set { SetValue(ContentProperty, value); }
+ }
+
+ protected override void LayoutChildren(double x, double y, double width, double height)
+ {
+ for (var i = 0; i < LogicalChildren.Count; i++)
+ {
+ Element element = LogicalChildren[i];
+ var child = element as View;
+ if (child != null)
+ LayoutChildIntoBoundingRegion(child, new Rectangle(x, y, width, height));
+ }
+ }
+
+ [Obsolete("Use OnMeasure")]
+ protected override SizeRequest OnSizeRequest(double widthConstraint, double heightConstraint)
+ {
+ double widthRequest = WidthRequest;
+ double heightRequest = HeightRequest;
+ var childRequest = new SizeRequest();
+ if ((widthRequest == -1 || heightRequest == -1) && Content != null)
+ {
+ childRequest = Content.Measure(widthConstraint, heightConstraint, MeasureFlags.IncludeMargins);
+ }
+
+ return new SizeRequest
+ {
+ Request = new Size { Width = widthRequest != -1 ? widthRequest : childRequest.Request.Width, Height = heightRequest != -1 ? heightRequest : childRequest.Request.Height },
+ Minimum = childRequest.Minimum
+ };
+ }
+
+ internal virtual void Clear()
+ {
+ Content = null;
+ }
+
+ internal override void ComputeConstraintForView(View view)
+ {
+ bool isFixedHorizontally = (Constraint & LayoutConstraint.HorizontallyFixed) != 0;
+ bool isFixedVertically = (Constraint & LayoutConstraint.VerticallyFixed) != 0;
+
+ var result = LayoutConstraint.None;
+ if (isFixedVertically && view.VerticalOptions.Alignment == LayoutAlignment.Fill)
+ result |= LayoutConstraint.VerticallyFixed;
+ if (isFixedHorizontally && view.HorizontalOptions.Alignment == LayoutAlignment.Fill)
+ result |= LayoutConstraint.HorizontallyFixed;
+ view.ComputedConstraint = result;
+ }
+
+ internal override void SetChildInheritedBindingContext(Element child, object context)
+ {
+ // We never want to use the standard inheritance mechanism, we will get this set by our parent
+ }
+
+ static async void OnContentChanged(BindableObject bindable, object oldValue, object newValue)
+ {
+ var self = (ContentPresenter)bindable;
+
+ var oldView = (View)oldValue;
+ var newView = (View)newValue;
+ if (oldView != null)
+ {
+ self.InternalChildren.Remove(oldView);
+ oldView.ParentOverride = null;
+ }
+
+ if (newView != null)
+ {
+ self.InternalChildren.Add(newView);
+ newView.ParentOverride = await TemplateUtilities.FindTemplatedParentAsync((Element)bindable);
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/ContentPropertyAttribute.cs b/Xamarin.Forms.Core/ContentPropertyAttribute.cs
new file mode 100644
index 00000000..7aa60744
--- /dev/null
+++ b/Xamarin.Forms.Core/ContentPropertyAttribute.cs
@@ -0,0 +1,26 @@
+//
+// ContentPropertyAttribute.cs
+//
+// Author:
+// Stephane Delcroix <stephane@delcroix.org>
+//
+// Copyright (c) 2013 S. Delcroix
+//
+
+using System;
+
+namespace Xamarin.Forms
+{
+ [AttributeUsage(AttributeTargets.Class)]
+ public sealed class ContentPropertyAttribute : Attribute
+ {
+ internal static string[] ContentPropertyTypes = { "Xamarin.Forms.ContentPropertyAttribute", "System.Windows.Markup.ContentPropertyAttribute" };
+
+ public ContentPropertyAttribute(string name)
+ {
+ Name = name;
+ }
+
+ public string Name { get; private set; }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/ContentView.cs b/Xamarin.Forms.Core/ContentView.cs
new file mode 100644
index 00000000..a30688f9
--- /dev/null
+++ b/Xamarin.Forms.Core/ContentView.cs
@@ -0,0 +1,26 @@
+namespace Xamarin.Forms
+{
+ [ContentProperty("Content")]
+ public class ContentView : TemplatedView
+ {
+ public static readonly BindableProperty ContentProperty = BindableProperty.Create(nameof(Content), typeof(View), typeof(ContentView), null, propertyChanged: TemplateUtilities.OnContentChanged);
+
+ public View Content
+ {
+ get { return (View)GetValue(ContentProperty); }
+ set { SetValue(ContentProperty, value); }
+ }
+
+ protected override void OnBindingContextChanged()
+ {
+ base.OnBindingContextChanged();
+
+ View content = Content;
+ ControlTemplate controlTemplate = ControlTemplate;
+ if (content != null && controlTemplate != null)
+ {
+ SetInheritedBindingContext(content, BindingContext);
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/ControlTemplate.cs b/Xamarin.Forms.Core/ControlTemplate.cs
new file mode 100644
index 00000000..1e198e97
--- /dev/null
+++ b/Xamarin.Forms.Core/ControlTemplate.cs
@@ -0,0 +1,15 @@
+using System;
+
+namespace Xamarin.Forms
+{
+ public class ControlTemplate : ElementTemplate
+ {
+ public ControlTemplate()
+ {
+ }
+
+ public ControlTemplate(Type type) : base(type)
+ {
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/CustomKeyboard.cs b/Xamarin.Forms.Core/CustomKeyboard.cs
new file mode 100644
index 00000000..422198cc
--- /dev/null
+++ b/Xamarin.Forms.Core/CustomKeyboard.cs
@@ -0,0 +1,12 @@
+namespace Xamarin.Forms
+{
+ internal sealed class CustomKeyboard : Keyboard
+ {
+ internal CustomKeyboard(KeyboardFlags flags)
+ {
+ Flags = flags;
+ }
+
+ internal KeyboardFlags Flags { get; private set; }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/DataTemplate.cs b/Xamarin.Forms.Core/DataTemplate.cs
new file mode 100644
index 00000000..676718a0
--- /dev/null
+++ b/Xamarin.Forms.Core/DataTemplate.cs
@@ -0,0 +1,80 @@
+using System;
+using System.Collections.Generic;
+
+namespace Xamarin.Forms
+{
+ public class DataTemplate : ElementTemplate
+ {
+ public DataTemplate()
+ {
+ }
+
+ public DataTemplate(Type type) : base(type)
+ {
+ }
+
+ public DataTemplate(Func<object> loadTemplate) : base(loadTemplate)
+ {
+ }
+
+ public IDictionary<BindableProperty, BindingBase> Bindings { get; } = new Dictionary<BindableProperty, BindingBase>();
+
+ public IDictionary<BindableProperty, object> Values { get; } = new Dictionary<BindableProperty, object>();
+
+ public void SetBinding(BindableProperty property, BindingBase binding)
+ {
+ if (property == null)
+ throw new ArgumentNullException("property");
+ if (binding == null)
+ throw new ArgumentNullException("binding");
+
+ Values.Remove(property);
+ Bindings[property] = binding;
+ }
+
+ public void SetValue(BindableProperty property, object value)
+ {
+ if (property == null)
+ throw new ArgumentNullException("property");
+
+ Bindings.Remove(property);
+ Values[property] = value;
+ }
+
+ internal override void SetupContent(object item)
+ {
+ ApplyBindings(item);
+ ApplyValues(item);
+ }
+
+ void ApplyBindings(object item)
+ {
+ if (Bindings == null)
+ return;
+
+ var bindable = item as BindableObject;
+ if (bindable == null)
+ return;
+
+ foreach (KeyValuePair<BindableProperty, BindingBase> kvp in Bindings)
+ {
+ if (Values.ContainsKey(kvp.Key))
+ throw new InvalidOperationException("Binding and Value found for " + kvp.Key.PropertyName);
+
+ bindable.SetBinding(kvp.Key, kvp.Value.Clone());
+ }
+ }
+
+ void ApplyValues(object item)
+ {
+ if (Values == null)
+ return;
+
+ var bindable = item as BindableObject;
+ if (bindable == null)
+ return;
+ foreach (KeyValuePair<BindableProperty, object> kvp in Values)
+ bindable.SetValue(kvp.Key, kvp.Value);
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/DataTemplateExtensions.cs b/Xamarin.Forms.Core/DataTemplateExtensions.cs
new file mode 100644
index 00000000..ffa0ffd0
--- /dev/null
+++ b/Xamarin.Forms.Core/DataTemplateExtensions.cs
@@ -0,0 +1,15 @@
+namespace Xamarin.Forms
+{
+ internal static class DataTemplateExtensions
+ {
+ public static object CreateContent(this DataTemplate self, object item, BindableObject container)
+ {
+ var selector = self as DataTemplateSelector;
+ if (selector != null)
+ {
+ self = selector.SelectTemplate(item, container);
+ }
+ return self.CreateContent();
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/DataTemplateSelector.cs b/Xamarin.Forms.Core/DataTemplateSelector.cs
new file mode 100644
index 00000000..8ffa4781
--- /dev/null
+++ b/Xamarin.Forms.Core/DataTemplateSelector.cs
@@ -0,0 +1,17 @@
+using System;
+
+namespace Xamarin.Forms
+{
+ public abstract class DataTemplateSelector : DataTemplate
+ {
+ public DataTemplate SelectTemplate(object item, BindableObject container)
+ {
+ DataTemplate result = OnSelectTemplate(item, container);
+ if (result is DataTemplateSelector)
+ throw new NotSupportedException("DataTemplateSelector.OnSelectTemplate must not return another DataTemplateSelector");
+ return result;
+ }
+
+ protected abstract DataTemplate OnSelectTemplate(object item, BindableObject container);
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/DateChangedEventArgs.cs b/Xamarin.Forms.Core/DateChangedEventArgs.cs
new file mode 100644
index 00000000..8fbc803d
--- /dev/null
+++ b/Xamarin.Forms.Core/DateChangedEventArgs.cs
@@ -0,0 +1,17 @@
+using System;
+
+namespace Xamarin.Forms
+{
+ public class DateChangedEventArgs : EventArgs
+ {
+ public DateChangedEventArgs(DateTime oldDate, DateTime newDate)
+ {
+ OldDate = oldDate;
+ NewDate = newDate;
+ }
+
+ public DateTime NewDate { get; private set; }
+
+ public DateTime OldDate { get; private set; }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/DatePicker.cs b/Xamarin.Forms.Core/DatePicker.cs
new file mode 100644
index 00000000..15f1c198
--- /dev/null
+++ b/Xamarin.Forms.Core/DatePicker.cs
@@ -0,0 +1,99 @@
+using System;
+using Xamarin.Forms.Platform;
+
+namespace Xamarin.Forms
+{
+ [RenderWith(typeof(_DatePickerRenderer))]
+ public class DatePicker : View
+ {
+ public static readonly BindableProperty FormatProperty = BindableProperty.Create("Format", typeof(string), typeof(DatePicker), "d");
+
+ public static readonly BindableProperty DateProperty = BindableProperty.Create("Date", typeof(DateTime), typeof(DatePicker), DateTime.Today, BindingMode.TwoWay, coerceValue: CoerceDate,
+ propertyChanged: DatePropertyChanged);
+
+ public static readonly BindableProperty MinimumDateProperty = BindableProperty.Create("MinimumDate", typeof(DateTime), typeof(DatePicker), new DateTime(1900, 1, 1),
+ validateValue: ValidateMinimumDate, coerceValue: CoerceMinimumDate);
+
+ public static readonly BindableProperty MaximumDateProperty = BindableProperty.Create("MaximumDate", typeof(DateTime), typeof(DatePicker), new DateTime(2100, 12, 31),
+ validateValue: ValidateMaximumDate, coerceValue: CoerceMaximumDate);
+
+ public DateTime Date
+ {
+ get { return (DateTime)GetValue(DateProperty); }
+ set { SetValue(DateProperty, value); }
+ }
+
+ public string Format
+ {
+ get { return (string)GetValue(FormatProperty); }
+ set { SetValue(FormatProperty, value); }
+ }
+
+ public DateTime MaximumDate
+ {
+ get { return (DateTime)GetValue(MaximumDateProperty); }
+ set { SetValue(MaximumDateProperty, value); }
+ }
+
+ public DateTime MinimumDate
+ {
+ get { return (DateTime)GetValue(MinimumDateProperty); }
+ set { SetValue(MinimumDateProperty, value); }
+ }
+
+ public event EventHandler<DateChangedEventArgs> DateSelected;
+
+ static object CoerceDate(BindableObject bindable, object value)
+ {
+ var picker = (DatePicker)bindable;
+ DateTime dateValue = ((DateTime)value).Date;
+
+ if (dateValue > picker.MaximumDate)
+ dateValue = picker.MaximumDate;
+
+ if (dateValue < picker.MinimumDate)
+ dateValue = picker.MinimumDate;
+
+ return dateValue;
+ }
+
+ static object CoerceMaximumDate(BindableObject bindable, object value)
+ {
+ DateTime dateValue = ((DateTime)value).Date;
+ var picker = (DatePicker)bindable;
+ if (picker.Date > dateValue)
+ picker.Date = dateValue;
+
+ return dateValue;
+ }
+
+ static object CoerceMinimumDate(BindableObject bindable, object value)
+ {
+ DateTime dateValue = ((DateTime)value).Date;
+ var picker = (DatePicker)bindable;
+ if (picker.Date < dateValue)
+ picker.Date = dateValue;
+
+ return dateValue;
+ }
+
+ static void DatePropertyChanged(BindableObject bindable, object oldValue, object newValue)
+ {
+ var datePicker = (DatePicker)bindable;
+ EventHandler<DateChangedEventArgs> selected = datePicker.DateSelected;
+
+ if (selected != null)
+ selected(datePicker, new DateChangedEventArgs((DateTime)oldValue, (DateTime)newValue));
+ }
+
+ static bool ValidateMaximumDate(BindableObject bindable, object value)
+ {
+ return (DateTime)value >= ((DatePicker)bindable).MinimumDate;
+ }
+
+ static bool ValidateMinimumDate(BindableObject bindable, object value)
+ {
+ return (DateTime)value <= ((DatePicker)bindable).MaximumDate;
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/DefinitionCollection.cs b/Xamarin.Forms.Core/DefinitionCollection.cs
new file mode 100644
index 00000000..bf0e4d06
--- /dev/null
+++ b/Xamarin.Forms.Core/DefinitionCollection.cs
@@ -0,0 +1,109 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+
+namespace Xamarin.Forms
+{
+ public class DefinitionCollection<T> : IList<T>, ICollection<T> where T : IDefinition
+ {
+ readonly List<T> _internalList = new List<T>();
+
+ internal DefinitionCollection()
+ {
+ }
+
+ public void Add(T item)
+ {
+ _internalList.Add(item);
+ item.SizeChanged += OnItemSizeChanged;
+ OnItemSizeChanged(this, EventArgs.Empty);
+ }
+
+ public void Clear()
+ {
+ foreach (T item in _internalList)
+ item.SizeChanged -= OnItemSizeChanged;
+ _internalList.Clear();
+ OnItemSizeChanged(this, EventArgs.Empty);
+ }
+
+ public bool Contains(T item)
+ {
+ return _internalList.Contains(item);
+ }
+
+ public void CopyTo(T[] array, int arrayIndex)
+ {
+ _internalList.CopyTo(array, arrayIndex);
+ }
+
+ public int Count
+ {
+ get { return _internalList.Count; }
+ }
+
+ public bool IsReadOnly
+ {
+ get { return false; }
+ }
+
+ public bool Remove(T item)
+ {
+ item.SizeChanged -= OnItemSizeChanged;
+ bool success = _internalList.Remove(item);
+ if (success)
+ OnItemSizeChanged(this, EventArgs.Empty);
+ return success;
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return _internalList.GetEnumerator();
+ }
+
+ public IEnumerator<T> GetEnumerator()
+ {
+ return _internalList.GetEnumerator();
+ }
+
+ public int IndexOf(T item)
+ {
+ return _internalList.IndexOf(item);
+ }
+
+ public void Insert(int index, T item)
+ {
+ _internalList.Insert(index, item);
+ item.SizeChanged += OnItemSizeChanged;
+ OnItemSizeChanged(this, EventArgs.Empty);
+ }
+
+ public T this[int index]
+ {
+ get { return _internalList[index]; }
+ set
+ {
+ _internalList[index] = value;
+ value.SizeChanged += OnItemSizeChanged;
+ OnItemSizeChanged(this, EventArgs.Empty);
+ }
+ }
+
+ public void RemoveAt(int index)
+ {
+ T item = _internalList[index];
+ _internalList.RemoveAt(index);
+ item.SizeChanged -= OnItemSizeChanged;
+ OnItemSizeChanged(this, EventArgs.Empty);
+ }
+
+ public event EventHandler ItemSizeChanged;
+
+ void OnItemSizeChanged(object sender, EventArgs e)
+ {
+ EventHandler eh = ItemSizeChanged;
+ if (eh != null)
+ eh(this, EventArgs.Empty);
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/DelegateLogListener.cs b/Xamarin.Forms.Core/DelegateLogListener.cs
new file mode 100644
index 00000000..20a0ab76
--- /dev/null
+++ b/Xamarin.Forms.Core/DelegateLogListener.cs
@@ -0,0 +1,22 @@
+using System;
+
+namespace Xamarin.Forms
+{
+ internal class DelegateLogListener : LogListener
+ {
+ readonly Action<string, string> _log;
+
+ public DelegateLogListener(Action<string, string> log)
+ {
+ if (log == null)
+ throw new ArgumentNullException("log");
+
+ _log = log;
+ }
+
+ public override void Warning(string category, string message)
+ {
+ _log(category, message);
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/DependencyAttribute.cs b/Xamarin.Forms.Core/DependencyAttribute.cs
new file mode 100644
index 00000000..e0ea22a1
--- /dev/null
+++ b/Xamarin.Forms.Core/DependencyAttribute.cs
@@ -0,0 +1,15 @@
+using System;
+
+namespace Xamarin.Forms
+{
+ [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
+ public class DependencyAttribute : Attribute
+ {
+ public DependencyAttribute(Type implementorType)
+ {
+ Implementor = implementorType;
+ }
+
+ internal Type Implementor { get; private set; }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/DependencyFetchTarget.cs b/Xamarin.Forms.Core/DependencyFetchTarget.cs
new file mode 100644
index 00000000..2433c4f4
--- /dev/null
+++ b/Xamarin.Forms.Core/DependencyFetchTarget.cs
@@ -0,0 +1,8 @@
+namespace Xamarin.Forms
+{
+ public enum DependencyFetchTarget
+ {
+ GlobalInstance,
+ NewInstance
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/DependencyService.cs b/Xamarin.Forms.Core/DependencyService.cs
new file mode 100644
index 00000000..d3c43998
--- /dev/null
+++ b/Xamarin.Forms.Core/DependencyService.cs
@@ -0,0 +1,102 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+
+namespace Xamarin.Forms
+{
+ public static class DependencyService
+ {
+ static bool s_initialized;
+
+ static readonly List<Type> DependencyTypes = new List<Type>();
+ static readonly Dictionary<Type, DependencyData> DependencyImplementations = new Dictionary<Type, DependencyData>();
+
+ public static T Get<T>(DependencyFetchTarget fetchTarget = DependencyFetchTarget.GlobalInstance) where T : class
+ {
+ if (!s_initialized)
+ Initialize();
+
+ Type targetType = typeof(T);
+
+ if (!DependencyImplementations.ContainsKey(targetType))
+ {
+ Type implementor = FindImplementor(targetType);
+ DependencyImplementations[targetType] = implementor != null ? new DependencyData { ImplementorType = implementor } : null;
+ }
+
+ DependencyData dependencyImplementation = DependencyImplementations[targetType];
+ if (dependencyImplementation == null)
+ return null;
+
+ if (fetchTarget == DependencyFetchTarget.GlobalInstance)
+ {
+ if (dependencyImplementation.GlobalInstance == null)
+ {
+ dependencyImplementation.GlobalInstance = Activator.CreateInstance(dependencyImplementation.ImplementorType);
+ }
+ return (T)dependencyImplementation.GlobalInstance;
+ }
+ return (T)Activator.CreateInstance(dependencyImplementation.ImplementorType);
+ }
+
+ public static void Register<T>() where T : class
+ {
+ Type type = typeof(T);
+ if (!DependencyTypes.Contains(type))
+ DependencyTypes.Add(type);
+ }
+
+ public static void Register<T, TImpl>() where T : class where TImpl : class, T
+ {
+ Type targetType = typeof(T);
+ Type implementorType = typeof(TImpl);
+ if (!DependencyTypes.Contains(targetType))
+ DependencyTypes.Add(targetType);
+
+ DependencyImplementations[targetType] = new DependencyData { ImplementorType = implementorType };
+ }
+
+ static Type FindImplementor(Type target)
+ {
+ return DependencyTypes.FirstOrDefault(t => target.IsAssignableFrom(t));
+ }
+
+ static void Initialize()
+ {
+ Assembly[] assemblies = Device.GetAssemblies();
+ if (Registrar.ExtraAssemblies != null)
+ {
+ assemblies = assemblies.Union(Registrar.ExtraAssemblies).ToArray();
+ }
+
+ Type targetAttrType = typeof(DependencyAttribute);
+
+ // Don't use LINQ for performance reasons
+ // Naive implementation can easily take over a second to run
+ foreach (Assembly assembly in assemblies)
+ {
+ Attribute[] attributes = assembly.GetCustomAttributes(targetAttrType).ToArray();
+ if (attributes.Length == 0)
+ continue;
+
+ foreach (DependencyAttribute attribute in attributes)
+ {
+ if (!DependencyTypes.Contains(attribute.Implementor))
+ {
+ DependencyTypes.Add(attribute.Implementor);
+ }
+ }
+ }
+
+ s_initialized = true;
+ }
+
+ class DependencyData
+ {
+ public object GlobalInstance { get; set; }
+
+ public Type ImplementorType { get; set; }
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/Device.cs b/Xamarin.Forms.Core/Device.cs
new file mode 100644
index 00000000..db0a2747
--- /dev/null
+++ b/Xamarin.Forms.Core/Device.cs
@@ -0,0 +1,159 @@
+using System;
+using System.IO;
+using System.Reflection;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Xamarin.Forms
+{
+ public static class Device
+ {
+ internal static DeviceInfo info;
+
+ static IPlatformServices s_platformServices;
+
+ public static TargetIdiom Idiom { get; internal set; }
+
+ public static TargetPlatform OS { get; internal set; }
+
+ internal static DeviceInfo Info
+ {
+ get
+ {
+ if (info == null)
+ throw new InvalidOperationException("You MUST call Xamarin.Forms.Init(); prior to using it.");
+ return info;
+ }
+ set { info = value; }
+ }
+
+ internal static bool IsInvokeRequired
+ {
+ get { return PlatformServices.IsInvokeRequired; }
+ }
+
+ internal static IPlatformServices PlatformServices
+ {
+ get
+ {
+ if (s_platformServices == null)
+ throw new InvalidOperationException("You MUST call Xamarin.Forms.Init(); prior to using it.");
+ return s_platformServices;
+ }
+ set { s_platformServices = value; }
+ }
+
+ public static void BeginInvokeOnMainThread(Action action)
+ {
+ PlatformServices.BeginInvokeOnMainThread(action);
+ }
+
+ public static double GetNamedSize(NamedSize size, Element targetElement)
+ {
+ return GetNamedSize(size, targetElement.GetType());
+ }
+
+ public static double GetNamedSize(NamedSize size, Type targetElementType)
+ {
+ return GetNamedSize(size, targetElementType, false);
+ }
+
+ public static void OnPlatform(Action iOS = null, Action Android = null, Action WinPhone = null, Action Default = null)
+ {
+ switch (OS)
+ {
+ case TargetPlatform.iOS:
+ if (iOS != null)
+ iOS();
+ else if (Default != null)
+ Default();
+ break;
+ case TargetPlatform.Android:
+ if (Android != null)
+ Android();
+ else if (Default != null)
+ Default();
+ break;
+ case TargetPlatform.Windows:
+ case TargetPlatform.WinPhone:
+ if (WinPhone != null)
+ WinPhone();
+ else if (Default != null)
+ Default();
+ break;
+ case TargetPlatform.Other:
+ if (Default != null)
+ Default();
+ break;
+ }
+ }
+
+ public static T OnPlatform<T>(T iOS, T Android, T WinPhone)
+ {
+ switch (OS)
+ {
+ case TargetPlatform.iOS:
+ return iOS;
+ case TargetPlatform.Android:
+ return Android;
+ case TargetPlatform.Windows:
+ case TargetPlatform.WinPhone:
+ return WinPhone;
+ }
+
+ return iOS;
+ }
+
+ public static void OpenUri(Uri uri)
+ {
+ PlatformServices.OpenUriAction(uri);
+ }
+
+ public static void StartTimer(TimeSpan interval, Func<bool> callback)
+ {
+ PlatformServices.StartTimer(interval, callback);
+ }
+
+ internal static Assembly[] GetAssemblies()
+ {
+ return PlatformServices.GetAssemblies();
+ }
+
+ internal static double GetNamedSize(NamedSize size, Type targetElementType, bool useOldSizes)
+ {
+ return PlatformServices.GetNamedSize(size, targetElementType, useOldSizes);
+ }
+
+ internal static Task<Stream> GetStreamAsync(Uri uri, CancellationToken cancellationToken)
+ {
+ return PlatformServices.GetStreamAsync(uri, cancellationToken);
+ }
+
+ public static class Styles
+ {
+ public static readonly string TitleStyleKey = "TitleStyle";
+
+ public static readonly string SubtitleStyleKey = "SubtitleStyle";
+
+ public static readonly string BodyStyleKey = "BodyStyle";
+
+ public static readonly string ListItemTextStyleKey = "ListItemTextStyle";
+
+ public static readonly string ListItemDetailTextStyleKey = "ListItemDetailTextStyle";
+
+ public static readonly string CaptionStyleKey = "CaptionStyle";
+
+ public static readonly Style TitleStyle = new Style(typeof(Label)) { BaseResourceKey = TitleStyleKey };
+
+ public static readonly Style SubtitleStyle = new Style(typeof(Label)) { BaseResourceKey = SubtitleStyleKey };
+
+ public static readonly Style BodyStyle = new Style(typeof(Label)) { BaseResourceKey = BodyStyleKey };
+
+ public static readonly Style ListItemTextStyle = new Style(typeof(Label)) { BaseResourceKey = ListItemTextStyleKey };
+
+ public static readonly Style ListItemDetailTextStyle = new Style(typeof(Label)) { BaseResourceKey = ListItemDetailTextStyleKey };
+
+ public static readonly Style CaptionStyle = new Style(typeof(Label)) { BaseResourceKey = CaptionStyleKey };
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/DeviceInfo.cs b/Xamarin.Forms.Core/DeviceInfo.cs
new file mode 100644
index 00000000..dc83075a
--- /dev/null
+++ b/Xamarin.Forms.Core/DeviceInfo.cs
@@ -0,0 +1,51 @@
+using System;
+using System.ComponentModel;
+using System.Runtime.CompilerServices;
+
+namespace Xamarin.Forms
+{
+ internal abstract class DeviceInfo : INotifyPropertyChanged, IDisposable
+ {
+ DeviceOrientation _currentOrientation;
+ bool _disposed;
+
+ public DeviceOrientation CurrentOrientation
+ {
+ get { return _currentOrientation; }
+ internal set
+ {
+ if (Equals(_currentOrientation, value))
+ return;
+ _currentOrientation = value;
+ OnPropertyChanged();
+ }
+ }
+
+ public abstract Size PixelScreenSize { get; }
+
+ public abstract Size ScaledScreenSize { get; }
+
+ public abstract double ScalingFactor { get; }
+
+ public void Dispose()
+ {
+ Dispose(true);
+ }
+
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ protected virtual void Dispose(bool disposing)
+ {
+ if (_disposed)
+ return;
+ _disposed = true;
+ }
+
+ protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
+ {
+ PropertyChangedEventHandler handler = PropertyChanged;
+ if (handler != null)
+ handler(this, new PropertyChangedEventArgs(propertyName));
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/DeviceOrientation.cs b/Xamarin.Forms.Core/DeviceOrientation.cs
new file mode 100644
index 00000000..53a03f2d
--- /dev/null
+++ b/Xamarin.Forms.Core/DeviceOrientation.cs
@@ -0,0 +1,13 @@
+namespace Xamarin.Forms
+{
+ internal enum DeviceOrientation
+ {
+ Portrait,
+ Landscape,
+ PortraitUp,
+ PortraitDown,
+ LandscapeLeft,
+ LandscapeRight,
+ Other
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/DeviceOrientationExtensions.cs b/Xamarin.Forms.Core/DeviceOrientationExtensions.cs
new file mode 100644
index 00000000..8dbaaa82
--- /dev/null
+++ b/Xamarin.Forms.Core/DeviceOrientationExtensions.cs
@@ -0,0 +1,15 @@
+namespace Xamarin.Forms
+{
+ internal static class DeviceOrientationExtensions
+ {
+ public static bool IsLandscape(this DeviceOrientation orientation)
+ {
+ return orientation == DeviceOrientation.Landscape || orientation == DeviceOrientation.LandscapeLeft || orientation == DeviceOrientation.LandscapeRight;
+ }
+
+ public static bool IsPortrait(this DeviceOrientation orientation)
+ {
+ return orientation == DeviceOrientation.Portrait || orientation == DeviceOrientation.PortraitDown || orientation == DeviceOrientation.PortraitUp;
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/Easing.cs b/Xamarin.Forms.Core/Easing.cs
new file mode 100644
index 00000000..8d64e255
--- /dev/null
+++ b/Xamarin.Forms.Core/Easing.cs
@@ -0,0 +1,98 @@
+//
+// Tweener.cs
+//
+// Author:
+// Jason Smith <jason.smith@xamarin.com>
+//
+// Copyright (c) 2012 Xamarin Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+using System;
+
+namespace Xamarin.Forms
+{
+ public class Easing
+ {
+ public static readonly Easing Linear = new Easing(x => x);
+
+ public static readonly Easing SinOut = new Easing(x => Math.Sin(x * Math.PI * 0.5f));
+ public static readonly Easing SinIn = new Easing(x => 1.0f - Math.Cos(x * Math.PI * 0.5f));
+ public static readonly Easing SinInOut = new Easing(x => -Math.Cos(Math.PI * x) / 2.0f + 0.5f);
+
+ public static readonly Easing CubicIn = new Easing(x => x * x * x);
+ public static readonly Easing CubicOut = new Easing(x => Math.Pow(x - 1.0f, 3.0f) + 1.0f);
+
+ public static readonly Easing CubicInOut = new Easing(x => x < 0.5f ? Math.Pow(x * 2.0f, 3.0f) / 2.0f : (Math.Pow((x - 1) * 2.0f, 3.0f) + 2.0f) / 2.0f);
+
+ public static readonly Easing BounceOut;
+ public static readonly Easing BounceIn;
+
+ public static readonly Easing SpringIn = new Easing(x => x * x * ((1.70158f + 1) * x - 1.70158f));
+ public static readonly Easing SpringOut = new Easing(x => (x - 1) * (x - 1) * ((1.70158f + 1) * (x - 1) + 1.70158f) + 1);
+
+ readonly Func<double, double> _easingFunc;
+
+ static Easing()
+ {
+ BounceOut = new Easing(p =>
+ {
+ if (p < 1 / 2.75f)
+ {
+ return 7.5625f * p * p;
+ }
+ if (p < 2 / 2.75f)
+ {
+ p -= 1.5f / 2.75f;
+
+ return 7.5625f * p * p + .75f;
+ }
+ if (p < 2.5f / 2.75f)
+ {
+ p -= 2.25f / 2.75f;
+
+ return 7.5625f * p * p + .9375f;
+ }
+ p -= 2.625f / 2.75f;
+
+ return 7.5625f * p * p + .984375f;
+ });
+
+ BounceIn = new Easing(p => 1.0f - BounceOut.Ease(1 - p));
+ }
+
+ public Easing(Func<double, double> easingFunc)
+ {
+ if (easingFunc == null)
+ throw new ArgumentNullException("easingFunc");
+
+ _easingFunc = easingFunc;
+ }
+
+ public double Ease(double v)
+ {
+ return _easingFunc(v);
+ }
+
+ public static implicit operator Easing(Func<double, double> func)
+ {
+ return new Easing(func);
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/Editor.cs b/Xamarin.Forms.Core/Editor.cs
new file mode 100644
index 00000000..949c0865
--- /dev/null
+++ b/Xamarin.Forms.Core/Editor.cs
@@ -0,0 +1,67 @@
+using System;
+using Xamarin.Forms.Platform;
+
+namespace Xamarin.Forms
+{
+ [RenderWith(typeof(_EditorRenderer))]
+ public class Editor : InputView, IFontElement
+ {
+ public static readonly BindableProperty TextProperty = BindableProperty.Create("Text", typeof(string), typeof(Editor), null, BindingMode.TwoWay, propertyChanged: (bindable, oldValue, newValue) =>
+ {
+ var editor = (Editor)bindable;
+ if (editor.TextChanged != null)
+ editor.TextChanged(editor, new TextChangedEventArgs((string)oldValue, (string)newValue));
+ });
+
+ public static readonly BindableProperty FontFamilyProperty = BindableProperty.Create("FontFamily", typeof(string), typeof(Editor), default(string));
+
+ public static readonly BindableProperty FontSizeProperty = BindableProperty.Create("FontSize", typeof(double), typeof(Editor), -1.0,
+ defaultValueCreator: bindable => Device.GetNamedSize(NamedSize.Default, (Editor)bindable));
+
+ public static readonly BindableProperty FontAttributesProperty = BindableProperty.Create("FontAttributes", typeof(FontAttributes), typeof(Editor), FontAttributes.None);
+
+ public static readonly BindableProperty TextColorProperty = BindableProperty.Create("TextColor", typeof(Color), typeof(Editor), Color.Default);
+
+ public string Text
+ {
+ get { return (string)GetValue(TextProperty); }
+ set { SetValue(TextProperty, value); }
+ }
+
+ public Color TextColor
+ {
+ get { return (Color)GetValue(TextColorProperty); }
+ set { SetValue(TextColorProperty, value); }
+ }
+
+ public FontAttributes FontAttributes
+ {
+ get { return (FontAttributes)GetValue(FontAttributesProperty); }
+ set { SetValue(FontAttributesProperty, value); }
+ }
+
+ public string FontFamily
+ {
+ get { return (string)GetValue(FontFamilyProperty); }
+ set { SetValue(FontFamilyProperty, value); }
+ }
+
+ [TypeConverter(typeof(FontSizeConverter))]
+ public double FontSize
+ {
+ get { return (double)GetValue(FontSizeProperty); }
+ set { SetValue(FontSizeProperty, value); }
+ }
+
+ public event EventHandler Completed;
+
+ public event EventHandler<TextChangedEventArgs> TextChanged;
+
+ internal void SendCompleted()
+ {
+ EventHandler handler = Completed;
+ if (handler != null)
+ handler(this, EventArgs.Empty);
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/Effect.cs b/Xamarin.Forms.Core/Effect.cs
new file mode 100644
index 00000000..9e269118
--- /dev/null
+++ b/Xamarin.Forms.Core/Effect.cs
@@ -0,0 +1,70 @@
+using System;
+using System.ComponentModel;
+
+namespace Xamarin.Forms
+{
+ public abstract class Effect
+ {
+ internal Effect()
+ {
+ }
+
+ public Element Element { get; internal set; }
+
+ public bool IsAttached { get; private set; }
+
+ public string ResolveId { get; internal set; }
+
+ #region Statics
+
+ public static Effect Resolve(string name)
+ {
+ Type effectType;
+ Effect result = null;
+ if (Registrar.Effects.TryGetValue(name, out effectType))
+ {
+ result = (Effect)Activator.CreateInstance(effectType);
+ }
+
+ if (result == null)
+ result = new NullEffect();
+ result.ResolveId = name;
+ return result;
+ }
+
+ #endregion
+
+ // Received after Control/Container/Element made valid
+ protected abstract void OnAttached();
+
+ // Received after Control/Container made invalid
+ protected abstract void OnDetached();
+
+ internal virtual void ClearEffect()
+ {
+ if (IsAttached)
+ SendDetached();
+ Element = null;
+ }
+
+ internal virtual void SendAttached()
+ {
+ if (IsAttached)
+ return;
+ OnAttached();
+ IsAttached = true;
+ }
+
+ internal virtual void SendDetached()
+ {
+ if (!IsAttached)
+ return;
+ OnDetached();
+ IsAttached = false;
+ }
+
+ internal virtual void SendOnElementPropertyChanged(PropertyChangedEventArgs args)
+ {
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/Element.cs b/Xamarin.Forms.Core/Element.cs
new file mode 100644
index 00000000..a85bb3fb
--- /dev/null
+++ b/Xamarin.Forms.Core/Element.cs
@@ -0,0 +1,584 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Collections.Specialized;
+using System.ComponentModel;
+using System.Runtime.CompilerServices;
+using System.Xml;
+using Xamarin.Forms.Internals;
+
+namespace Xamarin.Forms
+{
+ public abstract class Element : BindableObject, IElement, INameScope, IElementController
+ {
+ internal static readonly ReadOnlyCollection<Element> EmptyChildren = new ReadOnlyCollection<Element>(new Element[0]);
+
+ public static readonly BindableProperty ClassIdProperty = BindableProperty.Create("ClassId", typeof(string), typeof(View), null);
+
+ string _automationId;
+
+ List<Action<object, ResourcesChangedEventArgs>> _changeHandlers;
+
+ List<KeyValuePair<string, BindableProperty>> _dynamicResources;
+
+ IEffectControlProvider _effectControlProvider;
+
+ TrackableCollection<Effect> _effects;
+
+ Guid? _id;
+
+ Element _parentOverride;
+
+ IPlatform _platform;
+
+ string _styleId;
+
+ public string AutomationId
+ {
+ get { return _automationId; }
+ set
+ {
+ if (_automationId != null)
+ throw new InvalidOperationException("AutomationId may only be set one time");
+ _automationId = value;
+ }
+ }
+
+ public string ClassId
+ {
+ get { return (string)GetValue(ClassIdProperty); }
+ set { SetValue(ClassIdProperty, value); }
+ }
+
+ public IList<Effect> Effects
+ {
+ get
+ {
+ if (_effects == null)
+ {
+ _effects = new TrackableCollection<Effect>();
+ _effects.CollectionChanged += EffectsOnCollectionChanged;
+ _effects.Clearing += EffectsOnClearing;
+ }
+ return _effects;
+ }
+ }
+
+ public Guid Id
+ {
+ get
+ {
+ if (!_id.HasValue)
+ _id = Guid.NewGuid();
+ return _id.Value;
+ }
+ }
+
+ [Obsolete("Use Parent")]
+ public VisualElement ParentView
+ {
+ get
+ {
+ Element parent = Parent;
+ while (parent != null)
+ {
+ var parentView = parent as VisualElement;
+ if (parentView != null)
+ return parentView;
+ parent = parent.RealParent;
+ }
+ return null;
+ }
+ }
+
+ public string StyleId
+ {
+ get { return _styleId; }
+ set
+ {
+ if (_styleId == value)
+ return;
+
+ OnPropertyChanging();
+ _styleId = value;
+ OnPropertyChanged();
+ }
+ }
+
+ internal virtual ReadOnlyCollection<Element> LogicalChildren
+ {
+ get { return EmptyChildren; }
+ }
+
+ internal bool Owned { get; set; }
+
+ internal Element ParentOverride
+ {
+ get { return _parentOverride; }
+ set
+ {
+ if (_parentOverride == value)
+ return;
+
+ bool emitChange = Parent != value;
+
+ if (emitChange)
+ OnPropertyChanging(nameof(Parent));
+
+ _parentOverride = value;
+
+ if (emitChange)
+ OnPropertyChanged(nameof(Parent));
+ }
+ }
+
+ internal IPlatform Platform
+ {
+ get
+ {
+ if (_platform == null && RealParent != null)
+ return RealParent.Platform;
+ return _platform;
+ }
+ set
+ {
+ if (_platform == value)
+ return;
+ _platform = value;
+ if (PlatformSet != null)
+ PlatformSet(this, EventArgs.Empty);
+ foreach (Element descendant in Descendants())
+ {
+ descendant._platform = _platform;
+ if (descendant.PlatformSet != null)
+ descendant.PlatformSet(this, EventArgs.Empty);
+ }
+ }
+ }
+
+ // you're not my real dad
+ internal Element RealParent { get; private set; }
+
+ List<KeyValuePair<string, BindableProperty>> DynamicResources
+ {
+ get { return _dynamicResources ?? (_dynamicResources = new List<KeyValuePair<string, BindableProperty>>(4)); }
+ }
+
+ void IElement.AddResourcesChangedListener(Action<object, ResourcesChangedEventArgs> onchanged)
+ {
+ _changeHandlers = _changeHandlers ?? new List<Action<object, ResourcesChangedEventArgs>>(2);
+ _changeHandlers.Add(onchanged);
+ }
+
+ public Element Parent
+ {
+ get { return _parentOverride ?? RealParent; }
+ set
+ {
+ if (RealParent == value)
+ return;
+
+ OnPropertyChanging();
+
+ if (RealParent != null)
+ ((IElement)RealParent).RemoveResourcesChangedListener(OnParentResourcesChanged);
+ RealParent = value;
+ if (RealParent != null)
+ {
+ OnParentResourcesChanged(RealParent.GetMergedResources());
+ ((IElement)RealParent).AddResourcesChangedListener(OnParentResourcesChanged);
+ }
+
+ object context = value != null ? value.BindingContext : null;
+ if (value != null)
+ {
+ value.SetChildInheritedBindingContext(this, context);
+ }
+ else
+ {
+ SetInheritedBindingContext(this, null);
+ }
+
+ OnParentSet();
+
+ if (RealParent != null)
+ {
+ IPlatform platform = RealParent.Platform;
+ if (platform != null)
+ Platform = platform;
+ }
+
+ OnPropertyChanged();
+ }
+ }
+
+ void IElement.RemoveResourcesChangedListener(Action<object, ResourcesChangedEventArgs> onchanged)
+ {
+ if (_changeHandlers == null)
+ return;
+ _changeHandlers.Remove(onchanged);
+ }
+
+ IEffectControlProvider IElementController.EffectControlProvider
+ {
+ get { return _effectControlProvider; }
+ set
+ {
+ if (_effectControlProvider == value)
+ return;
+ if (_effectControlProvider != null && _effects != null)
+ {
+ foreach (Effect effect in _effects)
+ effect?.SendDetached();
+ }
+ _effectControlProvider = value;
+ if (_effectControlProvider != null && _effects != null)
+ {
+ foreach (Effect effect in _effects)
+ {
+ if (effect != null)
+ AttachEffect(effect);
+ }
+ }
+ }
+ }
+
+ void IElementController.SetValueFromRenderer(BindableProperty property, object value)
+ {
+ SetValueCore(property, value);
+ }
+
+ void IElementController.SetValueFromRenderer(BindablePropertyKey property, object value)
+ {
+ SetValueCore(property, value);
+ }
+
+ object INameScope.FindByName(string name)
+ {
+ INameScope namescope = GetNameScope();
+ if (namescope == null)
+ throw new InvalidOperationException("this element is not in a namescope");
+ return namescope.FindByName(name);
+ }
+
+ void INameScope.RegisterName(string name, object scopedElement)
+ {
+ INameScope namescope = GetNameScope();
+ if (namescope == null)
+ throw new InvalidOperationException("this element is not in a namescope");
+ namescope.RegisterName(name, scopedElement);
+ }
+
+ void INameScope.RegisterName(string name, object scopedElement, IXmlLineInfo xmlLineInfo)
+ {
+ INameScope namescope = GetNameScope();
+ if (namescope == null)
+ throw new InvalidOperationException("this element is not in a namescope");
+ namescope.RegisterName(name, scopedElement, xmlLineInfo);
+ }
+
+ void INameScope.UnregisterName(string name)
+ {
+ INameScope namescope = GetNameScope();
+ if (namescope == null)
+ throw new InvalidOperationException("this element is not in a namescope");
+ namescope.UnregisterName(name);
+ }
+
+ public event EventHandler<ElementEventArgs> ChildAdded;
+
+ public event EventHandler<ElementEventArgs> ChildRemoved;
+
+ public event EventHandler<ElementEventArgs> DescendantAdded;
+
+ public event EventHandler<ElementEventArgs> DescendantRemoved;
+
+ public new void RemoveDynamicResource(BindableProperty property)
+ {
+ base.RemoveDynamicResource(property);
+ }
+
+ public new void SetDynamicResource(BindableProperty property, string key)
+ {
+ base.SetDynamicResource(property, key);
+ }
+
+ protected override void OnBindingContextChanged()
+ {
+ var gotBindingContext = false;
+ object bc = null;
+
+ for (var index = 0; index < LogicalChildren.Count; index++)
+ {
+ Element child = LogicalChildren[index];
+
+ if (!gotBindingContext)
+ {
+ bc = BindingContext;
+ gotBindingContext = true;
+ }
+
+ SetChildInheritedBindingContext(child, bc);
+ }
+
+ base.OnBindingContextChanged();
+ }
+
+ protected virtual void OnChildAdded(Element child)
+ {
+ child.Parent = this;
+ if (Platform != null)
+ child.Platform = Platform;
+
+ child.ApplyBindings();
+
+ if (ChildAdded != null)
+ ChildAdded(this, new ElementEventArgs(child));
+
+ OnDescendantAdded(child);
+ foreach (Element element in child.Descendants())
+ OnDescendantAdded(element);
+ }
+
+ protected virtual void OnChildRemoved(Element child)
+ {
+ child.Parent = null;
+
+ if (ChildRemoved != null)
+ ChildRemoved(child, new ElementEventArgs(child));
+
+ OnDescendantRemoved(child);
+ foreach (Element element in child.Descendants())
+ OnDescendantRemoved(element);
+ }
+
+ protected virtual void OnParentSet()
+ {
+ ParentSet?.Invoke(this, EventArgs.Empty);
+ }
+
+ protected override void OnPropertyChanged([CallerMemberName] string propertyName = null)
+ {
+ base.OnPropertyChanged(propertyName);
+
+ if (_effects == null || _effects.Count == 0)
+ return;
+
+ var args = new PropertyChangedEventArgs(propertyName);
+ foreach (Effect effect in _effects)
+ {
+ effect?.SendOnElementPropertyChanged(args);
+ }
+ }
+
+ internal IEnumerable<Element> Descendants()
+ {
+ var queue = new Queue<Element>(16);
+ queue.Enqueue(this);
+
+ while (queue.Count > 0)
+ {
+ ReadOnlyCollection<Element> children = queue.Dequeue().LogicalChildren;
+ for (var i = 0; i < children.Count; i++)
+ {
+ Element child = children[i];
+ yield return child;
+ queue.Enqueue(child);
+ }
+ }
+ }
+
+ internal void OnParentResourcesChanged(object sender, ResourcesChangedEventArgs e)
+ {
+ OnParentResourcesChanged(e.Values);
+ }
+
+ internal virtual void OnParentResourcesChanged(IEnumerable<KeyValuePair<string, object>> values)
+ {
+ OnResourcesChanged(values);
+ }
+
+ internal override void OnRemoveDynamicResource(BindableProperty property)
+ {
+ DynamicResources.RemoveAll(kvp => kvp.Value == property);
+ if (DynamicResources.Count == 0)
+ _dynamicResources = null;
+ base.OnRemoveDynamicResource(property);
+ }
+
+ internal void OnResourcesChanged(object sender, ResourcesChangedEventArgs e)
+ {
+ OnResourcesChanged(e.Values);
+ }
+
+ internal void OnResourcesChanged(IEnumerable<KeyValuePair<string, object>> values)
+ {
+ if (values == null)
+ return;
+ if (_changeHandlers != null)
+ foreach (Action<object, ResourcesChangedEventArgs> handler in _changeHandlers)
+ handler(this, new ResourcesChangedEventArgs(values));
+ if (_dynamicResources == null)
+ return;
+ foreach (KeyValuePair<string, object> value in values)
+ {
+ List<BindableProperty> changedResources = null;
+ foreach (KeyValuePair<string, BindableProperty> dynR in DynamicResources)
+ {
+ if (dynR.Key != value.Key)
+ continue;
+ changedResources = changedResources ?? new List<BindableProperty>();
+ changedResources.Add(dynR.Value);
+ }
+ if (changedResources == null)
+ continue;
+ foreach (BindableProperty changedResource in changedResources)
+ OnResourceChanged(changedResource, value.Value);
+ }
+ }
+
+ internal override void OnSetDynamicResource(BindableProperty property, string key)
+ {
+ base.OnSetDynamicResource(property, key);
+ DynamicResources.Add(new KeyValuePair<string, BindableProperty>(key, property));
+ object value;
+ if (this.TryGetResource(key, out value))
+ OnResourceChanged(property, value);
+ }
+
+ internal event EventHandler ParentSet;
+
+ internal event EventHandler PlatformSet;
+
+ internal virtual void SetChildInheritedBindingContext(Element child, object context)
+ {
+ SetInheritedBindingContext(child, context);
+ }
+
+ internal IEnumerable<Element> VisibleDescendants()
+ {
+ var queue = new Queue<Element>(16);
+ queue.Enqueue(this);
+
+ while (queue.Count > 0)
+ {
+ ReadOnlyCollection<Element> children = queue.Dequeue().LogicalChildren;
+ for (var i = 0; i < children.Count; i++)
+ {
+ var child = children[i] as VisualElement;
+ if (child == null || !child.IsVisible)
+ continue;
+ yield return child;
+ queue.Enqueue(child);
+ }
+ }
+ }
+
+ void AttachEffect(Effect effect)
+ {
+ if (_effectControlProvider == null)
+ return;
+ if (effect.IsAttached)
+ throw new InvalidOperationException("Cannot attach Effect to multiple sources");
+
+ Effect effectToRegister = effect;
+ if (effect is RoutingEffect)
+ effectToRegister = ((RoutingEffect)effect).Inner;
+ _effectControlProvider.RegisterEffect(effectToRegister);
+ effectToRegister.Element = this;
+ effect.SendAttached();
+ }
+
+ void EffectsOnClearing(object sender, EventArgs eventArgs)
+ {
+ foreach (Effect effect in _effects)
+ {
+ effect.ClearEffect();
+ }
+ }
+
+ void EffectsOnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
+ {
+ switch (e.Action)
+ {
+ case NotifyCollectionChangedAction.Add:
+ foreach (Effect effect in e.NewItems)
+ {
+ AttachEffect(effect);
+ }
+ break;
+ case NotifyCollectionChangedAction.Move:
+ break;
+ case NotifyCollectionChangedAction.Remove:
+ foreach (Effect effect in e.OldItems)
+ {
+ effect.ClearEffect();
+ }
+ break;
+ case NotifyCollectionChangedAction.Replace:
+ foreach (Effect effect in e.NewItems)
+ {
+ AttachEffect(effect);
+ }
+ foreach (Effect effect in e.OldItems)
+ {
+ effect.ClearEffect();
+ }
+ break;
+ case NotifyCollectionChangedAction.Reset:
+ if (e.NewItems != null)
+ {
+ foreach (Effect effect in e.NewItems)
+ {
+ AttachEffect(effect);
+ }
+ }
+ if (e.OldItems != null)
+ {
+ foreach (Effect effect in e.OldItems)
+ {
+ effect.ClearEffect();
+ }
+ }
+ break;
+ default:
+ throw new ArgumentOutOfRangeException();
+ }
+ }
+
+ INameScope GetNameScope()
+ {
+ INameScope namescope = NameScope.GetNameScope(this);
+ Element p = RealParent;
+ while (namescope == null && p != null)
+ {
+ namescope = NameScope.GetNameScope(p);
+ p = p.RealParent;
+ }
+ return namescope;
+ }
+
+ void OnDescendantAdded(Element child)
+ {
+ if (DescendantAdded != null)
+ DescendantAdded(this, new ElementEventArgs(child));
+
+ if (RealParent != null)
+ RealParent.OnDescendantAdded(child);
+ }
+
+ void OnDescendantRemoved(Element child)
+ {
+ if (DescendantRemoved != null)
+ DescendantRemoved(this, new ElementEventArgs(child));
+
+ if (RealParent != null)
+ RealParent.OnDescendantRemoved(child);
+ }
+
+ void OnResourceChanged(BindableProperty property, object value)
+ {
+ SetValueCore(property, value, SetValueFlags.ClearOneWayBindings | SetValueFlags.ClearTwoWayBindings);
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/ElementCollection.cs b/Xamarin.Forms.Core/ElementCollection.cs
new file mode 100644
index 00000000..37520ab6
--- /dev/null
+++ b/Xamarin.Forms.Core/ElementCollection.cs
@@ -0,0 +1,11 @@
+using System.Collections.ObjectModel;
+
+namespace Xamarin.Forms
+{
+ internal class ElementCollection<T> : ObservableWrapper<Element, T> where T : Element
+ {
+ public ElementCollection(ObservableCollection<Element> list) : base(list)
+ {
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/ElementEventArgs.cs b/Xamarin.Forms.Core/ElementEventArgs.cs
new file mode 100644
index 00000000..34af4e0e
--- /dev/null
+++ b/Xamarin.Forms.Core/ElementEventArgs.cs
@@ -0,0 +1,17 @@
+using System;
+
+namespace Xamarin.Forms
+{
+ public class ElementEventArgs : EventArgs
+ {
+ public ElementEventArgs(Element element)
+ {
+ if (element == null)
+ throw new ArgumentNullException("element");
+
+ Element = element;
+ }
+
+ public Element Element { get; private set; }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/ElementTemplate.cs b/Xamarin.Forms.Core/ElementTemplate.cs
new file mode 100644
index 00000000..016dee7e
--- /dev/null
+++ b/Xamarin.Forms.Core/ElementTemplate.cs
@@ -0,0 +1,96 @@
+using System;
+using System.Collections.Generic;
+using Xamarin.Forms.Internals;
+
+namespace Xamarin.Forms
+{
+#pragma warning disable 612
+ public class ElementTemplate : IElement, IDataTemplate
+#pragma warning restore 612
+ {
+ List<Action<object, ResourcesChangedEventArgs>> _changeHandlers;
+ Element _parent;
+
+ internal ElementTemplate()
+ {
+ }
+
+ internal ElementTemplate(Type type) : this()
+ {
+ if (type == null)
+ throw new ArgumentNullException("type");
+
+ LoadTemplate = () => Activator.CreateInstance(type);
+ }
+
+ internal ElementTemplate(Func<object> loadTemplate) : this()
+ {
+ if (loadTemplate == null)
+ throw new ArgumentNullException("loadTemplate");
+
+ LoadTemplate = loadTemplate;
+ }
+
+ Func<object> LoadTemplate { get; set; }
+#pragma warning disable 0612
+ Func<object> IDataTemplate.LoadTemplate
+ {
+#pragma warning restore 0612
+ get { return LoadTemplate; }
+ set { LoadTemplate = value; }
+ }
+
+ void IElement.AddResourcesChangedListener(Action<object, ResourcesChangedEventArgs> onchanged)
+ {
+ _changeHandlers = _changeHandlers ?? new List<Action<object, ResourcesChangedEventArgs>>(1);
+ _changeHandlers.Add(onchanged);
+ }
+
+ Element IElement.Parent
+ {
+ get { return _parent; }
+ set
+ {
+ if (_parent == value)
+ return;
+ if (_parent != null)
+ ((IElement)_parent).RemoveResourcesChangedListener(OnResourcesChanged);
+ _parent = value;
+ if (_parent != null)
+ ((IElement)_parent).AddResourcesChangedListener(OnResourcesChanged);
+ }
+ }
+
+ void IElement.RemoveResourcesChangedListener(Action<object, ResourcesChangedEventArgs> onchanged)
+ {
+ if (_changeHandlers == null)
+ return;
+ _changeHandlers.Remove(onchanged);
+ }
+
+ public object CreateContent()
+ {
+ if (LoadTemplate == null)
+ throw new InvalidOperationException("LoadTemplate should not be null");
+ if (this is DataTemplateSelector)
+ throw new InvalidOperationException("Cannot call CreateContent directly on a DataTemplateSelector");
+
+ object item = LoadTemplate();
+ SetupContent(item);
+
+ return item;
+ }
+
+ internal virtual void SetupContent(object item)
+ {
+ }
+
+ void OnResourcesChanged(object sender, ResourcesChangedEventArgs e)
+ {
+ if (_changeHandlers == null)
+ return;
+ foreach (Action<object, ResourcesChangedEventArgs> handler in _changeHandlers)
+ handler(this, e);
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/EmailKeyboard.cs b/Xamarin.Forms.Core/EmailKeyboard.cs
new file mode 100644
index 00000000..1911fd3c
--- /dev/null
+++ b/Xamarin.Forms.Core/EmailKeyboard.cs
@@ -0,0 +1,6 @@
+namespace Xamarin.Forms
+{
+ internal sealed class EmailKeyboard : Keyboard
+ {
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/Entry.cs b/Xamarin.Forms.Core/Entry.cs
new file mode 100644
index 00000000..ef10e963
--- /dev/null
+++ b/Xamarin.Forms.Core/Entry.cs
@@ -0,0 +1,99 @@
+using System;
+using Xamarin.Forms.Platform;
+
+namespace Xamarin.Forms
+{
+ [RenderWith(typeof(_EntryRenderer))]
+ public class Entry : InputView, IFontElement
+ {
+ public static readonly BindableProperty PlaceholderProperty = BindableProperty.Create("Placeholder", typeof(string), typeof(Entry), default(string));
+
+ public static readonly BindableProperty IsPasswordProperty = BindableProperty.Create("IsPassword", typeof(bool), typeof(Entry), default(bool));
+
+ public static readonly BindableProperty TextProperty = BindableProperty.Create("Text", typeof(string), typeof(Entry), null, BindingMode.TwoWay, propertyChanged: OnTextChanged);
+
+ public static readonly BindableProperty TextColorProperty = BindableProperty.Create("TextColor", typeof(Color), typeof(Entry), Color.Default);
+
+ public static readonly BindableProperty HorizontalTextAlignmentProperty = BindableProperty.Create("HorizontalTextAlignment", typeof(TextAlignment), typeof(Entry), TextAlignment.Start);
+
+ public static readonly BindableProperty PlaceholderColorProperty = BindableProperty.Create("PlaceholderColor", typeof(Color), typeof(Entry), Color.Default);
+
+ public static readonly BindableProperty FontFamilyProperty = BindableProperty.Create("FontFamily", typeof(string), typeof(Entry), default(string));
+
+ public static readonly BindableProperty FontSizeProperty = BindableProperty.Create("FontSize", typeof(double), typeof(Entry), -1.0,
+ defaultValueCreator: bindable => Device.GetNamedSize(NamedSize.Default, (Entry)bindable));
+
+ public static readonly BindableProperty FontAttributesProperty = BindableProperty.Create("FontAttributes", typeof(FontAttributes), typeof(Entry), FontAttributes.None);
+
+ public TextAlignment HorizontalTextAlignment
+ {
+ get { return (TextAlignment)GetValue(HorizontalTextAlignmentProperty); }
+ set { SetValue(HorizontalTextAlignmentProperty, value); }
+ }
+
+ public bool IsPassword
+ {
+ get { return (bool)GetValue(IsPasswordProperty); }
+ set { SetValue(IsPasswordProperty, value); }
+ }
+
+ public string Placeholder
+ {
+ get { return (string)GetValue(PlaceholderProperty); }
+ set { SetValue(PlaceholderProperty, value); }
+ }
+
+ public Color PlaceholderColor
+ {
+ get { return (Color)GetValue(PlaceholderColorProperty); }
+ set { SetValue(PlaceholderColorProperty, value); }
+ }
+
+ public string Text
+ {
+ get { return (string)GetValue(TextProperty); }
+ set { SetValue(TextProperty, value); }
+ }
+
+ public Color TextColor
+ {
+ get { return (Color)GetValue(TextColorProperty); }
+ set { SetValue(TextColorProperty, value); }
+ }
+
+ public FontAttributes FontAttributes
+ {
+ get { return (FontAttributes)GetValue(FontAttributesProperty); }
+ set { SetValue(FontAttributesProperty, value); }
+ }
+
+ public string FontFamily
+ {
+ get { return (string)GetValue(FontFamilyProperty); }
+ set { SetValue(FontFamilyProperty, value); }
+ }
+
+ [TypeConverter(typeof(FontSizeConverter))]
+ public double FontSize
+ {
+ get { return (double)GetValue(FontSizeProperty); }
+ set { SetValue(FontSizeProperty, value); }
+ }
+
+ public event EventHandler Completed;
+
+ public event EventHandler<TextChangedEventArgs> TextChanged;
+
+ internal void SendCompleted()
+ {
+ Completed?.Invoke(this, EventArgs.Empty);
+ }
+
+ static void OnTextChanged(BindableObject bindable, object oldValue, object newValue)
+ {
+ var entry = (Entry)bindable;
+
+ entry.TextChanged?.Invoke(entry, new TextChangedEventArgs((string)oldValue, (string)newValue));
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/EnumerableExtensions.cs b/Xamarin.Forms.Core/EnumerableExtensions.cs
new file mode 100644
index 00000000..066e7e91
--- /dev/null
+++ b/Xamarin.Forms.Core/EnumerableExtensions.cs
@@ -0,0 +1,81 @@
+using System;
+using System.Collections.Generic;
+
+namespace Xamarin.Forms
+{
+ internal static class EnumerableExtensions
+ {
+ public static IEnumerable<T> GetGesturesFor<T>(this IEnumerable<IGestureRecognizer> gestures, Func<T, bool> predicate = null) where T : GestureRecognizer
+ {
+ if (gestures == null)
+ yield break;
+
+ if (predicate == null)
+ predicate = x => true;
+
+ foreach (IGestureRecognizer item in gestures)
+ {
+ var gesture = item as T;
+ if (gesture != null && predicate(gesture))
+ {
+ yield return gesture;
+ }
+ }
+ }
+
+ internal static IEnumerable<T> Append<T>(this IEnumerable<T> enumerable, T item)
+ {
+ foreach (T x in enumerable)
+ yield return x;
+
+ yield return item;
+ }
+
+ internal static void ForEach<T>(this IEnumerable<T> enumeration, Action<T> action)
+ {
+ foreach (T item in enumeration)
+ {
+ action(item);
+ }
+ }
+
+ internal static int IndexOf<T>(this IEnumerable<T> enumerable, T item)
+ {
+ if (enumerable == null)
+ throw new ArgumentNullException("enumerable");
+
+ var i = 0;
+ foreach (T element in enumerable)
+ {
+ if (Equals(element, item))
+ return i;
+
+ i++;
+ }
+
+ return -1;
+ }
+
+ internal static int IndexOf<T>(this IEnumerable<T> enumerable, Func<T, bool> predicate)
+ {
+ var i = 0;
+ foreach (T element in enumerable)
+ {
+ if (predicate(element))
+ return i;
+
+ i++;
+ }
+
+ return -1;
+ }
+
+ internal static IEnumerable<T> Prepend<T>(this IEnumerable<T> enumerable, T item)
+ {
+ yield return item;
+
+ foreach (T x in enumerable)
+ yield return x;
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/EventArg.cs b/Xamarin.Forms.Core/EventArg.cs
new file mode 100644
index 00000000..9b9ea0a1
--- /dev/null
+++ b/Xamarin.Forms.Core/EventArg.cs
@@ -0,0 +1,18 @@
+using System;
+
+namespace Xamarin.Forms
+{
+ internal class EventArg<T> : EventArgs
+ {
+ // Property variable
+
+ // Constructor
+ public EventArg(T data)
+ {
+ Data = data;
+ }
+
+ // Property for EventArgs argument
+ public T Data { get; }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/ExportEffectAttribute.cs b/Xamarin.Forms.Core/ExportEffectAttribute.cs
new file mode 100644
index 00000000..35869570
--- /dev/null
+++ b/Xamarin.Forms.Core/ExportEffectAttribute.cs
@@ -0,0 +1,20 @@
+using System;
+
+namespace Xamarin.Forms
+{
+ [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
+ public class ExportEffectAttribute : Attribute
+ {
+ public ExportEffectAttribute(Type effectType, string uniqueName)
+ {
+ if (uniqueName.Contains("."))
+ throw new ArgumentException("uniqueName must not contain a .");
+ Type = effectType;
+ Id = uniqueName;
+ }
+
+ internal string Id { get; private set; }
+
+ internal Type Type { get; private set; }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/ExpressionSearch.cs b/Xamarin.Forms.Core/ExpressionSearch.cs
new file mode 100644
index 00000000..fbbe80a8
--- /dev/null
+++ b/Xamarin.Forms.Core/ExpressionSearch.cs
@@ -0,0 +1,7 @@
+namespace Xamarin.Forms
+{
+ internal abstract class ExpressionSearch
+ {
+ internal static IExpressionSearch Default { get; set; }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/FileAccess.cs b/Xamarin.Forms.Core/FileAccess.cs
new file mode 100644
index 00000000..3636b733
--- /dev/null
+++ b/Xamarin.Forms.Core/FileAccess.cs
@@ -0,0 +1,9 @@
+namespace Xamarin.Forms
+{
+ internal enum FileAccess
+ {
+ Read = 0x00000001,
+ Write = 0x00000002,
+ ReadWrite = Read | Write
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/FileImageSource.cs b/Xamarin.Forms.Core/FileImageSource.cs
new file mode 100644
index 00000000..9e1d1e73
--- /dev/null
+++ b/Xamarin.Forms.Core/FileImageSource.cs
@@ -0,0 +1,38 @@
+using System.Threading.Tasks;
+
+namespace Xamarin.Forms
+{
+ [TypeConverter(typeof(FileImageSourceConverter))]
+ public sealed class FileImageSource : ImageSource
+ {
+ public static readonly BindableProperty FileProperty = BindableProperty.Create("File", typeof(string), typeof(FileImageSource), default(string));
+
+ public string File
+ {
+ get { return (string)GetValue(FileProperty); }
+ set { SetValue(FileProperty, value); }
+ }
+
+ public override Task<bool> Cancel()
+ {
+ return Task.FromResult(false);
+ }
+
+ public static implicit operator FileImageSource(string file)
+ {
+ return (FileImageSource)FromFile(file);
+ }
+
+ public static implicit operator string(FileImageSource file)
+ {
+ return file != null ? file.File : null;
+ }
+
+ protected override void OnPropertyChanged(string propertyName = null)
+ {
+ if (propertyName == FileProperty.PropertyName)
+ OnSourceChanged();
+ base.OnPropertyChanged(propertyName);
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/FileImageSourceConverter.cs b/Xamarin.Forms.Core/FileImageSourceConverter.cs
new file mode 100644
index 00000000..25a0e22e
--- /dev/null
+++ b/Xamarin.Forms.Core/FileImageSourceConverter.cs
@@ -0,0 +1,15 @@
+using System;
+
+namespace Xamarin.Forms
+{
+ public sealed class FileImageSourceConverter : TypeConverter
+ {
+ public override object ConvertFromInvariantString(string value)
+ {
+ if (value != null)
+ return (FileImageSource)ImageSource.FromFile(value);
+
+ throw new InvalidOperationException(string.Format("Cannot convert \"{0}\" into {1}", value, typeof(FileImageSource)));
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/FileMode.cs b/Xamarin.Forms.Core/FileMode.cs
new file mode 100644
index 00000000..31369872
--- /dev/null
+++ b/Xamarin.Forms.Core/FileMode.cs
@@ -0,0 +1,12 @@
+namespace Xamarin.Forms
+{
+ internal enum FileMode
+ {
+ CreateNew = 1,
+ Create = 2,
+ Open = 3,
+ OpenOrCreate = 4,
+ Truncate = 5,
+ Append = 6
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/FileShare.cs b/Xamarin.Forms.Core/FileShare.cs
new file mode 100644
index 00000000..bf9fc717
--- /dev/null
+++ b/Xamarin.Forms.Core/FileShare.cs
@@ -0,0 +1,15 @@
+using System;
+
+namespace Xamarin.Forms
+{
+ [Flags]
+ internal enum FileShare
+ {
+ None = 0,
+ Read = 1,
+ Write = 2,
+ ReadWrite = 3,
+ Delete = 4,
+ Inheritable = 16
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/FocusEventArgs.cs b/Xamarin.Forms.Core/FocusEventArgs.cs
new file mode 100644
index 00000000..304fd823
--- /dev/null
+++ b/Xamarin.Forms.Core/FocusEventArgs.cs
@@ -0,0 +1,20 @@
+using System;
+
+namespace Xamarin.Forms
+{
+ public class FocusEventArgs : EventArgs
+ {
+ public FocusEventArgs(VisualElement visualElement, bool isFocused)
+ {
+ if (visualElement == null)
+ throw new ArgumentNullException("visualElement");
+
+ VisualElement = visualElement;
+ IsFocused = isFocused;
+ }
+
+ public bool IsFocused { get; private set; }
+
+ public VisualElement VisualElement { get; private set; }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/Font.cs b/Xamarin.Forms.Core/Font.cs
new file mode 100644
index 00000000..bb6da4f4
--- /dev/null
+++ b/Xamarin.Forms.Core/Font.cs
@@ -0,0 +1,145 @@
+using System;
+
+namespace Xamarin.Forms
+{
+ [TypeConverter(typeof(FontTypeConverter))]
+ public struct Font
+ {
+ public string FontFamily { get; private set; }
+
+ public double FontSize { get; private set; }
+
+ public NamedSize NamedSize { get; private set; }
+
+ public FontAttributes FontAttributes { get; private set; }
+
+ public bool IsDefault
+ {
+ get { return FontFamily == null && FontSize == 0 && NamedSize == NamedSize.Default && FontAttributes == FontAttributes.None; }
+ }
+
+ public bool UseNamedSize
+ {
+ get { return FontSize <= 0; }
+ }
+
+ public static Font Default
+ {
+ get { return default(Font); }
+ }
+
+ public Font WithSize(double size)
+ {
+ return new Font { FontFamily = FontFamily, FontSize = size, NamedSize = 0, FontAttributes = FontAttributes };
+ }
+
+ public Font WithSize(NamedSize size)
+ {
+ if (size <= 0)
+ throw new ArgumentOutOfRangeException("size");
+
+ return new Font { FontFamily = FontFamily, FontSize = 0, NamedSize = size, FontAttributes = FontAttributes };
+ }
+
+ public Font WithAttributes(FontAttributes fontAttributes)
+ {
+ return new Font { FontFamily = FontFamily, FontSize = FontSize, NamedSize = NamedSize, FontAttributes = fontAttributes };
+ }
+
+ public static Font OfSize(string name, double size)
+ {
+ var result = new Font { FontFamily = name, FontSize = size };
+ return result;
+ }
+
+ public static Font OfSize(string name, NamedSize size)
+ {
+ var result = new Font { FontFamily = name, NamedSize = size };
+ return result;
+ }
+
+ public static Font SystemFontOfSize(double size)
+ {
+ var result = new Font { FontSize = size };
+ return result;
+ }
+
+ public static Font SystemFontOfSize(NamedSize size)
+ {
+ var result = new Font { NamedSize = size };
+ return result;
+ }
+
+ public static Font SystemFontOfSize(double size, FontAttributes attributes)
+ {
+ var result = new Font { FontSize = size, FontAttributes = attributes };
+ return result;
+ }
+
+ public static Font SystemFontOfSize(NamedSize size, FontAttributes attributes)
+ {
+ var result = new Font { NamedSize = size, FontAttributes = attributes };
+ return result;
+ }
+
+ [Obsolete("BoldSystemFontOfSize is obsolete, please use SystemFontOfSize (double, FontAttributes)")]
+ public static Font BoldSystemFontOfSize(double size)
+ {
+ var result = new Font { FontSize = size, FontAttributes = FontAttributes.Bold };
+ return result;
+ }
+
+ [Obsolete("BoldSystemFontOfSize is obsolete, please use SystemFontOfSize (NamedSize, FontAttributes)")]
+ public static Font BoldSystemFontOfSize(NamedSize size)
+ {
+ var result = new Font { NamedSize = size, FontAttributes = FontAttributes.Bold };
+ return result;
+ }
+
+ bool Equals(Font other)
+ {
+ return string.Equals(FontFamily, other.FontFamily) && FontSize.Equals(other.FontSize) && NamedSize == other.NamedSize && FontAttributes == other.FontAttributes;
+ }
+
+ public override bool Equals(object obj)
+ {
+ if (ReferenceEquals(null, obj))
+ {
+ return false;
+ }
+ if (obj.GetType() != GetType())
+ {
+ return false;
+ }
+ return Equals((Font)obj);
+ }
+
+ public override int GetHashCode()
+ {
+ unchecked
+ {
+ int hashCode = FontFamily != null ? FontFamily.GetHashCode() : 0;
+ hashCode = (hashCode * 397) ^ FontSize.GetHashCode();
+ hashCode = (hashCode * 397) ^ NamedSize.GetHashCode();
+ hashCode = (hashCode * 397) ^ FontAttributes.GetHashCode();
+
+ return hashCode;
+ }
+ }
+
+ public static bool operator ==(Font left, Font right)
+ {
+ return left.Equals(right);
+ }
+
+ public static bool operator !=(Font left, Font right)
+ {
+ return !left.Equals(right);
+ }
+
+ public override string ToString()
+ {
+ return string.Format("FontFamily: {0}, FontSize: {1}, NamedSize: {2}, FontAttributes: {3}", FontFamily, FontSize, NamedSize, FontAttributes);
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/FontAttributes.cs b/Xamarin.Forms.Core/FontAttributes.cs
new file mode 100644
index 00000000..872629c5
--- /dev/null
+++ b/Xamarin.Forms.Core/FontAttributes.cs
@@ -0,0 +1,12 @@
+using System;
+
+namespace Xamarin.Forms
+{
+ [Flags]
+ public enum FontAttributes
+ {
+ None = 0,
+ Bold = 1 << 0,
+ Italic = 1 << 1
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/FontSizeConverter.cs b/Xamarin.Forms.Core/FontSizeConverter.cs
new file mode 100644
index 00000000..0d0a8865
--- /dev/null
+++ b/Xamarin.Forms.Core/FontSizeConverter.cs
@@ -0,0 +1,48 @@
+using System;
+using System.Globalization;
+using Xamarin.Forms.Xaml;
+
+namespace Xamarin.Forms
+{
+ public class FontSizeConverter : TypeConverter, IExtendedTypeConverter
+ {
+ [Obsolete("use ConvertFromInvariantString (string, IServiceProvider)")]
+ object IExtendedTypeConverter.ConvertFrom(CultureInfo culture, object value, IServiceProvider serviceProvider)
+ {
+ return ((IExtendedTypeConverter)this).ConvertFromInvariantString(value as string, serviceProvider);
+ }
+
+ object IExtendedTypeConverter.ConvertFromInvariantString(string value, IServiceProvider serviceProvider)
+ {
+ if (value != null)
+ {
+ double size;
+ if (double.TryParse(value, NumberStyles.Number, CultureInfo.InvariantCulture, out size))
+ return size;
+ NamedSize namedSize;
+ if (Enum.TryParse(value, out namedSize))
+ {
+ Type type;
+ var valueTargetProvider = serviceProvider.GetService(typeof(IProvideValueTarget)) as IProvideValueTarget;
+ type = valueTargetProvider != null ? valueTargetProvider.TargetObject.GetType() : typeof(Label);
+ return Device.GetNamedSize(namedSize, type, false);
+ }
+ }
+ throw new InvalidOperationException(string.Format("Cannot convert \"{0}\" into {1}", value, typeof(double)));
+ }
+
+ public override object ConvertFromInvariantString(string value)
+ {
+ if (value != null)
+ {
+ double size;
+ if (double.TryParse(value, NumberStyles.Number, CultureInfo.InvariantCulture, out size))
+ return size;
+ NamedSize namedSize;
+ if (Enum.TryParse(value, out namedSize))
+ return Device.GetNamedSize(namedSize, typeof(Label), false);
+ }
+ throw new InvalidOperationException(string.Format("Cannot convert \"{0}\" into {1}", value, typeof(double)));
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/FontTypeConverter.cs b/Xamarin.Forms.Core/FontTypeConverter.cs
new file mode 100644
index 00000000..464cf4ab
--- /dev/null
+++ b/Xamarin.Forms.Core/FontTypeConverter.cs
@@ -0,0 +1,88 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
+
+namespace Xamarin.Forms
+{
+ public sealed class FontTypeConverter : TypeConverter
+ {
+ public override object ConvertFromInvariantString(string value)
+ {
+ // string should be formatted as "[name],[attributes],[size]" there may be multiple attributes, e.g. "Georgia, Bold, Italic, 42"
+ if (value != null)
+ {
+ // trim because mono implements Enum.Parse incorrectly and fails to trim correctly.
+ List<string> parts = value.Split(',').Select(s => s.Trim()).ToList();
+
+ string name = null;
+ var bold = false;
+ var italic = false;
+ double size = -1;
+ NamedSize namedSize = 0;
+
+ // check if last is a size
+ string last = parts.Last();
+
+ double trySize;
+ NamedSize tryNamedSize;
+ if (double.TryParse(last, NumberStyles.Number, CultureInfo.InvariantCulture, out trySize))
+ {
+ size = trySize;
+ parts.RemoveAt(parts.Count - 1);
+ }
+ else if (Enum.TryParse(last, out tryNamedSize))
+ {
+ namedSize = tryNamedSize;
+ parts.RemoveAt(parts.Count - 1);
+ }
+
+ // check if first is a name
+ foreach (string part in parts)
+ {
+ FontAttributes tryAttibute;
+ if (Enum.TryParse(part, out tryAttibute))
+ {
+ // they did not provide a font name
+ if (tryAttibute == FontAttributes.Bold)
+ bold = true;
+ else if (tryAttibute == FontAttributes.Italic)
+ italic = true;
+ }
+ else
+ {
+ // they may have provided a font name
+ if (name != null)
+ throw new InvalidOperationException(string.Format("Cannot convert \"{0}\" into {1}", value, typeof(Font)));
+
+ name = part;
+ }
+ }
+
+ FontAttributes attributes = 0;
+ if (bold)
+ attributes = attributes | FontAttributes.Bold;
+ if (italic)
+ attributes = attributes | FontAttributes.Italic;
+ if (size == -1 && namedSize == 0)
+ namedSize = NamedSize.Medium;
+
+ if (name != null)
+ {
+ if (size == -1)
+ {
+ return Font.OfSize(name, namedSize).WithAttributes(attributes);
+ }
+ return Font.OfSize(name, size).WithAttributes(attributes);
+ }
+ if (size == -1)
+ {
+ return Font.SystemFontOfSize(namedSize, attributes);
+ }
+ return Font.SystemFontOfSize(size, attributes);
+ }
+
+ throw new InvalidOperationException(string.Format("Cannot convert \"{0}\" into {1}", value, typeof(Font)));
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/FormattedString.cs b/Xamarin.Forms.Core/FormattedString.cs
new file mode 100644
index 00000000..14839ab3
--- /dev/null
+++ b/Xamarin.Forms.Core/FormattedString.cs
@@ -0,0 +1,99 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Collections.Specialized;
+using System.ComponentModel;
+using System.Linq;
+using System.Runtime.CompilerServices;
+
+namespace Xamarin.Forms
+{
+ [ContentProperty("Spans")]
+ public class FormattedString : INotifyPropertyChanged
+ {
+ readonly SpanCollection _spans = new SpanCollection();
+
+ public FormattedString()
+ {
+ _spans.CollectionChanged += OnCollectionChanged;
+ }
+
+ public IList<Span> Spans
+ {
+ get { return _spans; }
+ }
+
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ public static explicit operator string(FormattedString formatted)
+ {
+ return formatted.ToString();
+ }
+
+ public static implicit operator FormattedString(string text)
+ {
+ return new FormattedString { Spans = { new Span { Text = text } } };
+ }
+
+ public override string ToString()
+ {
+ return string.Concat(Spans.Select(span => span.Text));
+ }
+
+ void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
+ {
+ if (e.OldItems != null)
+ {
+ foreach (object item in e.OldItems)
+ {
+ var bo = item as Span;
+ if (bo != null)
+ bo.PropertyChanged -= OnItemPropertyChanged;
+ }
+ }
+
+ if (e.NewItems != null)
+ {
+ foreach (object item in e.NewItems)
+ {
+ var bo = item as Span;
+ if (bo != null)
+ bo.PropertyChanged += OnItemPropertyChanged;
+ }
+ }
+
+ OnPropertyChanged("Spans");
+ }
+
+ void OnItemPropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ OnPropertyChanged("Spans");
+ }
+
+ void OnPropertyChanged([CallerMemberName] string propertyName = null)
+ {
+ PropertyChangedEventHandler handler = PropertyChanged;
+ if (handler != null)
+ handler(this, new PropertyChangedEventArgs(propertyName));
+ }
+
+ class SpanCollection : ObservableCollection<Span>
+ {
+ protected override void InsertItem(int index, Span item)
+ {
+ if (item == null)
+ throw new ArgumentNullException("item");
+
+ base.InsertItem(index, item);
+ }
+
+ protected override void SetItem(int index, Span item)
+ {
+ if (item == null)
+ throw new ArgumentNullException("item");
+
+ base.SetItem(index, item);
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/Frame.cs b/Xamarin.Forms.Core/Frame.cs
new file mode 100644
index 00000000..01f409e4
--- /dev/null
+++ b/Xamarin.Forms.Core/Frame.cs
@@ -0,0 +1,30 @@
+using Xamarin.Forms.Platform;
+
+namespace Xamarin.Forms
+{
+ [ContentProperty("Content")]
+ [RenderWith(typeof(_FrameRenderer))]
+ public class Frame : ContentView
+ {
+ public static readonly BindableProperty OutlineColorProperty = BindableProperty.Create("OutlineColor", typeof(Color), typeof(Frame), Color.Default);
+
+ public static readonly BindableProperty HasShadowProperty = BindableProperty.Create("HasShadow", typeof(bool), typeof(Frame), true);
+
+ public Frame()
+ {
+ Padding = new Size(20, 20);
+ }
+
+ public bool HasShadow
+ {
+ get { return (bool)GetValue(HasShadowProperty); }
+ set { SetValue(HasShadowProperty, value); }
+ }
+
+ public Color OutlineColor
+ {
+ get { return (Color)GetValue(OutlineColorProperty); }
+ set { SetValue(OutlineColorProperty, value); }
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/GestureRecognizer.cs b/Xamarin.Forms.Core/GestureRecognizer.cs
new file mode 100644
index 00000000..f68c996d
--- /dev/null
+++ b/Xamarin.Forms.Core/GestureRecognizer.cs
@@ -0,0 +1,9 @@
+namespace Xamarin.Forms
+{
+ public class GestureRecognizer : Element, IGestureRecognizer
+ {
+ internal GestureRecognizer()
+ {
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/GestureState.cs b/Xamarin.Forms.Core/GestureState.cs
new file mode 100644
index 00000000..a6c110f2
--- /dev/null
+++ b/Xamarin.Forms.Core/GestureState.cs
@@ -0,0 +1,12 @@
+namespace Xamarin.Forms
+{
+ public enum GestureState
+ {
+ Began,
+ Update,
+ Ended,
+ Failed,
+ Cancelled,
+ Possible
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/GestureStatus.cs b/Xamarin.Forms.Core/GestureStatus.cs
new file mode 100644
index 00000000..22f24460
--- /dev/null
+++ b/Xamarin.Forms.Core/GestureStatus.cs
@@ -0,0 +1,10 @@
+namespace Xamarin.Forms
+{
+ public enum GestureStatus
+ {
+ Started = 0,
+ Running = 1,
+ Completed = 2,
+ Canceled = 3
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/Grid.cs b/Xamarin.Forms.Core/Grid.cs
new file mode 100644
index 00000000..442146c2
--- /dev/null
+++ b/Xamarin.Forms.Core/Grid.cs
@@ -0,0 +1,359 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.ComponentModel;
+using System.Linq;
+
+namespace Xamarin.Forms
+{
+ public partial class Grid : Layout<View>
+ {
+ public static readonly BindableProperty RowProperty = BindableProperty.CreateAttached("Row", typeof(int), typeof(Grid), default(int), validateValue: (bindable, value) => (int)value >= 0);
+
+ public static readonly BindableProperty RowSpanProperty = BindableProperty.CreateAttached("RowSpan", typeof(int), typeof(Grid), 1, validateValue: (bindable, value) => (int)value >= 1);
+
+ public static readonly BindableProperty ColumnProperty = BindableProperty.CreateAttached("Column", typeof(int), typeof(Grid), default(int), validateValue: (bindable, value) => (int)value >= 0);
+
+ public static readonly BindableProperty ColumnSpanProperty = BindableProperty.CreateAttached("ColumnSpan", typeof(int), typeof(Grid), 1, validateValue: (bindable, value) => (int)value >= 1);
+
+ public static readonly BindableProperty RowSpacingProperty = BindableProperty.Create("RowSpacing", typeof(double), typeof(Grid), 6d,
+ propertyChanged: (bindable, oldValue, newValue) => ((Grid)bindable).InvalidateMeasure(InvalidationTrigger.MeasureChanged));
+
+ public static readonly BindableProperty ColumnSpacingProperty = BindableProperty.Create("ColumnSpacing", typeof(double), typeof(Grid), 6d,
+ propertyChanged: (bindable, oldValue, newValue) => ((Grid)bindable).InvalidateMeasure(InvalidationTrigger.MeasureChanged));
+
+ public static readonly BindableProperty ColumnDefinitionsProperty = BindableProperty.Create("ColumnDefinitions", typeof(ColumnDefinitionCollection), typeof(Grid), null,
+ validateValue: (bindable, value) => value != null, propertyChanged: (bindable, oldvalue, newvalue) =>
+ {
+ if (oldvalue != null)
+ ((ColumnDefinitionCollection)oldvalue).ItemSizeChanged -= ((Grid)bindable).OnDefinitionChanged;
+ if (newvalue != null)
+ ((ColumnDefinitionCollection)newvalue).ItemSizeChanged += ((Grid)bindable).OnDefinitionChanged;
+ }, defaultValueCreator: bindable =>
+ {
+ var colDef = new ColumnDefinitionCollection();
+ colDef.ItemSizeChanged += ((Grid)bindable).OnDefinitionChanged;
+ return colDef;
+ });
+
+ public static readonly BindableProperty RowDefinitionsProperty = BindableProperty.Create("RowDefinitions", typeof(RowDefinitionCollection), typeof(Grid), null,
+ validateValue: (bindable, value) => value != null, propertyChanged: (bindable, oldvalue, newvalue) =>
+ {
+ if (oldvalue != null)
+ ((RowDefinitionCollection)oldvalue).ItemSizeChanged -= ((Grid)bindable).OnDefinitionChanged;
+ if (newvalue != null)
+ ((RowDefinitionCollection)newvalue).ItemSizeChanged += ((Grid)bindable).OnDefinitionChanged;
+ }, defaultValueCreator: bindable =>
+ {
+ var rowDef = new RowDefinitionCollection();
+ rowDef.ItemSizeChanged += ((Grid)bindable).OnDefinitionChanged;
+ return rowDef;
+ });
+
+ readonly GridElementCollection _children;
+
+ public Grid()
+ {
+ _children = new GridElementCollection(InternalChildren, this) { Parent = this };
+ }
+
+ public new IGridList<View> Children
+ {
+ get { return _children; }
+ }
+
+ public ColumnDefinitionCollection ColumnDefinitions
+ {
+ get { return (ColumnDefinitionCollection)GetValue(ColumnDefinitionsProperty); }
+ set { SetValue(ColumnDefinitionsProperty, value); }
+ }
+
+ public double ColumnSpacing
+ {
+ get { return (double)GetValue(ColumnSpacingProperty); }
+ set { SetValue(ColumnSpacingProperty, value); }
+ }
+
+ public RowDefinitionCollection RowDefinitions
+ {
+ get { return (RowDefinitionCollection)GetValue(RowDefinitionsProperty); }
+ set { SetValue(RowDefinitionsProperty, value); }
+ }
+
+ public double RowSpacing
+ {
+ get { return (double)GetValue(RowSpacingProperty); }
+ set { SetValue(RowSpacingProperty, value); }
+ }
+
+ public static int GetColumn(BindableObject bindable)
+ {
+ return (int)bindable.GetValue(ColumnProperty);
+ }
+
+ public static int GetColumnSpan(BindableObject bindable)
+ {
+ return (int)bindable.GetValue(ColumnSpanProperty);
+ }
+
+ public static int GetRow(BindableObject bindable)
+ {
+ return (int)bindable.GetValue(RowProperty);
+ }
+
+ public static int GetRowSpan(BindableObject bindable)
+ {
+ return (int)bindable.GetValue(RowSpanProperty);
+ }
+
+ public static void SetColumn(BindableObject bindable, int value)
+ {
+ bindable.SetValue(ColumnProperty, value);
+ }
+
+ public static void SetColumnSpan(BindableObject bindable, int value)
+ {
+ bindable.SetValue(ColumnSpanProperty, value);
+ }
+
+ public static void SetRow(BindableObject bindable, int value)
+ {
+ bindable.SetValue(RowProperty, value);
+ }
+
+ public static void SetRowSpan(BindableObject bindable, int value)
+ {
+ bindable.SetValue(RowSpanProperty, value);
+ }
+
+ protected override void OnAdded(View view)
+ {
+ base.OnAdded(view);
+ view.PropertyChanged += OnItemPropertyChanged;
+ }
+
+ protected override void OnBindingContextChanged()
+ {
+ UpdateInheritedBindingContexts();
+ base.OnBindingContextChanged();
+ }
+
+ protected override void OnRemoved(View view)
+ {
+ base.OnRemoved(view);
+ view.PropertyChanged -= OnItemPropertyChanged;
+ }
+
+ internal override void ComputeConstraintForView(View view)
+ {
+ LayoutOptions vOptions = view.VerticalOptions;
+ LayoutOptions hOptions = view.HorizontalOptions;
+
+ var result = LayoutConstraint.None;
+
+ if (_rows == null || _columns == null)
+ EnsureRowsColumnsInitialized();
+
+ if (vOptions.Alignment == LayoutAlignment.Fill)
+ {
+ int row = GetRow(view);
+ int rowSpan = GetRowSpan(view);
+ List<RowDefinition> rowDefinitions = _rows;
+
+ var canFix = true;
+
+ for (int i = row; i < row + rowSpan && i < rowDefinitions.Count; i++)
+ {
+ GridLength height = rowDefinitions[i].Height;
+ if (height.IsAuto)
+ {
+ canFix = false;
+ break;
+ }
+ if ((Constraint & LayoutConstraint.VerticallyFixed) == 0 && height.IsStar)
+ {
+ canFix = false;
+ break;
+ }
+ }
+
+ if (canFix)
+ result |= LayoutConstraint.VerticallyFixed;
+ }
+
+ if (hOptions.Alignment == LayoutAlignment.Fill)
+ {
+ int col = GetColumn(view);
+ int colSpan = GetColumnSpan(view);
+ List<ColumnDefinition> columnDefinitions = _columns;
+
+ var canFix = true;
+
+ for (int i = col; i < col + colSpan && i < columnDefinitions.Count; i++)
+ {
+ GridLength width = columnDefinitions[i].Width;
+ if (width.IsAuto)
+ {
+ canFix = false;
+ break;
+ }
+ if ((Constraint & LayoutConstraint.HorizontallyFixed) == 0 && width.IsStar)
+ {
+ canFix = false;
+ break;
+ }
+ }
+
+ if (canFix)
+ result |= LayoutConstraint.HorizontallyFixed;
+ }
+
+ view.ComputedConstraint = result;
+ }
+
+ internal override void InvalidateMeasure(InvalidationTrigger trigger)
+ {
+ base.InvalidateMeasure(trigger);
+ _columns = null;
+ _rows = null;
+ }
+
+ void OnDefinitionChanged(object sender, EventArgs args)
+ {
+ ComputeConstrainsForChildren();
+ UpdateInheritedBindingContexts();
+ InvalidateLayout();
+ }
+
+ void OnItemPropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ if (e.PropertyName == ColumnProperty.PropertyName || e.PropertyName == ColumnSpanProperty.PropertyName || e.PropertyName == RowProperty.PropertyName ||
+ e.PropertyName == RowSpanProperty.PropertyName)
+ {
+ var child = sender as View;
+ if (child != null)
+ {
+ ComputeConstraintForView(child);
+ }
+
+ InvalidateLayout();
+ }
+ }
+
+ void UpdateInheritedBindingContexts()
+ {
+ object bindingContext = BindingContext;
+ RowDefinitionCollection rowDefs = RowDefinitions;
+ if (rowDefs != null)
+ {
+ for (var i = 0; i < rowDefs.Count; i++)
+ {
+ RowDefinition rowdef = rowDefs[i];
+ SetInheritedBindingContext(rowdef, bindingContext);
+ }
+ }
+
+ ColumnDefinitionCollection colDefs = ColumnDefinitions;
+ if (colDefs != null)
+ {
+ for (var i = 0; i < colDefs.Count; i++)
+ {
+ ColumnDefinition coldef = colDefs[i];
+ SetInheritedBindingContext(coldef, bindingContext);
+ }
+ }
+ }
+
+ public interface IGridList<T> : IList<T> where T : View
+ {
+ void Add(View view, int left, int top);
+ void Add(View view, int left, int right, int top, int bottom);
+ void AddHorizontal(IEnumerable<View> views);
+ void AddHorizontal(View view);
+ void AddVertical(IEnumerable<View> views);
+ void AddVertical(View view);
+ }
+
+ class GridElementCollection : ElementCollection<View>, IGridList<View>
+ {
+ public GridElementCollection(ObservableCollection<Element> inner, Grid parent) : base(inner)
+ {
+ Parent = parent;
+ }
+
+ internal Grid Parent { get; set; }
+
+ public void Add(View view, int left, int top)
+ {
+ if (left < 0)
+ throw new ArgumentOutOfRangeException("left");
+ if (top < 0)
+ throw new ArgumentOutOfRangeException("top");
+ Add(view, left, left + 1, top, top + 1);
+ }
+
+ public void Add(View view, int left, int right, int top, int bottom)
+ {
+ if (left < 0)
+ throw new ArgumentOutOfRangeException("left");
+ if (top < 0)
+ throw new ArgumentOutOfRangeException("top");
+ if (left >= right)
+ throw new ArgumentOutOfRangeException("right");
+ if (top >= bottom)
+ throw new ArgumentOutOfRangeException("bottom");
+ if (view == null)
+ throw new ArgumentNullException("view");
+
+ SetRow(view, top);
+ SetRowSpan(view, bottom - top);
+ SetColumn(view, left);
+ SetColumnSpan(view, right - left);
+
+ Add(view);
+ }
+
+ public void AddHorizontal(IEnumerable<View> views)
+ {
+ if (views == null)
+ throw new ArgumentNullException("views");
+
+ views.ForEach(AddHorizontal);
+ }
+
+ public void AddHorizontal(View view)
+ {
+ if (view == null)
+ throw new ArgumentNullException("view");
+
+ int lastRow = this.Any() ? this.Max(w => GetRow(w) + GetRowSpan(w) - 1) : -1;
+ lastRow = Math.Max(lastRow, Parent.RowDefinitions.Count - 1);
+ int lastCol = this.Any() ? this.Max(w => GetColumn(w) + GetColumnSpan(w) - 1) : -1;
+ lastCol = Math.Max(lastCol, Parent.ColumnDefinitions.Count - 1);
+
+ Add(view, lastCol + 1, lastCol + 2, 0, Math.Max(1, lastRow));
+ }
+
+ public void AddVertical(IEnumerable<View> views)
+ {
+ if (views == null)
+ throw new ArgumentNullException("views");
+
+ views.ForEach(AddVertical);
+ }
+
+ public void AddVertical(View view)
+ {
+ if (view == null)
+ throw new ArgumentNullException("view");
+
+ int lastRow = this.Any() ? this.Max(w => GetRow(w) + GetRowSpan(w) - 1) : -1;
+ lastRow = Math.Max(lastRow, Parent.RowDefinitions.Count - 1);
+ int lastCol = this.Any() ? this.Max(w => GetColumn(w) + GetColumnSpan(w) - 1) : -1;
+ lastCol = Math.Max(lastCol, Parent.ColumnDefinitions.Count - 1);
+
+ Add(view, 0, Math.Max(1, lastCol), lastRow + 1, lastRow + 2);
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/GridCalc.cs b/Xamarin.Forms.Core/GridCalc.cs
new file mode 100644
index 00000000..778e188e
--- /dev/null
+++ b/Xamarin.Forms.Core/GridCalc.cs
@@ -0,0 +1,698 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Xamarin.Forms
+{
+ public partial class Grid
+ {
+ List<ColumnDefinition> _columns;
+ List<RowDefinition> _rows;
+
+ protected override void LayoutChildren(double x, double y, double width, double height)
+ {
+ if (!InternalChildren.Any())
+ return;
+
+ MeasureGrid(width, height);
+
+ // Make copies so if InvalidateMeasure is called during layout we dont crash when these get nulled
+ List<ColumnDefinition> columnsCopy = _columns;
+ List<RowDefinition> rowsCopy = _rows;
+
+ for (var index = 0; index < InternalChildren.Count; index++)
+ {
+ var child = (View)InternalChildren[index];
+ if (!child.IsVisible)
+ continue;
+ int r = GetRow(child);
+ int c = GetColumn(child);
+ int rs = GetRowSpan(child);
+ int cs = GetColumnSpan(child);
+
+ double posx = x + c * ColumnSpacing;
+ for (var i = 0; i < c; i++)
+ posx += columnsCopy[i].ActualWidth;
+ double posy = y + r * RowSpacing;
+ for (var i = 0; i < r; i++)
+ posy += rowsCopy[i].ActualHeight;
+
+ double w = columnsCopy[c].ActualWidth;
+ for (var i = 1; i < cs; i++)
+ w += ColumnSpacing + columnsCopy[c + i].ActualWidth;
+ double h = rowsCopy[r].ActualHeight;
+ for (var i = 1; i < rs; i++)
+ h += RowSpacing + rowsCopy[r + i].ActualHeight;
+
+ // in the future we can might maybe optimize by passing the already calculated size request
+ LayoutChildIntoBoundingRegion(child, new Rectangle(posx, posy, w, h));
+ }
+ }
+
+ [Obsolete("Use OnMeasure")]
+ protected override SizeRequest OnSizeRequest(double widthConstraint, double heightConstraint)
+ {
+ if (!InternalChildren.Any())
+ return new SizeRequest(new Size(0, 0));
+
+ MeasureGrid(widthConstraint, heightConstraint, true);
+
+ double columnWidthSum = 0;
+ double nonStarColumnWidthSum = 0;
+ for (var index = 0; index < _columns.Count; index++)
+ {
+ ColumnDefinition c = _columns[index];
+ columnWidthSum += c.ActualWidth;
+ if (!c.Width.IsStar)
+ nonStarColumnWidthSum += c.ActualWidth;
+ }
+ double rowHeightSum = 0;
+ double nonStarRowHeightSum = 0;
+ for (var index = 0; index < _rows.Count; index++)
+ {
+ RowDefinition r = _rows[index];
+ rowHeightSum += r.ActualHeight;
+ if (!r.Height.IsStar)
+ nonStarRowHeightSum += r.ActualHeight;
+ }
+
+ var request = new Size(columnWidthSum + (_columns.Count - 1) * ColumnSpacing, rowHeightSum + (_rows.Count - 1) * RowSpacing);
+ var minimum = new Size(nonStarColumnWidthSum + (_columns.Count - 1) * ColumnSpacing, nonStarRowHeightSum + (_rows.Count - 1) * RowSpacing);
+
+ var result = new SizeRequest(request, minimum);
+ return result;
+ }
+
+ void AssignAbsoluteCells()
+ {
+ for (var index = 0; index < _rows.Count; index++)
+ {
+ RowDefinition row = _rows[index];
+ if (row.Height.IsAbsolute)
+ row.ActualHeight = row.Height.Value;
+ }
+
+ for (var index = 0; index < _columns.Count; index++)
+ {
+ ColumnDefinition col = _columns[index];
+ if (col.Width.IsAbsolute)
+ col.ActualWidth = col.Width.Value;
+ }
+ }
+
+ void CalculateAutoCells(double width, double height)
+ {
+ // this require multiple passes. First process the 1-span, then 2, 3, ...
+ // And this needs to be run twice, just in case a lower-span column can be determined by a larger span
+ for (var iteration = 0; iteration < 2; iteration++)
+ {
+ for (var rowspan = 1; rowspan <= _rows.Count; rowspan++)
+ {
+ for (var i = 0; i < _rows.Count; i++)
+ {
+ RowDefinition row = _rows[i];
+ if (!row.Height.IsAuto)
+ continue;
+ if (row.ActualHeight >= 0) // if Actual is already set (by a smaller span), skip till pass 3
+ continue;
+
+ double actualHeight = row.ActualHeight;
+ double minimumHeight = row.MinimumHeight;
+ for (var index = 0; index < InternalChildren.Count; index++)
+ {
+ var child = (View)InternalChildren[index];
+ if (!child.IsVisible || GetRowSpan(child) != rowspan || !IsInRow(child, i) || NumberOfUnsetRowHeight(child) > 1)
+ continue;
+ double assignedWidth = GetAssignedColumnWidth(child);
+ double assignedHeight = GetAssignedRowHeight(child);
+ double widthRequest = assignedWidth + GetUnassignedWidth(width);
+ double heightRequest = double.IsPositiveInfinity(height) ? double.PositiveInfinity : assignedHeight + GetUnassignedHeight(height);
+
+ SizeRequest sizeRequest = child.Measure(widthRequest, heightRequest, MeasureFlags.IncludeMargins);
+ actualHeight = Math.Max(actualHeight, sizeRequest.Request.Height - assignedHeight - RowSpacing * (GetRowSpan(child) - 1));
+ minimumHeight = Math.Max(minimumHeight, sizeRequest.Minimum.Height - assignedHeight - RowSpacing * (GetRowSpan(child) - 1));
+ }
+ if (actualHeight >= 0)
+ row.ActualHeight = actualHeight;
+ if (minimumHeight >= 0)
+ row.MinimumHeight = minimumHeight;
+ }
+ }
+
+ for (var colspan = 1; colspan <= _columns.Count; colspan++)
+ {
+ for (var i = 0; i < _columns.Count; i++)
+ {
+ ColumnDefinition col = _columns[i];
+ if (!col.Width.IsAuto)
+ continue;
+ if (col.ActualWidth >= 0) // if Actual is already set (by a smaller span), skip
+ continue;
+
+ double actualWidth = col.ActualWidth;
+ double minimumWidth = col.MinimumWidth;
+ for (var index = 0; index < InternalChildren.Count; index++)
+ {
+ var child = (View)InternalChildren[index];
+ if (!child.IsVisible || GetColumnSpan(child) != colspan || !IsInColumn(child, i) || NumberOfUnsetColumnWidth(child) > 1)
+ continue;
+ double assignedWidth = GetAssignedColumnWidth(child);
+ double assignedHeight = GetAssignedRowHeight(child);
+ double widthRequest = double.IsPositiveInfinity(width) ? double.PositiveInfinity : assignedWidth + GetUnassignedWidth(width);
+ double heightRequest = assignedHeight + GetUnassignedHeight(height);
+
+ SizeRequest sizeRequest = child.Measure(widthRequest, heightRequest, MeasureFlags.IncludeMargins);
+ actualWidth = Math.Max(actualWidth, sizeRequest.Request.Width - assignedWidth - (GetColumnSpan(child) - 1) * ColumnSpacing);
+ minimumWidth = Math.Max(minimumWidth, sizeRequest.Minimum.Width - assignedWidth - (GetColumnSpan(child) - 1) * ColumnSpacing);
+ }
+ if (actualWidth >= 0)
+ col.ActualWidth = actualWidth;
+ if (minimumWidth >= 0)
+ col.MinimumWidth = actualWidth;
+ }
+ }
+ }
+ }
+
+ void CalculateStarCells(double width, double height, double totalStarsWidth, double totalStarsHeight)
+ {
+ double starColWidth = GetUnassignedWidth(width) / totalStarsWidth;
+ double starRowHeight = GetUnassignedHeight(height) / totalStarsHeight;
+
+ for (var index = 0; index < _columns.Count; index++)
+ {
+ ColumnDefinition col = _columns[index];
+ if (col.Width.IsStar)
+ col.ActualWidth = col.Width.Value * starColWidth;
+ }
+
+ for (var index = 0; index < _rows.Count; index++)
+ {
+ RowDefinition row = _rows[index];
+ if (row.Height.IsStar)
+ row.ActualHeight = row.Height.Value * starRowHeight;
+ }
+ }
+
+ void ContractColumnsIfNeeded(double width, Func<ColumnDefinition, bool> predicate)
+ {
+ double columnWidthSum = 0;
+ for (var index = 0; index < _columns.Count; index++)
+ {
+ ColumnDefinition c = _columns[index];
+ columnWidthSum += c.ActualWidth;
+ }
+
+ double rowHeightSum = 0;
+ for (var index = 0; index < _rows.Count; index++)
+ {
+ RowDefinition r = _rows[index];
+ rowHeightSum += r.ActualHeight;
+ }
+
+ var request = new Size(columnWidthSum + (_columns.Count - 1) * ColumnSpacing, rowHeightSum + (_rows.Count - 1) * RowSpacing);
+ if (request.Width > width)
+ {
+ double contractionSpace = 0;
+ for (var index = 0; index < _columns.Count; index++)
+ {
+ ColumnDefinition c = _columns[index];
+ if (predicate(c))
+ contractionSpace += c.ActualWidth - c.MinimumWidth;
+ }
+ if (contractionSpace > 0)
+ {
+ // contract as much as we can but no more
+ double contractionNeeded = Math.Min(contractionSpace, Math.Max(request.Width - width, 0));
+ double contractFactor = contractionNeeded / contractionSpace;
+ for (var index = 0; index < _columns.Count; index++)
+ {
+ ColumnDefinition col = _columns[index];
+ if (!predicate(col))
+ continue;
+ double availableSpace = col.ActualWidth - col.MinimumWidth;
+ double contraction = availableSpace * contractFactor;
+ col.ActualWidth -= contraction;
+ contractionNeeded -= contraction;
+ }
+ }
+ }
+ }
+
+ void ContractRowsIfNeeded(double height, Func<RowDefinition, bool> predicate)
+ {
+ double columnSum = 0;
+ for (var index = 0; index < _columns.Count; index++)
+ {
+ ColumnDefinition c = _columns[index];
+ columnSum += Math.Max(0, c.ActualWidth);
+ }
+ double rowSum = 0;
+ for (var index = 0; index < _rows.Count; index++)
+ {
+ RowDefinition r = _rows[index];
+ rowSum += Math.Max(0, r.ActualHeight);
+ }
+
+ var request = new Size(columnSum + (_columns.Count - 1) * ColumnSpacing, rowSum + (_rows.Count - 1) * RowSpacing);
+ if (request.Height <= height)
+ return;
+ double contractionSpace = 0;
+ for (var index = 0; index < _rows.Count; index++)
+ {
+ RowDefinition r = _rows[index];
+ if (predicate(r))
+ contractionSpace += r.ActualHeight - r.MinimumHeight;
+ }
+ if (!(contractionSpace > 0))
+ return;
+ // contract as much as we can but no more
+ double contractionNeeded = Math.Min(contractionSpace, Math.Max(request.Height - height, 0));
+ double contractFactor = contractionNeeded / contractionSpace;
+ for (var index = 0; index < _rows.Count; index++)
+ {
+ RowDefinition row = _rows[index];
+ if (!predicate(row))
+ continue;
+ double availableSpace = row.ActualHeight - row.MinimumHeight;
+ double contraction = availableSpace * contractFactor;
+ row.ActualHeight -= contraction;
+ contractionNeeded -= contraction;
+ }
+ }
+
+ void EnsureRowsColumnsInitialized()
+ {
+ _columns = ColumnDefinitions == null ? new List<ColumnDefinition>() : ColumnDefinitions.ToList();
+ _rows = RowDefinitions == null ? new List<RowDefinition>() : RowDefinitions.ToList();
+
+ int lastRow = -1;
+ for (var index = 0; index < InternalChildren.Count; index++)
+ {
+ Element w = InternalChildren[index];
+ lastRow = Math.Max(lastRow, GetRow(w) + GetRowSpan(w) - 1);
+ }
+ lastRow = Math.Max(lastRow, RowDefinitions.Count - 1);
+
+ int lastCol = -1;
+ for (var index = 0; index < InternalChildren.Count; index++)
+ {
+ Element w = InternalChildren[index];
+ lastCol = Math.Max(lastCol, GetColumn(w) + GetColumnSpan(w) - 1);
+ }
+ lastCol = Math.Max(lastCol, ColumnDefinitions.Count - 1);
+
+ while (_columns.Count <= lastCol)
+ _columns.Add(new ColumnDefinition());
+ while (_rows.Count <= lastRow)
+ _rows.Add(new RowDefinition());
+
+ for (var index = 0; index < _columns.Count; index++)
+ {
+ ColumnDefinition col = _columns[index];
+ col.ActualWidth = -1;
+ }
+ for (var index = 0; index < _rows.Count; index++)
+ {
+ RowDefinition row = _rows[index];
+ row.ActualHeight = -1;
+ }
+ }
+
+ void ExpandLastAutoColumnIfNeeded(double width, bool expandToRequest)
+ {
+ for (var index = 0; index < InternalChildren.Count; index++)
+ {
+ Element element = InternalChildren[index];
+ var child = (View)element;
+ if (!child.IsVisible)
+ continue;
+
+ ColumnDefinition col = GetLastAutoColumn(child);
+ if (col == null)
+ continue;
+
+ double assignedWidth = GetAssignedColumnWidth(child);
+ double w = double.IsPositiveInfinity(width) ? double.PositiveInfinity : assignedWidth + GetUnassignedWidth(width);
+ SizeRequest sizeRequest = child.Measure(w, GetAssignedRowHeight(child), MeasureFlags.IncludeMargins);
+ double requiredWidth = expandToRequest ? sizeRequest.Request.Width : sizeRequest.Minimum.Width;
+ double deltaWidth = requiredWidth - assignedWidth - (GetColumnSpan(child) - 1) * ColumnSpacing;
+ if (deltaWidth > 0)
+ {
+ col.ActualWidth += deltaWidth;
+ }
+ }
+ }
+
+ void ExpandLastAutoRowIfNeeded(double height, bool expandToRequest)
+ {
+ for (var index = 0; index < InternalChildren.Count; index++)
+ {
+ Element element = InternalChildren[index];
+ var child = (View)element;
+ if (!child.IsVisible)
+ continue;
+
+ RowDefinition row = GetLastAutoRow(child);
+ if (row == null)
+ continue;
+
+ double assignedHeight = GetAssignedRowHeight(child);
+ double h = double.IsPositiveInfinity(height) ? double.PositiveInfinity : assignedHeight + GetUnassignedHeight(height);
+ SizeRequest sizeRequest = child.Measure(GetAssignedColumnWidth(child), h, MeasureFlags.IncludeMargins);
+ double requiredHeight = expandToRequest ? sizeRequest.Request.Height : sizeRequest.Minimum.Height;
+ double deltaHeight = requiredHeight - assignedHeight - (GetRowSpan(child) - 1) * RowSpacing;
+ if (deltaHeight > 0)
+ {
+ row.ActualHeight += deltaHeight;
+ }
+ }
+ }
+
+ void MeasureAndContractStarredColumns(double width, double height, double totalStarsWidth)
+ {
+ double starColWidth;
+ starColWidth = MeasuredStarredColumns();
+
+ if (!double.IsPositiveInfinity(width) && double.IsPositiveInfinity(height))
+ {
+ // re-zero columns so GetUnassignedWidth returns correctly
+ for (var index = 0; index < _columns.Count; index++)
+ {
+ ColumnDefinition col = _columns[index];
+ if (col.Width.IsStar)
+ col.ActualWidth = 0;
+ }
+
+ starColWidth = Math.Max(starColWidth, GetUnassignedWidth(width) / totalStarsWidth);
+ }
+
+ for (var index = 0; index < _columns.Count; index++)
+ {
+ ColumnDefinition col = _columns[index];
+ if (col.Width.IsStar)
+ col.ActualWidth = col.Width.Value * starColWidth;
+ }
+
+ ContractColumnsIfNeeded(width, c => c.Width.IsStar);
+ }
+
+ void MeasureAndContractStarredRows(double width, double height, double totalStarsHeight)
+ {
+ double starRowHeight;
+ starRowHeight = MeasureStarredRows();
+
+ if (!double.IsPositiveInfinity(height) && double.IsPositiveInfinity(width))
+ {
+ for (var index = 0; index < _rows.Count; index++)
+ {
+ RowDefinition row = _rows[index];
+ if (row.Height.IsStar)
+ row.ActualHeight = 0;
+ }
+
+ starRowHeight = Math.Max(starRowHeight, GetUnassignedHeight(height) / totalStarsHeight);
+ }
+
+ for (var index = 0; index < _rows.Count; index++)
+ {
+ RowDefinition row = _rows[index];
+ if (row.Height.IsStar)
+ row.ActualHeight = row.Height.Value * starRowHeight;
+ }
+
+ ContractRowsIfNeeded(height, r => r.Height.IsStar);
+ }
+
+ double MeasuredStarredColumns()
+ {
+ double starColWidth;
+ for (var iteration = 0; iteration < 2; iteration++)
+ {
+ for (var colspan = 1; colspan <= _columns.Count; colspan++)
+ {
+ for (var i = 0; i < _columns.Count; i++)
+ {
+ ColumnDefinition col = _columns[i];
+ if (!col.Width.IsStar)
+ continue;
+ if (col.ActualWidth >= 0) // if Actual is already set (by a smaller span), skip
+ continue;
+
+ double actualWidth = col.ActualWidth;
+ double minimumWidth = col.MinimumWidth;
+ for (var index = 0; index < InternalChildren.Count; index++)
+ {
+ var child = (View)InternalChildren[index];
+ if (!child.IsVisible || GetColumnSpan(child) != colspan || !IsInColumn(child, i) || NumberOfUnsetColumnWidth(child) > 1)
+ continue;
+ double assignedWidth = GetAssignedColumnWidth(child);
+
+ SizeRequest sizeRequest = child.Measure(double.PositiveInfinity, double.PositiveInfinity, MeasureFlags.IncludeMargins);
+ actualWidth = Math.Max(actualWidth, sizeRequest.Request.Width - assignedWidth - (GetColumnSpan(child) - 1) * ColumnSpacing);
+ minimumWidth = Math.Max(minimumWidth, sizeRequest.Minimum.Width - assignedWidth - (GetColumnSpan(child) - 1) * ColumnSpacing);
+ }
+ if (actualWidth >= 0)
+ col.ActualWidth = actualWidth;
+
+ if (minimumWidth >= 0)
+ col.MinimumWidth = minimumWidth;
+ }
+ }
+ }
+
+ //Measure the stars
+ starColWidth = 1;
+ for (var index = 0; index < _columns.Count; index++)
+ {
+ ColumnDefinition col = _columns[index];
+ if (!col.Width.IsStar)
+ continue;
+ starColWidth = Math.Max(starColWidth, col.ActualWidth / col.Width.Value);
+ }
+
+ return starColWidth;
+ }
+
+ void MeasureGrid(double width, double height, bool requestSize = false)
+ {
+ EnsureRowsColumnsInitialized();
+
+ AssignAbsoluteCells();
+
+ CalculateAutoCells(width, height);
+
+ if (!requestSize)
+ {
+ ContractColumnsIfNeeded(width, c => c.Width.IsAuto);
+ ContractRowsIfNeeded(height, r => r.Height.IsAuto);
+ }
+
+ double totalStarsHeight = 0;
+ for (var index = 0; index < _rows.Count; index++)
+ {
+ RowDefinition row = _rows[index];
+ if (row.Height.IsStar)
+ totalStarsHeight += row.Height.Value;
+ }
+
+ double totalStarsWidth = 0;
+ for (var index = 0; index < _columns.Count; index++)
+ {
+ ColumnDefinition col = _columns[index];
+ if (col.Width.IsStar)
+ totalStarsWidth += col.Width.Value;
+ }
+
+ if (requestSize)
+ {
+ MeasureAndContractStarredColumns(width, height, totalStarsWidth);
+ MeasureAndContractStarredRows(width, height, totalStarsHeight);
+ }
+ else
+ {
+ CalculateStarCells(width, height, totalStarsWidth, totalStarsHeight);
+ }
+
+ ZeroUnassignedCells();
+
+ ExpandLastAutoRowIfNeeded(height, requestSize);
+ ExpandLastAutoColumnIfNeeded(width, requestSize);
+ }
+
+ double MeasureStarredRows()
+ {
+ double starRowHeight;
+ for (var iteration = 0; iteration < 2; iteration++)
+ {
+ for (var rowspan = 1; rowspan <= _rows.Count; rowspan++)
+ {
+ for (var i = 0; i < _rows.Count; i++)
+ {
+ RowDefinition row = _rows[i];
+ if (!row.Height.IsStar)
+ continue;
+ if (row.ActualHeight >= 0) // if Actual is already set (by a smaller span), skip till pass 3
+ continue;
+
+ double actualHeight = row.ActualHeight;
+ double minimumHeight = row.MinimumHeight;
+ for (var index = 0; index < InternalChildren.Count; index++)
+ {
+ var child = (View)InternalChildren[index];
+ if (!child.IsVisible || GetRowSpan(child) != rowspan || !IsInRow(child, i) || NumberOfUnsetRowHeight(child) > 1)
+ continue;
+ double assignedHeight = GetAssignedRowHeight(child);
+ double assignedWidth = GetAssignedColumnWidth(child);
+
+ SizeRequest sizeRequest = child.Measure(assignedWidth, double.PositiveInfinity, MeasureFlags.IncludeMargins);
+ actualHeight = Math.Max(actualHeight, sizeRequest.Request.Height - assignedHeight - RowSpacing * (GetRowSpan(child) - 1));
+ minimumHeight = Math.Max(minimumHeight, sizeRequest.Minimum.Height - assignedHeight - RowSpacing * (GetRowSpan(child) - 1));
+ }
+ if (actualHeight >= 0)
+ row.ActualHeight = actualHeight;
+
+ if (minimumHeight >= 0)
+ row.MinimumHeight = minimumHeight;
+ }
+ }
+ }
+
+ // 3. Star columns:
+
+ //Measure the stars
+ starRowHeight = 1;
+ for (var index = 0; index < _rows.Count; index++)
+ {
+ RowDefinition row = _rows[index];
+ if (!row.Height.IsStar)
+ continue;
+ starRowHeight = Math.Max(starRowHeight, row.ActualHeight / row.Height.Value);
+ }
+
+ return starRowHeight;
+ }
+
+ void ZeroUnassignedCells()
+ {
+ for (var index = 0; index < _columns.Count; index++)
+ {
+ ColumnDefinition col = _columns[index];
+ if (col.ActualWidth < 0)
+ col.ActualWidth = 0;
+ }
+ for (var index = 0; index < _rows.Count; index++)
+ {
+ RowDefinition row = _rows[index];
+ if (row.ActualHeight < 0)
+ row.ActualHeight = 0;
+ }
+ }
+
+ #region Helpers
+
+ static bool IsInColumn(BindableObject child, int column)
+ {
+ int childColumn = GetColumn(child);
+ int span = GetColumnSpan(child);
+ return childColumn <= column && column < childColumn + span;
+ }
+
+ static bool IsInRow(BindableObject child, int row)
+ {
+ int childRow = GetRow(child);
+ int span = GetRowSpan(child);
+ return childRow <= row && row < childRow + span;
+ }
+
+ int NumberOfUnsetColumnWidth(BindableObject child)
+ {
+ var n = 0;
+ int index = GetColumn(child);
+ int span = GetColumnSpan(child);
+ for (int i = index; i < index + span; i++)
+ if (_columns[i].ActualWidth <= 0)
+ n++;
+ return n;
+ }
+
+ int NumberOfUnsetRowHeight(BindableObject child)
+ {
+ var n = 0;
+ int index = GetRow(child);
+ int span = GetRowSpan(child);
+ for (int i = index; i < index + span; i++)
+ if (_rows[i].ActualHeight <= 0)
+ n++;
+ return n;
+ }
+
+ double GetAssignedColumnWidth(BindableObject child)
+ {
+ var actual = 0d;
+ int index = GetColumn(child);
+ int span = GetColumnSpan(child);
+ for (int i = index; i < index + span; i++)
+ if (_columns[i].ActualWidth >= 0)
+ actual += _columns[i].ActualWidth;
+ return actual;
+ }
+
+ double GetAssignedRowHeight(BindableObject child)
+ {
+ var actual = 0d;
+ int index = GetRow(child);
+ int span = GetRowSpan(child);
+ for (int i = index; i < index + span; i++)
+ if (_rows[i].ActualHeight >= 0)
+ actual += _rows[i].ActualHeight;
+ return actual;
+ }
+
+ ColumnDefinition GetLastAutoColumn(BindableObject child)
+ {
+ int index = GetColumn(child);
+ int span = GetColumnSpan(child);
+ for (int i = index + span - 1; i >= index; i--)
+ if (_columns[i].Width.IsAuto)
+ return _columns[i];
+ return null;
+ }
+
+ RowDefinition GetLastAutoRow(BindableObject child)
+ {
+ int index = GetRow(child);
+ int span = GetRowSpan(child);
+ for (int i = index + span - 1; i >= index; i--)
+ if (_rows[i].Height.IsAuto)
+ return _rows[i];
+ return null;
+ }
+
+ double GetUnassignedHeight(double heightRequest)
+ {
+ double assigned = (_rows.Count - 1) * RowSpacing;
+ for (var i = 0; i < _rows.Count; i++)
+ {
+ double actual = _rows[i].ActualHeight;
+ if (actual >= 0)
+ assigned += actual;
+ }
+ return heightRequest - assigned;
+ }
+
+ double GetUnassignedWidth(double widthRequest)
+ {
+ double assigned = (_columns.Count - 1) * ColumnSpacing;
+ for (var i = 0; i < _columns.Count; i++)
+ {
+ double actual = _columns[i].ActualWidth;
+ if (actual >= 0)
+ assigned += actual;
+ }
+ return widthRequest - assigned;
+ }
+
+ #endregion
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/GridLength.cs b/Xamarin.Forms.Core/GridLength.cs
new file mode 100644
index 00000000..5d569d08
--- /dev/null
+++ b/Xamarin.Forms.Core/GridLength.cs
@@ -0,0 +1,74 @@
+using System;
+using System.Diagnostics;
+
+namespace Xamarin.Forms
+{
+ [TypeConverter(typeof(GridLengthTypeConverter))]
+ [DebuggerDisplay("{Value}.{GridUnitType}")]
+ public struct GridLength
+ {
+ public static GridLength Auto
+ {
+ get { return new GridLength(1, GridUnitType.Auto); }
+ }
+
+ public double Value { get; }
+
+ public GridUnitType GridUnitType { get; }
+
+ public bool IsAbsolute
+ {
+ get { return GridUnitType == GridUnitType.Absolute; }
+ }
+
+ public bool IsAuto
+ {
+ get { return GridUnitType == GridUnitType.Auto; }
+ }
+
+ public bool IsStar
+ {
+ get { return GridUnitType == GridUnitType.Star; }
+ }
+
+ public GridLength(double value) : this(value, GridUnitType.Absolute)
+ {
+ }
+
+ public GridLength(double value, GridUnitType type)
+ {
+ if (value < 0 || double.IsNaN(value))
+ throw new ArgumentException("value is less than 0 or is not a number", "value");
+ if ((int)type < (int)GridUnitType.Absolute || (int)type > (int)GridUnitType.Auto)
+ throw new ArgumentException("type is not a valid GridUnitType", "type");
+
+ Value = value;
+ GridUnitType = type;
+ }
+
+ public override bool Equals(object obj)
+ {
+ return obj != null && obj is GridLength && Equals((GridLength)obj);
+ }
+
+ bool Equals(GridLength other)
+ {
+ return GridUnitType == other.GridUnitType && Math.Abs(Value - other.Value) < double.Epsilon;
+ }
+
+ public override int GetHashCode()
+ {
+ return GridUnitType.GetHashCode() * 397 ^ Value.GetHashCode();
+ }
+
+ public static implicit operator GridLength(double absoluteValue)
+ {
+ return new GridLength(absoluteValue);
+ }
+
+ public override string ToString()
+ {
+ return string.Format("{0}.{1}", Value, GridUnitType);
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/GridLengthTypeConverter.cs b/Xamarin.Forms.Core/GridLengthTypeConverter.cs
new file mode 100644
index 00000000..7c58b417
--- /dev/null
+++ b/Xamarin.Forms.Core/GridLengthTypeConverter.cs
@@ -0,0 +1,27 @@
+using System;
+using System.Globalization;
+
+namespace Xamarin.Forms
+{
+ public class GridLengthTypeConverter : TypeConverter
+ {
+ public override object ConvertFromInvariantString(string value)
+ {
+ if (value == null)
+ return null;
+
+ double length;
+ value = value.Trim();
+ if (string.Compare(value, "auto", StringComparison.OrdinalIgnoreCase) == 0)
+ return GridLength.Auto;
+ if (string.Compare(value, "*", StringComparison.OrdinalIgnoreCase) == 0)
+ return new GridLength(1, GridUnitType.Star);
+ if (value.EndsWith("*", StringComparison.Ordinal) && double.TryParse(value.Substring(0, value.Length - 1), NumberStyles.Number, CultureInfo.InvariantCulture, out length))
+ return new GridLength(length, GridUnitType.Star);
+ if (double.TryParse(value, NumberStyles.Number, CultureInfo.InvariantCulture, out length))
+ return new GridLength(length);
+
+ throw new FormatException();
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/GridUnitType.cs b/Xamarin.Forms.Core/GridUnitType.cs
new file mode 100644
index 00000000..372c1804
--- /dev/null
+++ b/Xamarin.Forms.Core/GridUnitType.cs
@@ -0,0 +1,9 @@
+namespace Xamarin.Forms
+{
+ public enum GridUnitType
+ {
+ Absolute,
+ Star,
+ Auto
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/HandlerAttribute.cs b/Xamarin.Forms.Core/HandlerAttribute.cs
new file mode 100644
index 00000000..b9dc9ecb
--- /dev/null
+++ b/Xamarin.Forms.Core/HandlerAttribute.cs
@@ -0,0 +1,23 @@
+using System;
+
+namespace Xamarin.Forms
+{
+ [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
+ public abstract class HandlerAttribute : Attribute
+ {
+ protected HandlerAttribute(Type handler, Type target)
+ {
+ TargetType = target;
+ HandlerType = handler;
+ }
+
+ internal Type HandlerType { get; private set; }
+
+ internal Type TargetType { get; private set; }
+
+ public virtual bool ShouldRegister()
+ {
+ return true;
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/HtmlWebViewSource.cs b/Xamarin.Forms.Core/HtmlWebViewSource.cs
new file mode 100644
index 00000000..157e5054
--- /dev/null
+++ b/Xamarin.Forms.Core/HtmlWebViewSource.cs
@@ -0,0 +1,28 @@
+namespace Xamarin.Forms
+{
+ public class HtmlWebViewSource : WebViewSource
+ {
+ public static readonly BindableProperty HtmlProperty = BindableProperty.Create("Html", typeof(string), typeof(HtmlWebViewSource), default(string),
+ propertyChanged: (bindable, oldvalue, newvalue) => ((HtmlWebViewSource)bindable).OnSourceChanged());
+
+ public static readonly BindableProperty BaseUrlProperty = BindableProperty.Create("BaseUrl", typeof(string), typeof(HtmlWebViewSource), default(string),
+ propertyChanged: (bindable, oldvalue, newvalue) => ((HtmlWebViewSource)bindable).OnSourceChanged());
+
+ public string BaseUrl
+ {
+ get { return (string)GetValue(BaseUrlProperty); }
+ set { SetValue(BaseUrlProperty, value); }
+ }
+
+ public string Html
+ {
+ get { return (string)GetValue(HtmlProperty); }
+ set { SetValue(HtmlProperty, value); }
+ }
+
+ internal override void Load(IWebViewRenderer renderer)
+ {
+ renderer.LoadHtml(Html, BaseUrl);
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/IAnimatable.cs b/Xamarin.Forms.Core/IAnimatable.cs
new file mode 100644
index 00000000..2e57c2e5
--- /dev/null
+++ b/Xamarin.Forms.Core/IAnimatable.cs
@@ -0,0 +1,34 @@
+//
+// IAnimatable.cs
+//
+// Author:
+// Jason Smith <jason.smith@xamarin.com>
+//
+// Copyright (c) 2012 Xamarin Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+namespace Xamarin.Forms
+{
+ public interface IAnimatable
+ {
+ void BatchBegin();
+ void BatchCommit();
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/IApplicationController.cs b/Xamarin.Forms.Core/IApplicationController.cs
new file mode 100644
index 00000000..201032f9
--- /dev/null
+++ b/Xamarin.Forms.Core/IApplicationController.cs
@@ -0,0 +1,6 @@
+namespace Xamarin.Forms
+{
+ public interface IApplicationController
+ {
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/IButtonController.cs b/Xamarin.Forms.Core/IButtonController.cs
new file mode 100644
index 00000000..2fa1737f
--- /dev/null
+++ b/Xamarin.Forms.Core/IButtonController.cs
@@ -0,0 +1,7 @@
+namespace Xamarin.Forms
+{
+ internal interface IButtonController : IViewController
+ {
+ void SendClicked();
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/ICarouselViewController.cs b/Xamarin.Forms.Core/ICarouselViewController.cs
new file mode 100644
index 00000000..f59bc3d3
--- /dev/null
+++ b/Xamarin.Forms.Core/ICarouselViewController.cs
@@ -0,0 +1,10 @@
+namespace Xamarin.Forms
+{
+ public interface ICarouselViewController : IItemViewController
+ {
+ void SendPositionAppearing(int position);
+ void SendPositionDisappearing(int position);
+ void SendSelectedItemChanged(object item);
+ void SendSelectedPositionChanged(int position);
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/IControlTemplated.cs b/Xamarin.Forms.Core/IControlTemplated.cs
new file mode 100644
index 00000000..f5798c2e
--- /dev/null
+++ b/Xamarin.Forms.Core/IControlTemplated.cs
@@ -0,0 +1,11 @@
+using System.Collections.Generic;
+
+namespace Xamarin.Forms
+{
+ internal interface IControlTemplated
+ {
+ ControlTemplate ControlTemplate { get; set; }
+
+ IList<Element> InternalChildren { get; }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/IDefinition.cs b/Xamarin.Forms.Core/IDefinition.cs
new file mode 100644
index 00000000..6971d611
--- /dev/null
+++ b/Xamarin.Forms.Core/IDefinition.cs
@@ -0,0 +1,9 @@
+using System;
+
+namespace Xamarin.Forms
+{
+ public interface IDefinition
+ {
+ event EventHandler SizeChanged;
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/IDeserializer.cs b/Xamarin.Forms.Core/IDeserializer.cs
new file mode 100644
index 00000000..60478e1b
--- /dev/null
+++ b/Xamarin.Forms.Core/IDeserializer.cs
@@ -0,0 +1,11 @@
+using System.Collections.Generic;
+using System.Threading.Tasks;
+
+namespace Xamarin.Forms
+{
+ internal interface IDeserializer
+ {
+ Task<IDictionary<string, object>> DeserializePropertiesAsync();
+ Task SerializePropertiesAsync(IDictionary<string, object> properties);
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/IEffectControlProvider.cs b/Xamarin.Forms.Core/IEffectControlProvider.cs
new file mode 100644
index 00000000..f1882f5f
--- /dev/null
+++ b/Xamarin.Forms.Core/IEffectControlProvider.cs
@@ -0,0 +1,7 @@
+namespace Xamarin.Forms
+{
+ public interface IEffectControlProvider
+ {
+ void RegisterEffect(Effect effect);
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/IElement.cs b/Xamarin.Forms.Core/IElement.cs
new file mode 100644
index 00000000..ee302b5f
--- /dev/null
+++ b/Xamarin.Forms.Core/IElement.cs
@@ -0,0 +1,13 @@
+using System;
+
+namespace Xamarin.Forms
+{
+ internal interface IElement
+ {
+ Element Parent { get; set; }
+
+ //Use these 2 instead of an event to avoid cloning way too much multicastdelegates on mono
+ void AddResourcesChangedListener(Action<object, ResourcesChangedEventArgs> onchanged);
+ void RemoveResourcesChangedListener(Action<object, ResourcesChangedEventArgs> onchanged);
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/IElementController.cs b/Xamarin.Forms.Core/IElementController.cs
new file mode 100644
index 00000000..4e1ab10d
--- /dev/null
+++ b/Xamarin.Forms.Core/IElementController.cs
@@ -0,0 +1,10 @@
+namespace Xamarin.Forms
+{
+ public interface IElementController
+ {
+ IEffectControlProvider EffectControlProvider { get; set; }
+
+ void SetValueFromRenderer(BindableProperty property, object value);
+ void SetValueFromRenderer(BindablePropertyKey propertyKey, object value);
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/IExpressionSearch.cs b/Xamarin.Forms.Core/IExpressionSearch.cs
new file mode 100644
index 00000000..e5d4c26f
--- /dev/null
+++ b/Xamarin.Forms.Core/IExpressionSearch.cs
@@ -0,0 +1,10 @@
+using System.Collections.Generic;
+using System.Linq.Expressions;
+
+namespace Xamarin.Forms
+{
+ internal interface IExpressionSearch
+ {
+ List<T> FindObjects<T>(Expression expression) where T : class;
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/IExtendedTypeConverter.cs b/Xamarin.Forms.Core/IExtendedTypeConverter.cs
new file mode 100644
index 00000000..e3b16b52
--- /dev/null
+++ b/Xamarin.Forms.Core/IExtendedTypeConverter.cs
@@ -0,0 +1,13 @@
+using System;
+using System.Globalization;
+
+namespace Xamarin.Forms
+{
+ public interface IExtendedTypeConverter
+ {
+ [Obsolete("use ConvertFromInvariantString (string, IServiceProvider)")]
+ object ConvertFrom(CultureInfo culture, object value, IServiceProvider serviceProvider);
+
+ object ConvertFromInvariantString(string value, IServiceProvider serviceProvider);
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/IFontElement.cs b/Xamarin.Forms.Core/IFontElement.cs
new file mode 100644
index 00000000..f1f3a36f
--- /dev/null
+++ b/Xamarin.Forms.Core/IFontElement.cs
@@ -0,0 +1,11 @@
+namespace Xamarin.Forms
+{
+ internal interface IFontElement
+ {
+ FontAttributes FontAttributes { get; }
+
+ string FontFamily { get; }
+
+ double FontSize { get; }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/IGestureRecognizer.cs b/Xamarin.Forms.Core/IGestureRecognizer.cs
new file mode 100644
index 00000000..4e361c0a
--- /dev/null
+++ b/Xamarin.Forms.Core/IGestureRecognizer.cs
@@ -0,0 +1,8 @@
+using System.ComponentModel;
+
+namespace Xamarin.Forms
+{
+ public interface IGestureRecognizer : INotifyPropertyChanged
+ {
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/IIsolatedStorageFile.cs b/Xamarin.Forms.Core/IIsolatedStorageFile.cs
new file mode 100644
index 00000000..50899023
--- /dev/null
+++ b/Xamarin.Forms.Core/IIsolatedStorageFile.cs
@@ -0,0 +1,18 @@
+using System;
+using System.IO;
+using System.Threading.Tasks;
+
+namespace Xamarin.Forms
+{
+ internal interface IIsolatedStorageFile
+ {
+ Task CreateDirectoryAsync(string path);
+ Task<bool> GetDirectoryExistsAsync(string path);
+ Task<bool> GetFileExistsAsync(string path);
+
+ Task<DateTimeOffset> GetLastWriteTimeAsync(string path);
+
+ Task<Stream> OpenFileAsync(string path, FileMode mode, FileAccess access);
+ Task<Stream> OpenFileAsync(string path, FileMode mode, FileAccess access, FileShare share);
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/IItemViewController.cs b/Xamarin.Forms.Core/IItemViewController.cs
new file mode 100644
index 00000000..79f711e9
--- /dev/null
+++ b/Xamarin.Forms.Core/IItemViewController.cs
@@ -0,0 +1,10 @@
+namespace Xamarin.Forms
+{
+ public interface IItemViewController
+ {
+ void BindView(View view, object item);
+ View CreateView(object itemType);
+ object GetItem(int index);
+ object GetItemType(object item);
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/IItemsView.cs b/Xamarin.Forms.Core/IItemsView.cs
new file mode 100644
index 00000000..7f968769
--- /dev/null
+++ b/Xamarin.Forms.Core/IItemsView.cs
@@ -0,0 +1,9 @@
+namespace Xamarin.Forms
+{
+ internal interface IItemsView<T> where T : BindableObject
+ {
+ T CreateDefault(object item);
+ void SetupContent(T content, int index);
+ void UnhookContent(T content);
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/ILayout.cs b/Xamarin.Forms.Core/ILayout.cs
new file mode 100644
index 00000000..781111a8
--- /dev/null
+++ b/Xamarin.Forms.Core/ILayout.cs
@@ -0,0 +1,9 @@
+using System;
+
+namespace Xamarin.Forms
+{
+ public interface ILayout
+ {
+ event EventHandler LayoutChanged;
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/ILayoutController.cs b/Xamarin.Forms.Core/ILayoutController.cs
new file mode 100644
index 00000000..56bff7e0
--- /dev/null
+++ b/Xamarin.Forms.Core/ILayoutController.cs
@@ -0,0 +1,9 @@
+using System.Collections.Generic;
+
+namespace Xamarin.Forms
+{
+ public interface ILayoutController
+ {
+ IReadOnlyList<Element> Children { get; }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/IListViewController.cs b/Xamarin.Forms.Core/IListViewController.cs
new file mode 100644
index 00000000..078f13b7
--- /dev/null
+++ b/Xamarin.Forms.Core/IListViewController.cs
@@ -0,0 +1,15 @@
+namespace Xamarin.Forms
+{
+ internal interface IListViewController : IViewController
+ {
+ Element FooterElement { get; }
+
+ Element HeaderElement { get; }
+
+ bool RefreshAllowed { get; }
+
+ void SendCellAppearing(Cell cell);
+ void SendCellDisappearing(Cell cell);
+ void SendRefreshing();
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/IMarkupExtension.cs b/Xamarin.Forms.Core/IMarkupExtension.cs
new file mode 100644
index 00000000..24a435f9
--- /dev/null
+++ b/Xamarin.Forms.Core/IMarkupExtension.cs
@@ -0,0 +1,14 @@
+using System;
+
+namespace Xamarin.Forms.Xaml
+{
+ public interface IMarkupExtension<out T> : IMarkupExtension
+ {
+ new T ProvideValue(IServiceProvider serviceProvider);
+ }
+
+ public interface IMarkupExtension
+ {
+ object ProvideValue(IServiceProvider serviceProvider);
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/INavigation.cs b/Xamarin.Forms.Core/INavigation.cs
new file mode 100644
index 00000000..6ae5959d
--- /dev/null
+++ b/Xamarin.Forms.Core/INavigation.cs
@@ -0,0 +1,28 @@
+using System.Collections.Generic;
+using System.Threading.Tasks;
+
+namespace Xamarin.Forms
+{
+ public interface INavigation
+ {
+ IReadOnlyList<Page> ModalStack { get; }
+
+ IReadOnlyList<Page> NavigationStack { get; }
+
+ void InsertPageBefore(Page page, Page before);
+ Task<Page> PopAsync();
+ Task<Page> PopAsync(bool animated);
+ Task<Page> PopModalAsync();
+ Task<Page> PopModalAsync(bool animated);
+ Task PopToRootAsync();
+ Task PopToRootAsync(bool animated);
+
+ Task PushAsync(Page page);
+
+ Task PushAsync(Page page, bool animated);
+ Task PushModalAsync(Page page);
+ Task PushModalAsync(Page page, bool animated);
+
+ void RemovePage(Page page);
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/IOpenGlViewController.cs b/Xamarin.Forms.Core/IOpenGlViewController.cs
new file mode 100644
index 00000000..d89c5fef
--- /dev/null
+++ b/Xamarin.Forms.Core/IOpenGlViewController.cs
@@ -0,0 +1,9 @@
+using System;
+
+namespace Xamarin.Forms
+{
+ public interface IOpenGlViewController : IViewController
+ {
+ event EventHandler DisplayRequested;
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/IPageContainer.cs b/Xamarin.Forms.Core/IPageContainer.cs
new file mode 100644
index 00000000..9582e5e2
--- /dev/null
+++ b/Xamarin.Forms.Core/IPageContainer.cs
@@ -0,0 +1,7 @@
+namespace Xamarin.Forms
+{
+ public interface IPageContainer<out T> where T : Page
+ {
+ T CurrentPage { get; }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/IPanGestureController.cs b/Xamarin.Forms.Core/IPanGestureController.cs
new file mode 100644
index 00000000..96283092
--- /dev/null
+++ b/Xamarin.Forms.Core/IPanGestureController.cs
@@ -0,0 +1,13 @@
+namespace Xamarin.Forms
+{
+ internal interface IPanGestureController
+ {
+ void SendPan(Element sender, double totalX, double totalY, int gestureId);
+
+ void SendPanCanceled(Element sender, int gestureId);
+
+ void SendPanCompleted(Element sender, int gestureId);
+
+ void SendPanStarted(Element sender, int gestureId);
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/IPinchGestureController.cs b/Xamarin.Forms.Core/IPinchGestureController.cs
new file mode 100644
index 00000000..9848fa74
--- /dev/null
+++ b/Xamarin.Forms.Core/IPinchGestureController.cs
@@ -0,0 +1,15 @@
+namespace Xamarin.Forms
+{
+ internal interface IPinchGestureController
+ {
+ bool IsPinching { get; set; }
+
+ void SendPinch(Element sender, double scale, Point currentScalePoint);
+
+ void SendPinchCanceled(Element sender);
+
+ void SendPinchEnded(Element sender);
+
+ void SendPinchStarted(Element sender, Point intialScalePoint);
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/IPlatform.cs b/Xamarin.Forms.Core/IPlatform.cs
new file mode 100644
index 00000000..507abcfe
--- /dev/null
+++ b/Xamarin.Forms.Core/IPlatform.cs
@@ -0,0 +1,7 @@
+namespace Xamarin.Forms
+{
+ internal interface IPlatform
+ {
+ SizeRequest GetNativeSize(VisualElement view, double widthConstraint, double heightConstraint);
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/IPlatformServices.cs b/Xamarin.Forms.Core/IPlatformServices.cs
new file mode 100644
index 00000000..e4a3ce0b
--- /dev/null
+++ b/Xamarin.Forms.Core/IPlatformServices.cs
@@ -0,0 +1,36 @@
+using System;
+using System.IO;
+using System.Reflection;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Xamarin.Forms
+{
+ internal interface IPlatformServices
+ {
+ bool IsInvokeRequired { get; }
+
+ void BeginInvokeOnMainThread(Action action);
+
+ //this will go once Timer is included in Pcl profiles
+ ITimer CreateTimer(Action<object> callback);
+ ITimer CreateTimer(Action<object> callback, object state, int dueTime, int period);
+ ITimer CreateTimer(Action<object> callback, object state, long dueTime, long period);
+ ITimer CreateTimer(Action<object> callback, object state, TimeSpan dueTime, TimeSpan period);
+ ITimer CreateTimer(Action<object> callback, object state, uint dueTime, uint period);
+
+ Assembly[] GetAssemblies();
+
+ string GetMD5Hash(string input);
+
+ double GetNamedSize(NamedSize size, Type targetElementType, bool useOldSizes);
+
+ Task<Stream> GetStreamAsync(Uri uri, CancellationToken cancellationToken);
+
+ IIsolatedStorageFile GetUserStoreForApplication();
+
+ void OpenUriAction(Uri uri);
+
+ void StartTimer(TimeSpan interval, Func<bool> callback);
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/IProvideParentValues.cs b/Xamarin.Forms.Core/IProvideParentValues.cs
new file mode 100644
index 00000000..c07c606f
--- /dev/null
+++ b/Xamarin.Forms.Core/IProvideParentValues.cs
@@ -0,0 +1,9 @@
+using System.Collections.Generic;
+
+namespace Xamarin.Forms.Xaml
+{
+ internal interface IProvideParentValues : IProvideValueTarget
+ {
+ IEnumerable<object> ParentObjects { get; }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/IProvideValueTarget.cs b/Xamarin.Forms.Core/IProvideValueTarget.cs
new file mode 100644
index 00000000..b8324b5e
--- /dev/null
+++ b/Xamarin.Forms.Core/IProvideValueTarget.cs
@@ -0,0 +1,9 @@
+namespace Xamarin.Forms.Xaml
+{
+ public interface IProvideValueTarget
+ {
+ object TargetObject { get; }
+
+ object TargetProperty { get; }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/IRegisterable.cs b/Xamarin.Forms.Core/IRegisterable.cs
new file mode 100644
index 00000000..995ebbce
--- /dev/null
+++ b/Xamarin.Forms.Core/IRegisterable.cs
@@ -0,0 +1,6 @@
+namespace Xamarin.Forms
+{
+ public interface IRegisterable
+ {
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/IResourceDictionary.cs b/Xamarin.Forms.Core/IResourceDictionary.cs
new file mode 100644
index 00000000..5fccb9d9
--- /dev/null
+++ b/Xamarin.Forms.Core/IResourceDictionary.cs
@@ -0,0 +1,12 @@
+using System;
+using System.Collections.Generic;
+
+namespace Xamarin.Forms
+{
+ internal interface IResourceDictionary : IEnumerable<KeyValuePair<string, object>>
+ {
+ bool TryGetValue(string key, out object value);
+
+ event EventHandler<ResourcesChangedEventArgs> ValuesChanged;
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/IResourcesProvider.cs b/Xamarin.Forms.Core/IResourcesProvider.cs
new file mode 100644
index 00000000..e06ef743
--- /dev/null
+++ b/Xamarin.Forms.Core/IResourcesProvider.cs
@@ -0,0 +1,7 @@
+namespace Xamarin.Forms
+{
+ internal interface IResourcesProvider
+ {
+ ResourceDictionary Resources { get; set; }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/IRootObjectProvider.cs b/Xamarin.Forms.Core/IRootObjectProvider.cs
new file mode 100644
index 00000000..883033a5
--- /dev/null
+++ b/Xamarin.Forms.Core/IRootObjectProvider.cs
@@ -0,0 +1,7 @@
+namespace Xamarin.Forms.Xaml
+{
+ public interface IRootObjectProvider
+ {
+ object RootObject { get; }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/IScrollViewController.cs b/Xamarin.Forms.Core/IScrollViewController.cs
new file mode 100644
index 00000000..c06ddc09
--- /dev/null
+++ b/Xamarin.Forms.Core/IScrollViewController.cs
@@ -0,0 +1,15 @@
+using System;
+
+namespace Xamarin.Forms
+{
+ public interface IScrollViewController : ILayoutController
+ {
+ Point GetScrollPositionForElement(VisualElement item, ScrollToPosition position);
+
+ event EventHandler<ScrollToRequestedEventArgs> ScrollToRequested;
+
+ void SendScrollFinished();
+
+ void SetScrolledPosition(double x, double y);
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/IStyle.cs b/Xamarin.Forms.Core/IStyle.cs
new file mode 100644
index 00000000..cdb998dc
--- /dev/null
+++ b/Xamarin.Forms.Core/IStyle.cs
@@ -0,0 +1,12 @@
+using System;
+
+namespace Xamarin.Forms
+{
+ internal interface IStyle
+ {
+ Type TargetType { get; }
+
+ void Apply(BindableObject bindable);
+ void UnApply(BindableObject bindable);
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/ISystemResourcesProvider.cs b/Xamarin.Forms.Core/ISystemResourcesProvider.cs
new file mode 100644
index 00000000..9f0e5772
--- /dev/null
+++ b/Xamarin.Forms.Core/ISystemResourcesProvider.cs
@@ -0,0 +1,7 @@
+namespace Xamarin.Forms
+{
+ internal interface ISystemResourcesProvider
+ {
+ IResourceDictionary GetSystemResources();
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/ITimer.cs b/Xamarin.Forms.Core/ITimer.cs
new file mode 100644
index 00000000..ba867dba
--- /dev/null
+++ b/Xamarin.Forms.Core/ITimer.cs
@@ -0,0 +1,13 @@
+using System;
+
+namespace Xamarin.Forms
+{
+ //this will go once Timer is included in Pcl profiles
+ internal interface ITimer
+ {
+ void Change(int dueTime, int period);
+ void Change(long dueTime, long period);
+ void Change(TimeSpan dueTime, TimeSpan period);
+ void Change(uint dueTime, uint period);
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/IValueConverter.cs b/Xamarin.Forms.Core/IValueConverter.cs
new file mode 100644
index 00000000..8702ec3d
--- /dev/null
+++ b/Xamarin.Forms.Core/IValueConverter.cs
@@ -0,0 +1,11 @@
+using System;
+using System.Globalization;
+
+namespace Xamarin.Forms
+{
+ public interface IValueConverter
+ {
+ object Convert(object value, Type targetType, object parameter, CultureInfo culture);
+ object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture);
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/IValueConverterProvider.cs b/Xamarin.Forms.Core/IValueConverterProvider.cs
new file mode 100644
index 00000000..1221ce74
--- /dev/null
+++ b/Xamarin.Forms.Core/IValueConverterProvider.cs
@@ -0,0 +1,10 @@
+using System;
+using System.Reflection;
+
+namespace Xamarin.Forms.Xaml
+{
+ internal interface IValueConverterProvider
+ {
+ object Convert(object value, Type toType, Func<MemberInfo> minfoRetriever, IServiceProvider serviceProvider);
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/IValueProvider.cs b/Xamarin.Forms.Core/IValueProvider.cs
new file mode 100644
index 00000000..19d3bf93
--- /dev/null
+++ b/Xamarin.Forms.Core/IValueProvider.cs
@@ -0,0 +1,9 @@
+using System;
+
+namespace Xamarin.Forms.Xaml
+{
+ public interface IValueProvider
+ {
+ object ProvideValue(IServiceProvider serviceProvider);
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/IViewContainer.cs b/Xamarin.Forms.Core/IViewContainer.cs
new file mode 100644
index 00000000..9b2e0570
--- /dev/null
+++ b/Xamarin.Forms.Core/IViewContainer.cs
@@ -0,0 +1,9 @@
+using System.Collections.Generic;
+
+namespace Xamarin.Forms
+{
+ public interface IViewContainer<T> where T : VisualElement
+ {
+ IList<T> Children { get; }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/IViewController.cs b/Xamarin.Forms.Core/IViewController.cs
new file mode 100644
index 00000000..1d956387
--- /dev/null
+++ b/Xamarin.Forms.Core/IViewController.cs
@@ -0,0 +1,6 @@
+namespace Xamarin.Forms
+{
+ public interface IViewController : IVisualElementController
+ {
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/IVisualElementController.cs b/Xamarin.Forms.Core/IVisualElementController.cs
new file mode 100644
index 00000000..03be293a
--- /dev/null
+++ b/Xamarin.Forms.Core/IVisualElementController.cs
@@ -0,0 +1,7 @@
+namespace Xamarin.Forms
+{
+ public interface IVisualElementController : IElementController
+ {
+ void NativeSizeChanged();
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/IWebViewRenderer.cs b/Xamarin.Forms.Core/IWebViewRenderer.cs
new file mode 100644
index 00000000..0b1b3db5
--- /dev/null
+++ b/Xamarin.Forms.Core/IWebViewRenderer.cs
@@ -0,0 +1,8 @@
+namespace Xamarin.Forms
+{
+ internal interface IWebViewRenderer
+ {
+ void LoadHtml(string html, string baseUrl);
+ void LoadUrl(string url);
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/IXamlTypeResolver.cs b/Xamarin.Forms.Core/IXamlTypeResolver.cs
new file mode 100644
index 00000000..1708e0b1
--- /dev/null
+++ b/Xamarin.Forms.Core/IXamlTypeResolver.cs
@@ -0,0 +1,10 @@
+using System;
+
+namespace Xamarin.Forms.Xaml
+{
+ public interface IXamlTypeResolver
+ {
+ Type Resolve(string qualifiedTypeName, IServiceProvider serviceProvider = null);
+ bool TryResolve(string qualifiedTypeName, out Type type);
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/IXmlLineInfoProvider.cs b/Xamarin.Forms.Core/IXmlLineInfoProvider.cs
new file mode 100644
index 00000000..348f4d4d
--- /dev/null
+++ b/Xamarin.Forms.Core/IXmlLineInfoProvider.cs
@@ -0,0 +1,9 @@
+using System.Xml;
+
+namespace Xamarin.Forms.Xaml
+{
+ public interface IXmlLineInfoProvider
+ {
+ IXmlLineInfo XmlLineInfo { get; }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/Image.cs b/Xamarin.Forms.Core/Image.cs
new file mode 100644
index 00000000..9e329331
--- /dev/null
+++ b/Xamarin.Forms.Core/Image.cs
@@ -0,0 +1,145 @@
+using System;
+using Xamarin.Forms.Platform;
+
+namespace Xamarin.Forms
+{
+ [RenderWith(typeof(_ImageRenderer))]
+ public class Image : View
+ {
+ public static readonly BindableProperty SourceProperty = BindableProperty.Create("Source", typeof(ImageSource), typeof(Image), default(ImageSource), propertyChanging: OnSourcePropertyChanging,
+ propertyChanged: OnSourcePropertyChanged);
+
+ public static readonly BindableProperty AspectProperty = BindableProperty.Create("Aspect", typeof(Aspect), typeof(Image), Aspect.AspectFit);
+
+ public static readonly BindableProperty IsOpaqueProperty = BindableProperty.Create("IsOpaque", typeof(bool), typeof(Image), false);
+
+ internal static readonly BindablePropertyKey IsLoadingPropertyKey = BindableProperty.CreateReadOnly("IsLoading", typeof(bool), typeof(Image), default(bool));
+
+ public static readonly BindableProperty IsLoadingProperty = IsLoadingPropertyKey.BindableProperty;
+
+ public Aspect Aspect
+ {
+ get { return (Aspect)GetValue(AspectProperty); }
+ set { SetValue(AspectProperty, value); }
+ }
+
+ public bool IsLoading
+ {
+ get { return (bool)GetValue(IsLoadingProperty); }
+ }
+
+ public bool IsOpaque
+ {
+ get { return (bool)GetValue(IsOpaqueProperty); }
+ set { SetValue(IsOpaqueProperty, value); }
+ }
+
+ [TypeConverter(typeof(ImageSourceConverter))]
+ public ImageSource Source
+ {
+ get { return (ImageSource)GetValue(SourceProperty); }
+ set { SetValue(SourceProperty, value); }
+ }
+
+ protected override void OnBindingContextChanged()
+ {
+ if (Source != null)
+ SetInheritedBindingContext(Source, BindingContext);
+
+ base.OnBindingContextChanged();
+ }
+
+ [Obsolete("Use OnMeasure")]
+ protected override SizeRequest OnSizeRequest(double widthConstraint, double heightConstraint)
+ {
+ SizeRequest desiredSize = base.OnSizeRequest(double.PositiveInfinity, double.PositiveInfinity);
+
+ double desiredAspect = desiredSize.Request.Width / desiredSize.Request.Height;
+ double constraintAspect = widthConstraint / heightConstraint;
+
+ double desiredWidth = desiredSize.Request.Width;
+ double desiredHeight = desiredSize.Request.Height;
+
+ if (desiredWidth == 0 || desiredHeight == 0)
+ return new SizeRequest(new Size(0, 0));
+
+ double width = desiredWidth;
+ double height = desiredHeight;
+ if (constraintAspect > desiredAspect)
+ {
+ // constraint area is proportionally wider than image
+ switch (Aspect)
+ {
+ case Aspect.AspectFit:
+ case Aspect.AspectFill:
+ height = Math.Min(desiredHeight, heightConstraint);
+ width = desiredWidth * (height / desiredHeight);
+ break;
+ case Aspect.Fill:
+ width = Math.Min(desiredWidth, widthConstraint);
+ height = desiredHeight * (width / desiredWidth);
+ break;
+ }
+ }
+ else if (constraintAspect < desiredAspect)
+ {
+ // constraint area is proportionally taller than image
+ switch (Aspect)
+ {
+ case Aspect.AspectFit:
+ case Aspect.AspectFill:
+ width = Math.Min(desiredWidth, widthConstraint);
+ height = desiredHeight * (width / desiredWidth);
+ break;
+ case Aspect.Fill:
+ height = Math.Min(desiredHeight, heightConstraint);
+ width = desiredWidth * (height / desiredHeight);
+ break;
+ }
+ }
+ else
+ {
+ // constraint area is same aspect as image
+ width = Math.Min(desiredWidth, widthConstraint);
+ height = desiredHeight * (width / desiredWidth);
+ }
+
+ return new SizeRequest(new Size(width, height));
+ }
+
+ void OnSourceChanged(object sender, EventArgs eventArgs)
+ {
+ OnPropertyChanged(SourceProperty.PropertyName);
+ InvalidateMeasure(InvalidationTrigger.MeasureChanged);
+ }
+
+ static void OnSourcePropertyChanged(BindableObject bindable, object oldvalue, object newvalue)
+ {
+ ((Image)bindable).OnSourcePropertyChanged((ImageSource)oldvalue, (ImageSource)newvalue);
+ }
+
+ void OnSourcePropertyChanged(ImageSource oldvalue, ImageSource newvalue)
+ {
+ if (newvalue != null)
+ {
+ newvalue.SourceChanged += OnSourceChanged;
+ SetInheritedBindingContext(newvalue, BindingContext);
+ }
+ InvalidateMeasure(InvalidationTrigger.MeasureChanged);
+ }
+
+ static void OnSourcePropertyChanging(BindableObject bindable, object oldvalue, object newvalue)
+ {
+ ((Image)bindable).OnSourcePropertyChanging((ImageSource)oldvalue, (ImageSource)newvalue);
+ }
+
+ async void OnSourcePropertyChanging(ImageSource oldvalue, ImageSource newvalue)
+ {
+ if (oldvalue == null)
+ return;
+
+ oldvalue.SourceChanged -= OnSourceChanged;
+ await oldvalue.Cancel();
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/ImageSource.cs b/Xamarin.Forms.Core/ImageSource.cs
new file mode 100644
index 00000000..71e75952
--- /dev/null
+++ b/Xamarin.Forms.Core/ImageSource.cs
@@ -0,0 +1,142 @@
+using System;
+using System.IO;
+using System.Reflection;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Xamarin.Forms
+{
+ [TypeConverter(typeof(ImageSourceConverter))]
+ public abstract class ImageSource : Element
+ {
+ readonly object _synchandle = new object();
+ CancellationTokenSource _cancellationTokenSource;
+
+ TaskCompletionSource<bool> _completionSource;
+
+ internal ImageSource()
+ {
+ }
+
+ protected CancellationTokenSource CancellationTokenSource
+ {
+ get { return _cancellationTokenSource; }
+ private set
+ {
+ if (_cancellationTokenSource == value)
+ return;
+ if (_cancellationTokenSource != null)
+ _cancellationTokenSource.Cancel();
+ _cancellationTokenSource = value;
+ }
+ }
+
+ bool IsLoading
+ {
+ get { return _cancellationTokenSource != null; }
+ }
+
+ public virtual Task<bool> Cancel()
+ {
+ if (!IsLoading)
+ return Task.FromResult(false);
+
+ var tcs = new TaskCompletionSource<bool>();
+ TaskCompletionSource<bool> original = Interlocked.CompareExchange(ref _completionSource, tcs, null);
+ if (original == null)
+ {
+ _cancellationTokenSource.Cancel();
+ }
+ else
+ tcs = original;
+
+ return tcs.Task;
+ }
+
+ public static ImageSource FromFile(string file)
+ {
+ return new FileImageSource { File = file };
+ }
+
+ public static ImageSource FromResource(string resource, Type resolvingType)
+ {
+ return FromResource(resource, resolvingType.GetTypeInfo().Assembly);
+ }
+
+ public static ImageSource FromResource(string resource, Assembly sourceAssembly = null)
+ {
+ if (sourceAssembly == null)
+ {
+ MethodInfo callingAssemblyMethod = typeof(Assembly).GetTypeInfo().GetDeclaredMethod("GetCallingAssembly");
+ if (callingAssemblyMethod != null)
+ {
+ sourceAssembly = (Assembly)callingAssemblyMethod.Invoke(null, new object[0]);
+ }
+ else
+ {
+ Log.Warning("Warning", "Can not find CallingAssembly, pass resolvingType to FromResource to ensure proper resolution");
+ return null;
+ }
+ }
+
+ return FromStream(() => sourceAssembly.GetManifestResourceStream(resource));
+ }
+
+ public static ImageSource FromStream(Func<Stream> stream)
+ {
+ return new StreamImageSource { Stream = token => Task.Run(stream, token) };
+ }
+
+ public static ImageSource FromUri(Uri uri)
+ {
+ if (!uri.IsAbsoluteUri)
+ throw new ArgumentException("uri is relative");
+ return new UriImageSource { Uri = uri };
+ }
+
+ public static implicit operator ImageSource(string source)
+ {
+ Uri uri;
+ return Uri.TryCreate(source, UriKind.Absolute, out uri) && uri.Scheme != "file" ? FromUri(uri) : FromFile(source);
+ }
+
+ public static implicit operator ImageSource(Uri uri)
+ {
+ if (!uri.IsAbsoluteUri)
+ throw new ArgumentException("uri is relative");
+ return FromUri(uri);
+ }
+
+ protected void OnLoadingCompleted(bool cancelled)
+ {
+ if (!IsLoading || _completionSource == null)
+ return;
+
+ TaskCompletionSource<bool> tcs = Interlocked.Exchange(ref _completionSource, null);
+ if (tcs != null)
+ tcs.SetResult(cancelled);
+
+ lock(_synchandle)
+ {
+ CancellationTokenSource = null;
+ }
+ }
+
+ protected void OnLoadingStarted()
+ {
+ lock(_synchandle)
+ {
+ CancellationTokenSource = new CancellationTokenSource();
+ }
+ }
+
+ protected void OnSourceChanged()
+ {
+ EventHandler eh = SourceChanged;
+ if (eh != null)
+ eh(this, EventArgs.Empty);
+ }
+
+ internal event EventHandler SourceChanged;
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/ImageSourceConverter.cs b/Xamarin.Forms.Core/ImageSourceConverter.cs
new file mode 100644
index 00000000..8ca829f7
--- /dev/null
+++ b/Xamarin.Forms.Core/ImageSourceConverter.cs
@@ -0,0 +1,18 @@
+using System;
+
+namespace Xamarin.Forms
+{
+ public sealed class ImageSourceConverter : TypeConverter
+ {
+ public override object ConvertFromInvariantString(string value)
+ {
+ if (value != null)
+ {
+ Uri uri;
+ return Uri.TryCreate(value, UriKind.Absolute, out uri) && uri.Scheme != "file" ? ImageSource.FromUri(uri) : ImageSource.FromFile(value);
+ }
+
+ throw new InvalidOperationException(string.Format("Cannot convert \"{0}\" into {1}", value, typeof(ImageSource)));
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/InputView.cs b/Xamarin.Forms.Core/InputView.cs
new file mode 100644
index 00000000..58b75c42
--- /dev/null
+++ b/Xamarin.Forms.Core/InputView.cs
@@ -0,0 +1,18 @@
+namespace Xamarin.Forms
+{
+ public class InputView : View
+ {
+ public static readonly BindableProperty KeyboardProperty = BindableProperty.Create("Keyboard", typeof(Keyboard), typeof(InputView), Keyboard.Default,
+ coerceValue: (o, v) => (Keyboard)v ?? Keyboard.Default);
+
+ internal InputView()
+ {
+ }
+
+ public Keyboard Keyboard
+ {
+ get { return (Keyboard)GetValue(KeyboardProperty); }
+ set { SetValue(KeyboardProperty, value); }
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/Interactivity/AttachedCollection.cs b/Xamarin.Forms.Core/Interactivity/AttachedCollection.cs
new file mode 100644
index 00000000..6aff5147
--- /dev/null
+++ b/Xamarin.Forms.Core/Interactivity/AttachedCollection.cs
@@ -0,0 +1,127 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+
+namespace Xamarin.Forms
+{
+ internal class AttachedCollection<T> : ObservableCollection<T>, ICollection<T>, IAttachedObject where T : BindableObject, IAttachedObject
+ {
+ readonly List<WeakReference> _associatedObjects = new List<WeakReference>();
+
+ public AttachedCollection()
+ {
+ }
+
+ public AttachedCollection(IEnumerable<T> collection) : base(collection)
+ {
+ }
+
+ public AttachedCollection(IList<T> list) : base(list)
+ {
+ }
+
+ public void AttachTo(BindableObject bindable)
+ {
+ if (bindable == null)
+ throw new ArgumentNullException("bindable");
+ OnAttachedTo(bindable);
+ }
+
+ public void DetachFrom(BindableObject bindable)
+ {
+ OnDetachingFrom(bindable);
+ }
+
+ protected override void ClearItems()
+ {
+ foreach (WeakReference weakbindable in _associatedObjects)
+ {
+ foreach (T item in this)
+ {
+ var bindable = weakbindable.Target as BindableObject;
+ if (bindable == null)
+ continue;
+ item.DetachFrom(bindable);
+ }
+ }
+ base.ClearItems();
+ }
+
+ protected override void InsertItem(int index, T item)
+ {
+ base.InsertItem(index, item);
+ foreach (WeakReference weakbindable in _associatedObjects)
+ {
+ var bindable = weakbindable.Target as BindableObject;
+ if (bindable == null)
+ continue;
+ item.AttachTo(bindable);
+ }
+ }
+
+ protected virtual void OnAttachedTo(BindableObject bindable)
+ {
+ lock(_associatedObjects)
+ {
+ _associatedObjects.Add(new WeakReference(bindable));
+ }
+ foreach (T item in this)
+ item.AttachTo(bindable);
+ }
+
+ protected virtual void OnDetachingFrom(BindableObject bindable)
+ {
+ foreach (T item in this)
+ item.DetachFrom(bindable);
+ lock(_associatedObjects)
+ {
+ for (var i = 0; i < _associatedObjects.Count; i++)
+ {
+ object target = _associatedObjects[i].Target;
+
+ if (target == null || target == bindable)
+ {
+ _associatedObjects.RemoveAt(i);
+ i--;
+ }
+ }
+ }
+ }
+
+ protected override void RemoveItem(int index)
+ {
+ T item = this[index];
+ foreach (WeakReference weakbindable in _associatedObjects)
+ {
+ var bindable = weakbindable.Target as BindableObject;
+ if (bindable == null)
+ continue;
+ item.DetachFrom(bindable);
+ }
+
+ base.RemoveItem(index);
+ }
+
+ protected override void SetItem(int index, T item)
+ {
+ T old = this[index];
+ foreach (WeakReference weakbindable in _associatedObjects)
+ {
+ var bindable = weakbindable.Target as BindableObject;
+ if (bindable == null)
+ continue;
+ old.DetachFrom(bindable);
+ }
+
+ base.SetItem(index, item);
+
+ foreach (WeakReference weakbindable in _associatedObjects)
+ {
+ var bindable = weakbindable.Target as BindableObject;
+ if (bindable == null)
+ continue;
+ item.AttachTo(bindable);
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/Interactivity/Behavior.cs b/Xamarin.Forms.Core/Interactivity/Behavior.cs
new file mode 100644
index 00000000..f8689041
--- /dev/null
+++ b/Xamarin.Forms.Core/Interactivity/Behavior.cs
@@ -0,0 +1,65 @@
+using System;
+
+namespace Xamarin.Forms
+{
+ public abstract class Behavior : BindableObject, IAttachedObject
+ {
+ internal Behavior(Type associatedType)
+ {
+ if (associatedType == null)
+ throw new ArgumentNullException("associatedType");
+ AssociatedType = associatedType;
+ }
+
+ protected Type AssociatedType { get; }
+
+ void IAttachedObject.AttachTo(BindableObject bindable)
+ {
+ if (bindable == null)
+ throw new ArgumentNullException("bindable");
+ if (!AssociatedType.IsInstanceOfType(bindable))
+ throw new InvalidOperationException("bindable not an instance of AssociatedType");
+ OnAttachedTo(bindable);
+ }
+
+ void IAttachedObject.DetachFrom(BindableObject bindable)
+ {
+ OnDetachingFrom(bindable);
+ }
+
+ protected virtual void OnAttachedTo(BindableObject bindable)
+ {
+ }
+
+ protected virtual void OnDetachingFrom(BindableObject bindable)
+ {
+ }
+ }
+
+ public abstract class Behavior<T> : Behavior where T : BindableObject
+ {
+ protected Behavior() : base(typeof(T))
+ {
+ }
+
+ protected override void OnAttachedTo(BindableObject bindable)
+ {
+ base.OnAttachedTo(bindable);
+ OnAttachedTo((T)bindable);
+ }
+
+ protected virtual void OnAttachedTo(T bindable)
+ {
+ }
+
+ protected override void OnDetachingFrom(BindableObject bindable)
+ {
+ OnDetachingFrom((T)bindable);
+ base.OnDetachingFrom(bindable);
+ }
+
+ protected virtual void OnDetachingFrom(T bindable)
+ {
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/Interactivity/BindingCondition.cs b/Xamarin.Forms.Core/Interactivity/BindingCondition.cs
new file mode 100644
index 00000000..88b7cf36
--- /dev/null
+++ b/Xamarin.Forms.Core/Interactivity/BindingCondition.cs
@@ -0,0 +1,100 @@
+using System;
+using Xamarin.Forms.Xaml;
+
+namespace Xamarin.Forms
+{
+ public sealed class BindingCondition : Condition, IValueProvider
+ {
+ readonly BindableProperty _boundProperty;
+
+ BindingBase _binding;
+ object _triggerValue;
+
+ public BindingCondition()
+ {
+ _boundProperty = BindableProperty.CreateAttached("Bound", typeof(object), typeof(DataTrigger), null, propertyChanged: OnBoundPropertyChanged);
+ }
+
+ public BindingBase Binding
+ {
+ get { return _binding; }
+ set
+ {
+ if (_binding == value)
+ return;
+ if (IsSealed)
+ throw new InvalidOperationException("Can not change Binding once the Trigger has been applied.");
+ _binding = value;
+ }
+ }
+
+ public object Value
+ {
+ get { return _triggerValue; }
+ set
+ {
+ if (_triggerValue == value)
+ return;
+ if (IsSealed)
+ throw new InvalidOperationException("Can not change Value once the Trigger has been applied.");
+ _triggerValue = value;
+ }
+ }
+
+ internal IServiceProvider ServiceProvider { get; set; }
+
+ internal IValueConverterProvider ValueConverter { get; set; }
+
+ object IValueProvider.ProvideValue(IServiceProvider serviceProvider)
+ {
+ ValueConverter = serviceProvider.GetService(typeof(IValueConverterProvider)) as IValueConverterProvider;
+ ServiceProvider = serviceProvider;
+
+ return this;
+ }
+
+ internal override bool GetState(BindableObject bindable)
+ {
+ object newValue = bindable.GetValue(_boundProperty);
+ return EqualsToValue(newValue);
+ }
+
+ internal override void SetUp(BindableObject bindable)
+ {
+ if (Binding != null)
+ bindable.SetBinding(_boundProperty, Binding.Clone());
+ }
+
+ internal override void TearDown(BindableObject bindable)
+ {
+ bindable.RemoveBinding(_boundProperty);
+ bindable.ClearValue(_boundProperty);
+ }
+
+ bool EqualsToValue(object other)
+ {
+ if ((other == Value) || (other != null && other.Equals(Value)))
+ return true;
+
+ object converted = null;
+ if (ValueConverter != null)
+ converted = ValueConverter.Convert(Value, other != null ? other.GetType() : typeof(object), null, ServiceProvider);
+ else
+ return false;
+
+ return (other == converted) || (other != null && other.Equals(converted));
+ }
+
+ void OnBoundPropertyChanged(BindableObject bindable, object oldValue, object newValue)
+ {
+ bool oldState = EqualsToValue(oldValue);
+ bool newState = EqualsToValue(newValue);
+
+ if (newState == oldState)
+ return;
+
+ if (ConditionChanged != null)
+ ConditionChanged(bindable, oldState, newState);
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/Interactivity/Condition.cs b/Xamarin.Forms.Core/Interactivity/Condition.cs
new file mode 100644
index 00000000..aad921cf
--- /dev/null
+++ b/Xamarin.Forms.Core/Interactivity/Condition.cs
@@ -0,0 +1,51 @@
+using System;
+
+namespace Xamarin.Forms
+{
+ public abstract class Condition
+ {
+ Action<BindableObject, bool, bool> _conditionChanged;
+
+ bool _isSealed;
+
+ internal Condition()
+ {
+ }
+
+ internal Action<BindableObject, bool, bool> ConditionChanged
+ {
+ get { return _conditionChanged; }
+ set
+ {
+ if (_conditionChanged == value)
+ return;
+ if (_conditionChanged != null)
+ throw new InvalidOperationException("The same condition instance can not be reused");
+ _conditionChanged = value;
+ }
+ }
+
+ internal bool IsSealed
+ {
+ get { return _isSealed; }
+ set
+ {
+ if (_isSealed == value)
+ return;
+ if (!value)
+ throw new InvalidOperationException("What is sealed can not be unsealed.");
+ _isSealed = value;
+ OnSealed();
+ }
+ }
+
+ internal abstract bool GetState(BindableObject bindable);
+
+ internal virtual void OnSealed()
+ {
+ }
+
+ internal abstract void SetUp(BindableObject bindable);
+ internal abstract void TearDown(BindableObject bindable);
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/Interactivity/DataTrigger.cs b/Xamarin.Forms.Core/Interactivity/DataTrigger.cs
new file mode 100644
index 00000000..e27ec134
--- /dev/null
+++ b/Xamarin.Forms.Core/Interactivity/DataTrigger.cs
@@ -0,0 +1,57 @@
+using System;
+using System.Collections.Generic;
+using Xamarin.Forms.Xaml;
+
+namespace Xamarin.Forms
+{
+ [ContentProperty("Setters")]
+ public sealed class DataTrigger : TriggerBase, IValueProvider
+ {
+ public DataTrigger([TypeConverter(typeof(TypeTypeConverter))] [Parameter("TargetType")] Type targetType) : base(new BindingCondition(), targetType)
+ {
+ }
+
+ public BindingBase Binding
+ {
+ get { return ((BindingCondition)Condition).Binding; }
+ set
+ {
+ if (((BindingCondition)Condition).Binding == value)
+ return;
+ if (IsSealed)
+ throw new InvalidOperationException("Can not change Binding once the Trigger has been applied.");
+ OnPropertyChanging();
+ ((BindingCondition)Condition).Binding = value;
+ OnPropertyChanged();
+ }
+ }
+
+ public new IList<Setter> Setters
+ {
+ get { return base.Setters; }
+ }
+
+ public object Value
+ {
+ get { return ((BindingCondition)Condition).Value; }
+ set
+ {
+ if (((BindingCondition)Condition).Value == value)
+ return;
+ if (IsSealed)
+ throw new InvalidOperationException("Can not change Value once the Trigger has been applied.");
+ OnPropertyChanging();
+ ((BindingCondition)Condition).Value = value;
+ OnPropertyChanged();
+ }
+ }
+
+ object IValueProvider.ProvideValue(IServiceProvider serviceProvider)
+ {
+ var valueconverter = serviceProvider.GetService(typeof(IValueConverterProvider)) as IValueConverterProvider;
+ (Condition as BindingCondition).ValueConverter = valueconverter;
+
+ return this;
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/Interactivity/EventTrigger.cs b/Xamarin.Forms.Core/Interactivity/EventTrigger.cs
new file mode 100644
index 00000000..52e221a0
--- /dev/null
+++ b/Xamarin.Forms.Core/Interactivity/EventTrigger.cs
@@ -0,0 +1,91 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+
+namespace Xamarin.Forms
+{
+ [ContentProperty("Actions")]
+ public sealed class EventTrigger : TriggerBase
+ {
+ static readonly MethodInfo s_handlerinfo = typeof(EventTrigger).GetRuntimeMethods().Single(mi => mi.Name == "OnEventTriggered" && mi.IsPublic == false);
+ readonly List<BindableObject> _associatedObjects = new List<BindableObject>();
+
+ EventInfo _eventinfo;
+
+ string _eventname;
+ Delegate _handlerdelegate;
+
+ public EventTrigger() : base(typeof(BindableObject))
+ {
+ Actions = new SealedList<TriggerAction>();
+ }
+
+ public IList<TriggerAction> Actions { get; }
+
+ public string Event
+ {
+ get { return _eventname; }
+ set
+ {
+ if (_eventname == value)
+ return;
+ if (IsSealed)
+ throw new InvalidOperationException("Event cannot be changed once the Trigger has been applied");
+ OnPropertyChanging();
+ _eventname = value;
+ OnPropertyChanged();
+ }
+ }
+
+ internal override void OnAttachedTo(BindableObject bindable)
+ {
+ base.OnAttachedTo(bindable);
+ if (!string.IsNullOrEmpty(Event))
+ AttachHandlerTo(bindable);
+ _associatedObjects.Add(bindable);
+ }
+
+ internal override void OnDetachingFrom(BindableObject bindable)
+ {
+ _associatedObjects.Remove(bindable);
+ DetachHandlerFrom(bindable);
+ base.OnDetachingFrom(bindable);
+ }
+
+ internal override void OnSeal()
+ {
+ base.OnSeal();
+ ((SealedList<TriggerAction>)Actions).IsReadOnly = true;
+ }
+
+ void AttachHandlerTo(BindableObject bindable)
+ {
+ try
+ {
+ _eventinfo = bindable.GetType().GetRuntimeEvent(Event);
+ _handlerdelegate = s_handlerinfo.CreateDelegate(_eventinfo.EventHandlerType, this);
+ }
+ catch (Exception)
+ {
+ Log.Warning("EventTrigger", "Can not attach EventTrigger to {0}.{1}. Check if the handler exists and if the signature is right.", bindable.GetType(), Event);
+ }
+ if (_eventinfo != null && _handlerdelegate != null)
+ _eventinfo.AddEventHandler(bindable, _handlerdelegate);
+ }
+
+ void DetachHandlerFrom(BindableObject bindable)
+ {
+ if (_eventinfo != null && _handlerdelegate != null)
+ _eventinfo.RemoveEventHandler(bindable, _handlerdelegate);
+ }
+
+ [Preserve]
+ void OnEventTriggered(object sender, EventArgs e)
+ {
+ var bindable = (BindableObject)sender;
+ foreach (TriggerAction action in Actions)
+ action.DoInvoke(bindable);
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/Interactivity/IAttachedObject.cs b/Xamarin.Forms.Core/Interactivity/IAttachedObject.cs
new file mode 100644
index 00000000..09748873
--- /dev/null
+++ b/Xamarin.Forms.Core/Interactivity/IAttachedObject.cs
@@ -0,0 +1,8 @@
+namespace Xamarin.Forms
+{
+ internal interface IAttachedObject
+ {
+ void AttachTo(BindableObject bindable);
+ void DetachFrom(BindableObject bindable);
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/Interactivity/MultiCondition.cs b/Xamarin.Forms.Core/Interactivity/MultiCondition.cs
new file mode 100644
index 00000000..23ca41c5
--- /dev/null
+++ b/Xamarin.Forms.Core/Interactivity/MultiCondition.cs
@@ -0,0 +1,66 @@
+using System.Collections.Generic;
+
+namespace Xamarin.Forms
+{
+ internal sealed class MultiCondition : Condition
+ {
+ readonly BindableProperty _aggregatedStateProperty;
+
+ public MultiCondition()
+ {
+ _aggregatedStateProperty = BindableProperty.CreateAttached("AggregatedState", typeof(bool), typeof(DataTrigger), false, propertyChanged: OnAggregatedStatePropertyChanged);
+ Conditions = new TriggerBase.SealedList<Condition>();
+ }
+
+ public IList<Condition> Conditions { get; }
+
+ internal override bool GetState(BindableObject bindable)
+ {
+ return (bool)bindable.GetValue(_aggregatedStateProperty);
+ }
+
+ internal override void OnSealed()
+ {
+ ((TriggerBase.SealedList<Condition>)Conditions).IsReadOnly = true;
+ foreach (Condition condition in Conditions)
+ condition.ConditionChanged = OnConditionChanged;
+ }
+
+ internal override void SetUp(BindableObject bindable)
+ {
+ foreach (Condition condition in Conditions)
+ condition.SetUp(bindable);
+ }
+
+ internal override void TearDown(BindableObject bindable)
+ {
+ foreach (Condition condition in Conditions)
+ condition.TearDown(bindable);
+ }
+
+ void OnAggregatedStatePropertyChanged(BindableObject bindable, object oldValue, object newValue)
+ {
+ if ((bool)oldValue == (bool)newValue)
+ return;
+
+ if (ConditionChanged != null)
+ ConditionChanged(bindable, (bool)oldValue, (bool)newValue);
+ }
+
+ void OnConditionChanged(BindableObject bindable, bool oldValue, bool newValue)
+ {
+ var oldState = (bool)bindable.GetValue(_aggregatedStateProperty);
+ var newState = true;
+ foreach (Condition condition in Conditions)
+ {
+ if (!condition.GetState(bindable))
+ {
+ newState = false;
+ break;
+ }
+ }
+ if (newState != oldState)
+ bindable.SetValue(_aggregatedStateProperty, newState);
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/Interactivity/MultiTrigger.cs b/Xamarin.Forms.Core/Interactivity/MultiTrigger.cs
new file mode 100644
index 00000000..3c85467c
--- /dev/null
+++ b/Xamarin.Forms.Core/Interactivity/MultiTrigger.cs
@@ -0,0 +1,23 @@
+using System;
+using System.Collections.Generic;
+
+namespace Xamarin.Forms
+{
+ [ContentProperty("Setters")]
+ public sealed class MultiTrigger : TriggerBase
+ {
+ public MultiTrigger([TypeConverter(typeof(TypeTypeConverter))] [Parameter("TargetType")] Type targetType) : base(new MultiCondition(), targetType)
+ {
+ }
+
+ public IList<Condition> Conditions
+ {
+ get { return ((MultiCondition)Condition).Conditions; }
+ }
+
+ public new IList<Setter> Setters
+ {
+ get { return base.Setters; }
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/Interactivity/PropertyCondition.cs b/Xamarin.Forms.Core/Interactivity/PropertyCondition.cs
new file mode 100644
index 00000000..be37d48f
--- /dev/null
+++ b/Xamarin.Forms.Core/Interactivity/PropertyCondition.cs
@@ -0,0 +1,100 @@
+using System;
+using System.ComponentModel;
+using System.Reflection;
+using Xamarin.Forms.Xaml;
+
+namespace Xamarin.Forms
+{
+ public sealed class PropertyCondition : Condition, IValueProvider
+ {
+ readonly BindableProperty _stateProperty;
+
+ BindableProperty _property;
+ object _triggerValue;
+
+ public PropertyCondition()
+ {
+ _stateProperty = BindableProperty.CreateAttached("State", typeof(bool), typeof(DataTrigger), false, propertyChanged: OnStatePropertyChanged);
+ }
+
+ public BindableProperty Property
+ {
+ get { return _property; }
+ set
+ {
+ if (_property == value)
+ return;
+ if (IsSealed)
+ throw new InvalidOperationException("Can not change Property once the Trigger has been applied.");
+ _property = value;
+ }
+ }
+
+ public object Value
+ {
+ get { return _triggerValue; }
+ set
+ {
+ if (_triggerValue == value)
+ return;
+ if (IsSealed)
+ throw new InvalidOperationException("Can not change Value once the Trigger has been applied.");
+ _triggerValue = value;
+ }
+ }
+
+ object IValueProvider.ProvideValue(IServiceProvider serviceProvider)
+ {
+ var valueconverter = serviceProvider.GetService(typeof(IValueConverterProvider)) as IValueConverterProvider;
+ Func<MemberInfo> minforetriever = () => Property.DeclaringType.GetRuntimeProperty(Property.PropertyName);
+
+ object value = valueconverter.Convert(Value, Property.ReturnType, minforetriever, serviceProvider);
+ Value = value;
+ return this;
+ }
+
+ internal override bool GetState(BindableObject bindable)
+ {
+ return (bool)bindable.GetValue(_stateProperty);
+ }
+
+ internal override void SetUp(BindableObject bindable)
+ {
+ object newvalue = bindable.GetValue(Property);
+
+ bool newState = (newvalue == Value) || (newvalue != null && newvalue.Equals(Value));
+ bindable.SetValue(_stateProperty, newState);
+ bindable.PropertyChanged += OnAttachedObjectPropertyChanged;
+ }
+
+ internal override void TearDown(BindableObject bindable)
+ {
+ bindable.ClearValue(_stateProperty);
+ bindable.PropertyChanged -= OnAttachedObjectPropertyChanged;
+ }
+
+ void OnAttachedObjectPropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ var bindable = (BindableObject)sender;
+ var oldState = (bool)bindable.GetValue(_stateProperty);
+
+ if (Property == null)
+ return;
+ if (e.PropertyName != Property.PropertyName)
+ return;
+ object newvalue = bindable.GetValue(Property);
+ bool newstate = (newvalue == Value) || (newvalue != null && newvalue.Equals(Value));
+ if (oldState != newstate)
+ bindable.SetValue(_stateProperty, newstate);
+ }
+
+ void OnStatePropertyChanged(BindableObject bindable, object oldValue, object newValue)
+ {
+ if ((bool)oldValue == (bool)newValue)
+ return;
+
+ if (ConditionChanged != null)
+ ConditionChanged(bindable, (bool)oldValue, (bool)newValue);
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/Interactivity/Trigger.cs b/Xamarin.Forms.Core/Interactivity/Trigger.cs
new file mode 100644
index 00000000..ea3dc5ae
--- /dev/null
+++ b/Xamarin.Forms.Core/Interactivity/Trigger.cs
@@ -0,0 +1,60 @@
+using System;
+using System.Collections.Generic;
+using System.Reflection;
+using Xamarin.Forms.Xaml;
+
+namespace Xamarin.Forms
+{
+ [ContentProperty("Setters")]
+ public sealed class Trigger : TriggerBase, IValueProvider
+ {
+ public Trigger([TypeConverter(typeof(TypeTypeConverter))] [Parameter("TargetType")] Type targetType) : base(new PropertyCondition(), targetType)
+ {
+ }
+
+ public BindableProperty Property
+ {
+ get { return ((PropertyCondition)Condition).Property; }
+ set
+ {
+ if (((PropertyCondition)Condition).Property == value)
+ return;
+ if (IsSealed)
+ throw new InvalidOperationException("Can not change Property once the Trigger has been applied.");
+ OnPropertyChanging();
+ ((PropertyCondition)Condition).Property = value;
+ OnPropertyChanged();
+ }
+ }
+
+ public new IList<Setter> Setters
+ {
+ get { return base.Setters; }
+ }
+
+ public object Value
+ {
+ get { return ((PropertyCondition)Condition).Value; }
+ set
+ {
+ if (((PropertyCondition)Condition).Value == value)
+ return;
+ if (IsSealed)
+ throw new InvalidOperationException("Can not change Value once the Trigger has been applied.");
+ OnPropertyChanging();
+ ((PropertyCondition)Condition).Value = value;
+ OnPropertyChanged();
+ }
+ }
+
+ object IValueProvider.ProvideValue(IServiceProvider serviceProvider)
+ {
+ var valueconverter = serviceProvider.GetService(typeof(IValueConverterProvider)) as IValueConverterProvider;
+ Func<MemberInfo> minforetriever = () => Property.DeclaringType.GetRuntimeProperty(Property.PropertyName);
+
+ object value = valueconverter.Convert(Value, Property.ReturnType, minforetriever, serviceProvider);
+ Value = value;
+ return this;
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/Interactivity/TriggerAction.cs b/Xamarin.Forms.Core/Interactivity/TriggerAction.cs
new file mode 100644
index 00000000..bb9dc08f
--- /dev/null
+++ b/Xamarin.Forms.Core/Interactivity/TriggerAction.cs
@@ -0,0 +1,37 @@
+using System;
+
+namespace Xamarin.Forms
+{
+ public abstract class TriggerAction
+ {
+ internal TriggerAction(Type associatedType)
+ {
+ if (associatedType == null)
+ throw new ArgumentNullException("associatedType");
+ AssociatedType = associatedType;
+ }
+
+ protected Type AssociatedType { get; private set; }
+
+ protected abstract void Invoke(object sender);
+
+ internal virtual void DoInvoke(object sender)
+ {
+ Invoke(sender);
+ }
+ }
+
+ public abstract class TriggerAction<T> : TriggerAction where T : BindableObject
+ {
+ protected TriggerAction() : base(typeof(T))
+ {
+ }
+
+ protected override void Invoke(object sender)
+ {
+ Invoke((T)sender);
+ }
+
+ protected abstract void Invoke(T sender);
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/Interactivity/TriggerBase.cs b/Xamarin.Forms.Core/Interactivity/TriggerBase.cs
new file mode 100644
index 00000000..9418c7ae
--- /dev/null
+++ b/Xamarin.Forms.Core/Interactivity/TriggerBase.cs
@@ -0,0 +1,212 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+
+namespace Xamarin.Forms
+{
+ public abstract class TriggerBase : BindableObject, IAttachedObject
+ {
+ bool _isSealed;
+
+ internal TriggerBase(Type targetType)
+ {
+ if (targetType == null)
+ throw new ArgumentNullException("targetType");
+ TargetType = targetType;
+
+ EnterActions = new SealedList<TriggerAction>();
+ ExitActions = new SealedList<TriggerAction>();
+ }
+
+ internal TriggerBase(Condition condition, Type targetType) : this(targetType)
+ {
+ Setters = new SealedList<Setter>();
+ Condition = condition;
+ Condition.ConditionChanged = OnConditionChanged;
+ }
+
+ public IList<TriggerAction> EnterActions { get; }
+
+ public IList<TriggerAction> ExitActions { get; }
+
+ public bool IsSealed
+ {
+ get { return _isSealed; }
+ private set
+ {
+ if (_isSealed == value)
+ return;
+ if (!value)
+ throw new InvalidOperationException("What is sealed can not be unsealed.");
+ _isSealed = value;
+ OnSeal();
+ }
+ }
+
+ public Type TargetType { get; }
+
+ internal Condition Condition { get; }
+
+ //Setters and Condition are used by Trigger, DataTrigger and MultiTrigger
+ internal IList<Setter> Setters { get; }
+
+ void IAttachedObject.AttachTo(BindableObject bindable)
+ {
+ IsSealed = true;
+
+ if (bindable == null)
+ throw new ArgumentNullException("bindable");
+ if (!TargetType.IsInstanceOfType(bindable))
+ throw new InvalidOperationException("bindable not an instance of AssociatedType");
+ OnAttachedTo(bindable);
+ }
+
+ void IAttachedObject.DetachFrom(BindableObject bindable)
+ {
+ if (bindable == null)
+ throw new ArgumentNullException("bindable");
+ OnDetachingFrom(bindable);
+ }
+
+ internal virtual void OnAttachedTo(BindableObject bindable)
+ {
+ if (Condition != null)
+ Condition.SetUp(bindable);
+ }
+
+ internal virtual void OnDetachingFrom(BindableObject bindable)
+ {
+ if (Condition != null)
+ Condition.TearDown(bindable);
+ }
+
+ internal virtual void OnSeal()
+ {
+ ((SealedList<TriggerAction>)EnterActions).IsReadOnly = true;
+ ((SealedList<TriggerAction>)ExitActions).IsReadOnly = true;
+ if (Setters != null)
+ ((SealedList<Setter>)Setters).IsReadOnly = true;
+ if (Condition != null)
+ Condition.IsSealed = true;
+ }
+
+ void OnConditionChanged(BindableObject bindable, bool oldValue, bool newValue)
+ {
+ if (newValue)
+ {
+ foreach (TriggerAction action in EnterActions)
+ action.DoInvoke(bindable);
+ foreach (Setter setter in Setters)
+ setter.Apply(bindable);
+ }
+ else
+ {
+ foreach (Setter setter in Setters)
+ setter.UnApply(bindable);
+ foreach (TriggerAction action in ExitActions)
+ action.DoInvoke(bindable);
+ }
+ }
+
+ internal class SealedList<T> : IList<T>
+ {
+ readonly IList<T> _actual;
+
+ bool _isReadOnly;
+
+ public SealedList()
+ {
+ _actual = new List<T>();
+ }
+
+ public void Add(T item)
+ {
+ if (IsReadOnly)
+ throw new InvalidOperationException("This list is ReadOnly");
+ _actual.Add(item);
+ }
+
+ public void Clear()
+ {
+ if (IsReadOnly)
+ throw new InvalidOperationException("This list is ReadOnly");
+ _actual.Clear();
+ }
+
+ public bool Contains(T item)
+ {
+ return _actual.Contains(item);
+ }
+
+ public void CopyTo(T[] array, int arrayIndex)
+ {
+ _actual.CopyTo(array, arrayIndex);
+ }
+
+ public int Count
+ {
+ get { return _actual.Count; }
+ }
+
+ public bool IsReadOnly
+ {
+ get { return _isReadOnly; }
+ set
+ {
+ if (_isReadOnly == value)
+ return;
+ if (!value)
+ throw new InvalidOperationException("Can't change this back to non readonly");
+ _isReadOnly = value;
+ }
+ }
+
+ public bool Remove(T item)
+ {
+ if (IsReadOnly)
+ throw new InvalidOperationException("This list is ReadOnly");
+ return _actual.Remove(item);
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return ((IEnumerable)_actual).GetEnumerator();
+ }
+
+ public IEnumerator<T> GetEnumerator()
+ {
+ return _actual.GetEnumerator();
+ }
+
+ public int IndexOf(T item)
+ {
+ return _actual.IndexOf(item);
+ }
+
+ public void Insert(int index, T item)
+ {
+ if (IsReadOnly)
+ throw new InvalidOperationException("This list is ReadOnly");
+ _actual.Insert(index, item);
+ }
+
+ public T this[int index]
+ {
+ get { return _actual[index]; }
+ set
+ {
+ if (IsReadOnly)
+ throw new InvalidOperationException("This list is ReadOnly");
+ _actual[index] = value;
+ }
+ }
+
+ public void RemoveAt(int index)
+ {
+ if (IsReadOnly)
+ throw new InvalidOperationException("This list is ReadOnly");
+ _actual.RemoveAt(index);
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/Internals/DynamicResource.cs b/Xamarin.Forms.Core/Internals/DynamicResource.cs
new file mode 100644
index 00000000..3724daec
--- /dev/null
+++ b/Xamarin.Forms.Core/Internals/DynamicResource.cs
@@ -0,0 +1,12 @@
+namespace Xamarin.Forms.Internals
+{
+ public class DynamicResource
+ {
+ public DynamicResource(string key)
+ {
+ Key = key;
+ }
+
+ public string Key { get; private set; }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/Internals/IDataTemplate.cs b/Xamarin.Forms.Core/Internals/IDataTemplate.cs
new file mode 100644
index 00000000..d6947ae8
--- /dev/null
+++ b/Xamarin.Forms.Core/Internals/IDataTemplate.cs
@@ -0,0 +1,10 @@
+using System;
+
+namespace Xamarin.Forms.Internals
+{
+ [Obsolete]
+ public interface IDataTemplate
+ {
+ Func<object> LoadTemplate { get; set; }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/Internals/IDynamicResourceHandler.cs b/Xamarin.Forms.Core/Internals/IDynamicResourceHandler.cs
new file mode 100644
index 00000000..1885d8ab
--- /dev/null
+++ b/Xamarin.Forms.Core/Internals/IDynamicResourceHandler.cs
@@ -0,0 +1,7 @@
+namespace Xamarin.Forms.Internals
+{
+ public interface IDynamicResourceHandler
+ {
+ void SetDynamicResource(BindableProperty property, string key);
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/Internals/INameScope.cs b/Xamarin.Forms.Core/Internals/INameScope.cs
new file mode 100644
index 00000000..ca520605
--- /dev/null
+++ b/Xamarin.Forms.Core/Internals/INameScope.cs
@@ -0,0 +1,12 @@
+using System.Xml;
+
+namespace Xamarin.Forms.Internals
+{
+ public interface INameScope
+ {
+ object FindByName(string name);
+ void RegisterName(string name, object scopedElement);
+ void RegisterName(string name, object scopedElement, IXmlLineInfo xmlLineInfo);
+ void UnregisterName(string name);
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/Internals/NameScope.cs b/Xamarin.Forms.Core/Internals/NameScope.cs
new file mode 100644
index 00000000..b29534b6
--- /dev/null
+++ b/Xamarin.Forms.Core/Internals/NameScope.cs
@@ -0,0 +1,56 @@
+using System;
+using System.Collections.Generic;
+using System.Xml;
+using Xamarin.Forms.Xaml;
+
+namespace Xamarin.Forms.Internals
+{
+ public class NameScope : INameScope
+ {
+ public static readonly BindableProperty NameScopeProperty = BindableProperty.CreateAttached("NameScope", typeof(INameScope), typeof(NameScope), default(INameScope));
+
+ readonly Dictionary<string, object> _names = new Dictionary<string, object>();
+
+ object INameScope.FindByName(string name)
+ {
+ if (_names.ContainsKey(name))
+ return _names[name];
+ return null;
+ }
+
+ void INameScope.RegisterName(string name, object scopedElement)
+ {
+ if (_names.ContainsKey(name))
+ throw new ArgumentException("An element with the same key already exists in NameScope", "name");
+
+ _names[name] = scopedElement;
+ }
+
+ void INameScope.RegisterName(string name, object scopedElement, IXmlLineInfo xmlLineInfo)
+ {
+ try
+ {
+ ((INameScope)this).RegisterName(name, scopedElement);
+ }
+ catch (ArgumentException)
+ {
+ throw new XamlParseException(string.Format("An element with the name \"{0}\" already exists in this NameScope", name), xmlLineInfo);
+ }
+ }
+
+ void INameScope.UnregisterName(string name)
+ {
+ _names.Remove(name);
+ }
+
+ public static INameScope GetNameScope(BindableObject bindable)
+ {
+ return (INameScope)bindable.GetValue(NameScopeProperty);
+ }
+
+ public static void SetNameScope(BindableObject bindable, INameScope value)
+ {
+ bindable.SetValue(NameScopeProperty, value);
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/InvalidNavigationException.cs b/Xamarin.Forms.Core/InvalidNavigationException.cs
new file mode 100644
index 00000000..fce5b314
--- /dev/null
+++ b/Xamarin.Forms.Core/InvalidNavigationException.cs
@@ -0,0 +1,11 @@
+using System;
+
+namespace Xamarin.Forms
+{
+ internal class InvalidNavigationException : Exception
+ {
+ public InvalidNavigationException(string message) : base(message)
+ {
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/InvalidationEventArgs.cs b/Xamarin.Forms.Core/InvalidationEventArgs.cs
new file mode 100644
index 00000000..d88e2519
--- /dev/null
+++ b/Xamarin.Forms.Core/InvalidationEventArgs.cs
@@ -0,0 +1,14 @@
+using System;
+
+namespace Xamarin.Forms
+{
+ internal class InvalidationEventArgs : EventArgs
+ {
+ public InvalidationEventArgs(InvalidationTrigger trigger)
+ {
+ Trigger = trigger;
+ }
+
+ public InvalidationTrigger Trigger { get; private set; }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/InvalidationTrigger.cs b/Xamarin.Forms.Core/InvalidationTrigger.cs
new file mode 100644
index 00000000..e7db534e
--- /dev/null
+++ b/Xamarin.Forms.Core/InvalidationTrigger.cs
@@ -0,0 +1,16 @@
+using System;
+
+namespace Xamarin.Forms
+{
+ [Flags]
+ internal enum InvalidationTrigger
+ {
+ Undefined = 0,
+ MeasureChanged = 1 << 0,
+ HorizontalOptionsChanged = 1 << 1,
+ VerticalOptionsChanged = 1 << 2,
+ SizeRequestChanged = 1 << 3,
+ RendererReady = 1 << 4,
+ MarginChanged = 1 << 5
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/ItemTappedEventArgs.cs b/Xamarin.Forms.Core/ItemTappedEventArgs.cs
new file mode 100644
index 00000000..9fde19bd
--- /dev/null
+++ b/Xamarin.Forms.Core/ItemTappedEventArgs.cs
@@ -0,0 +1,17 @@
+using System;
+
+namespace Xamarin.Forms
+{
+ public class ItemTappedEventArgs : EventArgs
+ {
+ public ItemTappedEventArgs(object group, object item)
+ {
+ Group = group;
+ Item = item;
+ }
+
+ public object Group { get; private set; }
+
+ public object Item { get; private set; }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/ItemVisibilityEventArgs.cs b/Xamarin.Forms.Core/ItemVisibilityEventArgs.cs
new file mode 100644
index 00000000..48166c13
--- /dev/null
+++ b/Xamarin.Forms.Core/ItemVisibilityEventArgs.cs
@@ -0,0 +1,14 @@
+using System;
+
+namespace Xamarin.Forms
+{
+ public sealed class ItemVisibilityEventArgs : EventArgs
+ {
+ public ItemVisibilityEventArgs(object item)
+ {
+ Item = item;
+ }
+
+ public object Item { get; private set; }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/ItemsView.cs b/Xamarin.Forms.Core/ItemsView.cs
new file mode 100644
index 00000000..51c105bd
--- /dev/null
+++ b/Xamarin.Forms.Core/ItemsView.cs
@@ -0,0 +1,92 @@
+using System.Collections;
+
+namespace Xamarin.Forms
+{
+ public abstract class ItemsView<TVisual> : View, IItemsView<TVisual> where TVisual : BindableObject
+ {
+ /*
+ public static readonly BindableProperty InfiniteScrollingProperty =
+ BindableProperty.Create<ItemsView, bool> (lv => lv.InfiniteScrolling, false);
+
+ public bool InfiniteScrolling
+ {
+ get { return (bool) GetValue (InfiniteScrollingProperty); }
+ set { SetValue (InfiniteScrollingProperty, value); }
+ }*/
+
+ public static readonly BindableProperty ItemsSourceProperty = BindableProperty.Create("ItemsSource", typeof(IEnumerable), typeof(ItemsView<TVisual>), null, propertyChanged: OnItemsSourceChanged);
+
+ public static readonly BindableProperty ItemTemplateProperty = BindableProperty.Create("ItemTemplate", typeof(DataTemplate), typeof(ItemsView<TVisual>), null, validateValue: ValidateItemTemplate);
+
+ internal ItemsView()
+ {
+ TemplatedItems = new TemplatedItemsList<ItemsView<TVisual>, TVisual>(this, ItemsSourceProperty, ItemTemplateProperty);
+ }
+
+ public IEnumerable ItemsSource
+ {
+ get { return (IEnumerable)GetValue(ItemsSourceProperty); }
+ set { SetValue(ItemsSourceProperty, value); }
+ }
+
+ public DataTemplate ItemTemplate
+ {
+ get { return (DataTemplate)GetValue(ItemTemplateProperty); }
+ set { SetValue(ItemTemplateProperty, value); }
+ }
+
+ /*public void UpdateNonNotifyingList()
+ {
+ this.templatedItems.ForceUpdate();
+ }*/
+
+ internal ListProxy ListProxy
+ {
+ get { return TemplatedItems.ListProxy; }
+ }
+
+ internal TemplatedItemsList<ItemsView<TVisual>, TVisual> TemplatedItems { get; }
+
+ TVisual IItemsView<TVisual>.CreateDefault(object item)
+ {
+ return CreateDefault(item);
+ }
+
+ void IItemsView<TVisual>.SetupContent(TVisual content, int index)
+ {
+ SetupContent(content, index);
+ }
+
+ void IItemsView<TVisual>.UnhookContent(TVisual content)
+ {
+ UnhookContent(content);
+ }
+
+ protected abstract TVisual CreateDefault(object item);
+
+ protected virtual void SetupContent(TVisual content, int index)
+ {
+ }
+
+ protected virtual void UnhookContent(TVisual content)
+ {
+ }
+
+ static void OnItemsSourceChanged(BindableObject bindable, object oldValue, object newValue)
+ {
+ var element = newValue as Element;
+ if (element == null)
+ return;
+ element.Parent = (Element)bindable;
+ }
+
+ static bool ValidateItemTemplate(BindableObject b, object v)
+ {
+ var lv = b as ListView;
+ if (lv == null)
+ return true;
+
+ return !(lv.CachingStrategy == ListViewCachingStrategy.RetainElement && lv.ItemTemplate is DataTemplateSelector);
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/ItemsViewSimple.cs b/Xamarin.Forms.Core/ItemsViewSimple.cs
new file mode 100644
index 00000000..fd631838
--- /dev/null
+++ b/Xamarin.Forms.Core/ItemsViewSimple.cs
@@ -0,0 +1,335 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Collections.Specialized;
+using System.Linq;
+using System.Runtime.CompilerServices;
+
+namespace Xamarin.Forms
+{
+ public abstract class ItemsView : View, IItemViewController
+ {
+ public static readonly BindableProperty ItemsSourceProperty = BindableProperty.Create("ItemsSource", typeof(IEnumerable), typeof(ItemsView), Enumerable.Empty<object>());
+
+ public static readonly BindableProperty ItemTemplateProperty = BindableProperty.Create("ItemTemplate", typeof(DataTemplate), typeof(ItemsView));
+
+ ItemSource _itemSource;
+
+ internal ItemsView()
+ {
+ }
+
+ public int Count => _itemSource.Count;
+
+ public IEnumerable ItemsSource
+ {
+ get { return (IEnumerable)GetValue(ItemsSourceProperty); }
+ set { SetValue(ItemsSourceProperty, value); }
+ }
+
+ public DataTemplate ItemTemplate
+ {
+ get { return (DataTemplate)GetValue(ItemTemplateProperty); }
+ set { SetValue(ItemTemplateProperty, value); }
+ }
+
+ void IItemViewController.BindView(View view, object item)
+ {
+ view.BindingContext = item;
+ }
+
+ View IItemViewController.CreateView(object type)
+ {
+ var dataTemplate = (DataTemplate)type;
+ object content = dataTemplate.CreateContent();
+ var view = (View)content;
+ view.Parent = this;
+ return view;
+ }
+
+ object IItemViewController.GetItem(int index) => _itemSource[index];
+
+ object IItemViewController.GetItemType(object item)
+ {
+ DataTemplate dataTemplate = ItemTemplate;
+ var dataTemplateSelector = dataTemplate as DataTemplateSelector;
+ if (dataTemplateSelector != null)
+ dataTemplate = dataTemplateSelector.SelectTemplate(item, this);
+
+ if (item == null)
+ throw new ArgumentException($"No DataTemplate resolved for item: {item}.");
+
+ return dataTemplate;
+ }
+
+ protected override void OnPropertyChanged([CallerMemberName] string propertyName = null)
+ {
+ if (propertyName == nameof(ItemsSource))
+ {
+ // abstract enumerable, IList, IList<T>, and IReadOnlyList<T>
+ _itemSource = new ItemSource(ItemsSource);
+
+ // subscribe to collection changed events
+ var dynamicItemSource = _itemSource as INotifyCollectionChanged;
+ if (dynamicItemSource != null)
+ {
+ new WeakNotifyCollectionChanged(dynamicItemSource, OnCollectionChange);
+ }
+ }
+
+ base.OnPropertyChanged(propertyName);
+ }
+
+ internal event NotifyCollectionChangedEventHandler CollectionChanged;
+
+ void OnCollectionChange(object sender, NotifyCollectionChangedEventArgs e)
+ {
+ CollectionChanged?.Invoke(sender, e);
+ }
+
+ sealed class WeakNotifyCollectionChanged
+ {
+ readonly INotifyCollectionChanged _source;
+ // prevent the itemSource from keeping the itemsView alive
+ readonly WeakReference<NotifyCollectionChangedEventHandler> _weakTarget;
+
+ public WeakNotifyCollectionChanged(INotifyCollectionChanged source, NotifyCollectionChangedEventHandler target)
+ {
+ _weakTarget = new WeakReference<NotifyCollectionChangedEventHandler>(target);
+ _source = source;
+ _source.CollectionChanged += OnCollectionChanged;
+ }
+
+ public void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
+ {
+ NotifyCollectionChangedEventHandler weakTarget;
+ if (!_weakTarget.TryGetTarget(out weakTarget))
+ {
+ _source.CollectionChanged -= OnCollectionChanged;
+ return;
+ }
+
+ weakTarget(sender, e);
+ }
+ }
+
+ sealed class ItemSource : IEnumerable<object>, INotifyCollectionChanged
+ {
+ IndexableCollection _indexable;
+
+ internal ItemSource(IEnumerable enumerable)
+ {
+ _indexable = new IndexableCollection(enumerable);
+ var dynamicItemSource = enumerable as INotifyCollectionChanged;
+ if (dynamicItemSource != null)
+ dynamicItemSource.CollectionChanged += OnCollectionChanged;
+ }
+
+ public int Count => _indexable.Count;
+
+ public IEnumerable Enumerable => _indexable.Enumerable;
+
+ public object this[int index]
+ {
+ get
+ {
+ // madness ported from listProxy
+ CollectionSynchronizationContext syncContext = SyncContext;
+ if (syncContext != null)
+ {
+ object value = null;
+ syncContext.Callback(Enumerable, SyncContext.Context, () => value = _indexable[index], false);
+
+ return value;
+ }
+
+ return _indexable[index];
+ }
+ }
+
+ CollectionSynchronizationContext SyncContext
+ {
+ get
+ {
+ CollectionSynchronizationContext syncContext;
+ BindingBase.TryGetSynchronizedCollection(Enumerable, out syncContext);
+ return syncContext;
+ }
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return GetEnumerator();
+ }
+
+ IEnumerator<object> IEnumerable<object>.GetEnumerator()
+ {
+ return GetEnumerator();
+ }
+
+ public event NotifyCollectionChangedEventHandler CollectionChanged;
+
+ public Enumerator GetEnumerator()
+ {
+ return new Enumerator(this);
+ }
+
+ public int IndexOf(object item)
+ {
+ // madness ported from listProxy
+ CollectionSynchronizationContext syncContext = SyncContext;
+ if (syncContext != null)
+ {
+ int value = -1;
+ syncContext.Callback(Enumerable, SyncContext.Context, () => value = _indexable.IndexOf(item), false);
+
+ return value;
+ }
+
+ return _indexable.IndexOf(item);
+ }
+
+ void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
+ {
+ Action onCollectionChanged = () =>
+ {
+ if (CollectionChanged != null)
+ CollectionChanged(this, e);
+ };
+
+ // madness ported from listProxy
+ CollectionSynchronizationContext syncContext = SyncContext;
+ if (syncContext != null)
+ {
+ syncContext.Callback(Enumerable, syncContext.Context, () => Device.BeginInvokeOnMainThread(onCollectionChanged), false);
+ }
+
+ else if (Device.IsInvokeRequired)
+ Device.BeginInvokeOnMainThread(onCollectionChanged);
+
+ else
+ onCollectionChanged();
+ }
+
+ internal struct Enumerator : IEnumerator<object>
+ {
+ readonly ItemSource _itemSource;
+ int _index;
+
+ internal Enumerator(ItemSource itemSource) : this()
+ {
+ _itemSource = itemSource;
+ }
+
+ public bool MoveNext()
+ {
+ if (_index == _itemSource.Count)
+ return false;
+
+ Current = _itemSource[_index++];
+ return true;
+ }
+
+ public object Current { get; private set; }
+
+ public void Reset()
+ {
+ Current = null;
+ _index = 0;
+ }
+
+ public void Dispose()
+ {
+ }
+ }
+
+ struct IndexableCollection : IEnumerable<object>
+ {
+ internal IndexableCollection(IEnumerable list)
+ {
+ Enumerable = list;
+
+ if (list is IList)
+ return;
+
+ if (list is IList<object>)
+ return;
+
+ if (list is IReadOnlyList<object>)
+ return;
+
+ Enumerable = list.Cast<object>().ToArray();
+ }
+
+ internal IEnumerable Enumerable { get; }
+
+ internal int Count
+ {
+ get
+ {
+ var list = Enumerable as IList;
+ if (list != null)
+ return list.Count;
+
+ var listOf = Enumerable as IList<object>;
+ if (listOf != null)
+ return listOf.Count;
+
+ var readOnlyList = (IReadOnlyList<object>)Enumerable;
+ return readOnlyList.Count;
+ }
+ }
+
+ internal object this[int index]
+ {
+ get
+ {
+ var list = Enumerable as IList;
+ if (list != null)
+ return list[index];
+
+ var listOf = Enumerable as IList<object>;
+ if (listOf != null)
+ return listOf[index];
+
+ var readOnlyList = (IReadOnlyList<object>)Enumerable;
+ return readOnlyList[index];
+ }
+ }
+
+ internal int IndexOf(object item)
+ {
+ var list = Enumerable as IList;
+ if (list != null)
+ return list.IndexOf(item);
+
+ var listOf = Enumerable as IList<object>;
+ if (listOf != null)
+ return listOf.IndexOf(item);
+
+ var readOnlyList = (IReadOnlyList<object>)Enumerable;
+ return readOnlyList.IndexOf(item);
+ }
+
+ public IEnumerator<object> GetEnumerator()
+ {
+ var list = Enumerable as IList;
+ if (list != null)
+ return list.Cast<object>().GetEnumerator();
+
+ var listOf = Enumerable as IList<object>;
+ if (listOf != null)
+ return listOf.GetEnumerator();
+
+ var readOnlyList = (IReadOnlyList<object>)Enumerable;
+ return readOnlyList.GetEnumerator();
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return GetEnumerator();
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/Keyboard.cs b/Xamarin.Forms.Core/Keyboard.cs
new file mode 100644
index 00000000..56d5ca8c
--- /dev/null
+++ b/Xamarin.Forms.Core/Keyboard.cs
@@ -0,0 +1,64 @@
+namespace Xamarin.Forms
+{
+ [TypeConverter(typeof(KeyboardTypeConverter))]
+ public class Keyboard
+ {
+ static Keyboard s_def;
+
+ static Keyboard s_email;
+
+ static Keyboard s_text;
+
+ static Keyboard s_url;
+
+ static Keyboard s_numeric;
+
+ static Keyboard s_telephone;
+
+ static Keyboard s_chat;
+
+ internal Keyboard()
+ {
+ }
+
+ public static Keyboard Chat
+ {
+ get { return s_chat ?? (s_chat = new ChatKeyboard()); }
+ }
+
+ public static Keyboard Default
+ {
+ get { return s_def ?? (s_def = new Keyboard()); }
+ }
+
+ public static Keyboard Email
+ {
+ get { return s_email ?? (s_email = new EmailKeyboard()); }
+ }
+
+ public static Keyboard Numeric
+ {
+ get { return s_numeric ?? (s_numeric = new NumericKeyboard()); }
+ }
+
+ public static Keyboard Telephone
+ {
+ get { return s_telephone ?? (s_telephone = new TelephoneKeyboard()); }
+ }
+
+ public static Keyboard Text
+ {
+ get { return s_text ?? (s_text = new TextKeyboard()); }
+ }
+
+ public static Keyboard Url
+ {
+ get { return s_url ?? (s_url = new UrlKeyboard()); }
+ }
+
+ public static Keyboard Create(KeyboardFlags flags)
+ {
+ return new CustomKeyboard(flags);
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/KeyboardFlags.cs b/Xamarin.Forms.Core/KeyboardFlags.cs
new file mode 100644
index 00000000..5ac2f1cc
--- /dev/null
+++ b/Xamarin.Forms.Core/KeyboardFlags.cs
@@ -0,0 +1,13 @@
+using System;
+
+namespace Xamarin.Forms
+{
+ [Flags]
+ public enum KeyboardFlags
+ {
+ CapitalizeSentence = 1,
+ Spellcheck = 1 << 1,
+ Suggestions = 1 << 2,
+ All = ~0
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/KeyboardTypeConverter.cs b/Xamarin.Forms.Core/KeyboardTypeConverter.cs
new file mode 100644
index 00000000..4a93010b
--- /dev/null
+++ b/Xamarin.Forms.Core/KeyboardTypeConverter.cs
@@ -0,0 +1,29 @@
+using System;
+using System.Linq;
+using System.Reflection;
+
+namespace Xamarin.Forms
+{
+ public class KeyboardTypeConverter : TypeConverter
+ {
+ public override object ConvertFromInvariantString(string value)
+ {
+ if (value != null)
+ {
+ string[] parts = value.Split('.');
+ if (parts.Length == 1 || (parts.Length == 2 && parts[0] == "Keyboard"))
+ {
+ string keyboard = parts[parts.Length - 1];
+ FieldInfo field = typeof(Keyboard).GetFields().FirstOrDefault(fi => fi.IsStatic && fi.Name == keyboard);
+ if (field != null)
+ return (Keyboard)field.GetValue(null);
+ PropertyInfo property = typeof(Keyboard).GetProperties().FirstOrDefault(pi => pi.Name == keyboard && pi.CanRead && pi.GetMethod.IsStatic);
+ if (property != null)
+ return (Keyboard)property.GetValue(null, null);
+ }
+ }
+
+ throw new InvalidOperationException(string.Format("Cannot convert \"{0}\" into {1}", value, typeof(Keyboard)));
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/Label.cs b/Xamarin.Forms.Core/Label.cs
new file mode 100644
index 00000000..fed021f8
--- /dev/null
+++ b/Xamarin.Forms.Core/Label.cs
@@ -0,0 +1,270 @@
+using System;
+using System.ComponentModel;
+using Xamarin.Forms.Platform;
+
+namespace Xamarin.Forms
+{
+ [ContentProperty("Text")]
+ [RenderWith(typeof(_LabelRenderer))]
+ public class Label : View, IFontElement
+ {
+ public static readonly BindableProperty HorizontalTextAlignmentProperty = BindableProperty.Create("HorizontalTextAlignment", typeof(TextAlignment), typeof(Label), TextAlignment.Start,
+ propertyChanged: OnHorizontalTextAlignmentPropertyChanged);
+
+ [Obsolete("XAlignProperty is obsolete. Please use HorizontalTextAlignmentProperty instead.")] public static readonly BindableProperty XAlignProperty = HorizontalTextAlignmentProperty;
+
+ public static readonly BindableProperty VerticalTextAlignmentProperty = BindableProperty.Create("VerticalTextAlignment", typeof(TextAlignment), typeof(Label), TextAlignment.Start,
+ propertyChanged: OnVerticalTextAlignmentPropertyChanged);
+
+ [Obsolete("YAlignProperty is obsolete. Please use VerticalTextAlignmentProperty instead.")] public static readonly BindableProperty YAlignProperty = VerticalTextAlignmentProperty;
+
+ public static readonly BindableProperty TextColorProperty = BindableProperty.Create("TextColor", typeof(Color), typeof(Label), Color.Default);
+
+ public static readonly BindableProperty FontProperty = BindableProperty.Create("Font", typeof(Font), typeof(Label), default(Font), propertyChanged: FontStructPropertyChanged);
+
+ public static readonly BindableProperty TextProperty = BindableProperty.Create("Text", typeof(string), typeof(Label), default(string), propertyChanged: OnTextPropertyChanged);
+
+ public static readonly BindableProperty FontFamilyProperty = BindableProperty.Create("FontFamily", typeof(string), typeof(Label), default(string), propertyChanged: OnFontFamilyChanged);
+
+ public static readonly BindableProperty FontSizeProperty = BindableProperty.Create("FontSize", typeof(double), typeof(Label), -1.0, propertyChanged: OnFontSizeChanged,
+ defaultValueCreator: bindable => Device.GetNamedSize(NamedSize.Default, (Label)bindable));
+
+ public static readonly BindableProperty FontAttributesProperty = BindableProperty.Create("FontAttributes", typeof(FontAttributes), typeof(Label), FontAttributes.None,
+ propertyChanged: OnFontAttributesChanged);
+
+ public static readonly BindableProperty FormattedTextProperty = BindableProperty.Create("FormattedText", typeof(FormattedString), typeof(Label), default(FormattedString),
+ propertyChanging: (bindable, oldvalue, newvalue) =>
+ {
+ if (oldvalue != null)
+ ((FormattedString)oldvalue).PropertyChanged -= ((Label)bindable).OnFormattedTextChanged;
+ }, propertyChanged: (bindable, oldvalue, newvalue) =>
+ {
+ if (newvalue != null)
+ ((FormattedString)newvalue).PropertyChanged += ((Label)bindable).OnFormattedTextChanged;
+ ((Label)bindable).InvalidateMeasure(InvalidationTrigger.MeasureChanged);
+ if (newvalue != null)
+ ((Label)bindable).Text = null;
+ });
+
+ public static readonly BindableProperty LineBreakModeProperty = BindableProperty.Create("LineBreakMode", typeof(LineBreakMode), typeof(Label), LineBreakMode.WordWrap,
+ propertyChanged: (bindable, oldvalue, newvalue) => ((Label)bindable).InvalidateMeasure(InvalidationTrigger.MeasureChanged));
+
+ bool _cancelEvents;
+
+ [Obsolete("Please use the Font attributes which are on the class itself. Obsoleted in v1.3.0")]
+ public Font Font
+ {
+ get { return (Font)GetValue(FontProperty); }
+ set { SetValue(FontProperty, value); }
+ }
+
+ public FormattedString FormattedText
+ {
+ get { return (FormattedString)GetValue(FormattedTextProperty); }
+ set { SetValue(FormattedTextProperty, value); }
+ }
+
+ public TextAlignment HorizontalTextAlignment
+ {
+ get { return (TextAlignment)GetValue(HorizontalTextAlignmentProperty); }
+ set { SetValue(HorizontalTextAlignmentProperty, value); }
+ }
+
+ public LineBreakMode LineBreakMode
+ {
+ get { return (LineBreakMode)GetValue(LineBreakModeProperty); }
+ set { SetValue(LineBreakModeProperty, value); }
+ }
+
+ public string Text
+ {
+ get { return (string)GetValue(TextProperty); }
+ set { SetValue(TextProperty, value); }
+ }
+
+ public Color TextColor
+ {
+ get { return (Color)GetValue(TextColorProperty); }
+ set { SetValue(TextColorProperty, value); }
+ }
+
+ public TextAlignment VerticalTextAlignment
+ {
+ get { return (TextAlignment)GetValue(VerticalTextAlignmentProperty); }
+ set { SetValue(VerticalTextAlignmentProperty, value); }
+ }
+
+ [Obsolete("XAlign is obsolete. Please use HorizontalTextAlignment instead.")]
+ public TextAlignment XAlign
+ {
+ get { return (TextAlignment)GetValue(XAlignProperty); }
+ set { SetValue(XAlignProperty, value); }
+ }
+
+ [Obsolete("YAlign is obsolete. Please use VerticalTextAlignment instead.")]
+ public TextAlignment YAlign
+ {
+ get { return (TextAlignment)GetValue(YAlignProperty); }
+ set { SetValue(YAlignProperty, value); }
+ }
+
+ public FontAttributes FontAttributes
+ {
+ get { return (FontAttributes)GetValue(FontAttributesProperty); }
+ set { SetValue(FontAttributesProperty, value); }
+ }
+
+ public string FontFamily
+ {
+ get { return (string)GetValue(FontFamilyProperty); }
+ set { SetValue(FontFamilyProperty, value); }
+ }
+
+ [TypeConverter(typeof(FontSizeConverter))]
+ public double FontSize
+ {
+ get { return (double)GetValue(FontSizeProperty); }
+ set { SetValue(FontSizeProperty, value); }
+ }
+
+ static void FontStructPropertyChanged(BindableObject bindable, object oldValue, object newValue)
+ {
+ var label = (Label)bindable;
+ if (label._cancelEvents)
+ return;
+
+ label._cancelEvents = true;
+
+ var font = (Font)newValue;
+ if (font == Font.Default)
+ {
+ label.FontFamily = null;
+ label.FontSize = Device.GetNamedSize(NamedSize.Default, label);
+ label.FontAttributes = FontAttributes.None;
+ }
+ else
+ {
+ label.FontFamily = font.FontFamily;
+ if (font.UseNamedSize)
+ {
+ label.FontSize = Device.GetNamedSize(font.NamedSize, label.GetType(), true);
+ }
+ else
+ {
+ label.FontSize = font.FontSize;
+ }
+ label.FontAttributes = font.FontAttributes;
+ }
+
+ label._cancelEvents = false;
+
+ label.InvalidateMeasure(InvalidationTrigger.MeasureChanged);
+ }
+
+ static void OnFontAttributesChanged(BindableObject bindable, object oldValue, object newValue)
+ {
+ var label = (Label)bindable;
+
+ if (label._cancelEvents)
+ return;
+
+ label._cancelEvents = true;
+
+ var attributes = (FontAttributes)newValue;
+
+ object[] values = label.GetValues(FontFamilyProperty, FontSizeProperty);
+ var family = (string)values[0];
+ if (family != null)
+ {
+ label.Font = Font.OfSize(family, (double)values[1]).WithAttributes(attributes);
+ }
+ else
+ {
+ label.Font = Font.SystemFontOfSize((double)values[1], attributes);
+ }
+
+ label._cancelEvents = false;
+
+ label.InvalidateMeasure(InvalidationTrigger.MeasureChanged);
+ }
+
+ static void OnFontFamilyChanged(BindableObject bindable, object oldValue, object newValue)
+ {
+ var label = (Label)bindable;
+ if (label._cancelEvents)
+ return;
+
+ label._cancelEvents = true;
+
+ object[] values = label.GetValues(FontSizeProperty, FontAttributesProperty);
+
+ var family = (string)newValue;
+ if (family != null)
+ {
+ label.Font = Font.OfSize(family, (double)values[0]).WithAttributes((FontAttributes)values[1]);
+ }
+ else
+ {
+ label.Font = Font.SystemFontOfSize((double)values[0], (FontAttributes)values[1]);
+ }
+
+ label._cancelEvents = false;
+ label.InvalidateMeasure(InvalidationTrigger.MeasureChanged);
+ }
+
+ static void OnFontSizeChanged(BindableObject bindable, object oldValue, object newValue)
+ {
+ var label = (Label)bindable;
+ if (label._cancelEvents)
+ return;
+
+ label._cancelEvents = true;
+
+ object[] values = label.GetValues(FontFamilyProperty, FontAttributesProperty);
+
+ var size = (double)newValue;
+ var family = (string)values[0];
+ if (family != null)
+ {
+ label.Font = Font.OfSize(family, size).WithAttributes((FontAttributes)values[1]);
+ }
+ else
+ {
+ label.Font = Font.SystemFontOfSize(size, (FontAttributes)values[1]);
+ }
+
+ label._cancelEvents = false;
+
+ label.InvalidateMeasure(InvalidationTrigger.MeasureChanged);
+ }
+
+ void OnFormattedTextChanged(object sender, PropertyChangedEventArgs e)
+ {
+ OnPropertyChanged("FormattedText");
+ }
+
+ static void OnHorizontalTextAlignmentPropertyChanged(BindableObject bindable, object oldValue, object newValue)
+ {
+ var label = (Label)bindable;
+ label.OnPropertyChanged(nameof(XAlign));
+ }
+
+ static void OnTextPropertyChanged(BindableObject bindable, object oldvalue, object newvalue)
+ {
+ var label = (Label)bindable;
+ LineBreakMode breakMode = label.LineBreakMode;
+ bool isVerticallyFixed = (label.Constraint & LayoutConstraint.VerticallyFixed) != 0;
+ bool isSingleLine = !(breakMode == LineBreakMode.CharacterWrap || breakMode == LineBreakMode.WordWrap);
+ if (!isVerticallyFixed || !isSingleLine)
+ ((Label)bindable).InvalidateMeasure(InvalidationTrigger.MeasureChanged);
+ if (newvalue != null)
+ ((Label)bindable).FormattedText = null;
+ }
+
+ static void OnVerticalTextAlignmentPropertyChanged(BindableObject bindable, object oldValue, object newValue)
+ {
+ var label = (Label)bindable;
+ label.OnPropertyChanged(nameof(YAlign));
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/Layout.cs b/Xamarin.Forms.Core/Layout.cs
new file mode 100644
index 00000000..c611777c
--- /dev/null
+++ b/Xamarin.Forms.Core/Layout.cs
@@ -0,0 +1,433 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Collections.Specialized;
+using System.Linq;
+
+namespace Xamarin.Forms
+{
+ [ContentProperty("Children")]
+ public abstract class Layout<T> : Layout, IViewContainer<T> where T : View
+ {
+ readonly ElementCollection<T> _children;
+
+ protected Layout()
+ {
+ _children = new ElementCollection<T>(InternalChildren);
+ }
+
+ public IList<T> Children
+ {
+ get { return _children; }
+ }
+
+ protected virtual void OnAdded(T view)
+ {
+ }
+
+ protected override void OnChildAdded(Element child)
+ {
+ base.OnChildAdded(child);
+
+ var typedChild = child as T;
+ if (typedChild != null)
+ OnAdded(typedChild);
+ }
+
+ protected override void OnChildRemoved(Element child)
+ {
+ base.OnChildRemoved(child);
+
+ var typedChild = child as T;
+ if (typedChild != null)
+ OnRemoved(typedChild);
+ }
+
+ protected virtual void OnRemoved(T view)
+ {
+ }
+ }
+
+ public abstract class Layout : View, ILayout, ILayoutController
+ {
+ public static readonly BindableProperty IsClippedToBoundsProperty = BindableProperty.Create("IsClippedToBounds", typeof(bool), typeof(Layout), false);
+
+ public static readonly BindableProperty PaddingProperty = BindableProperty.Create("Padding", typeof(Thickness), typeof(Layout), default(Thickness), propertyChanged: (bindable, old, newValue) =>
+ {
+ var layout = (Layout)bindable;
+ layout.UpdateChildrenLayout();
+ });
+
+ static IList<KeyValuePair<Layout, int>> s_resolutionList = new List<KeyValuePair<Layout, int>>();
+ static bool s_relayoutInProgress;
+ bool _allocatedFlag;
+
+ bool _hasDoneLayout;
+ Size _lastLayoutSize = new Size(-1, -1);
+
+ ReadOnlyCollection<Element> _logicalChildren;
+
+ protected Layout()
+ {
+ InternalChildren.CollectionChanged += InternalChildrenOnCollectionChanged;
+ }
+
+ public bool IsClippedToBounds
+ {
+ get { return (bool)GetValue(IsClippedToBoundsProperty); }
+ set { SetValue(IsClippedToBoundsProperty, value); }
+ }
+
+ public Thickness Padding
+ {
+ get { return (Thickness)GetValue(PaddingProperty); }
+ set { SetValue(PaddingProperty, value); }
+ }
+
+ internal ObservableCollection<Element> InternalChildren { get; } = new ObservableCollection<Element>();
+
+ internal override ReadOnlyCollection<Element> LogicalChildren
+ {
+ get { return _logicalChildren ?? (_logicalChildren = new ReadOnlyCollection<Element>(InternalChildren)); }
+ }
+
+ public event EventHandler LayoutChanged;
+
+ IReadOnlyList<Element> ILayoutController.Children
+ {
+ get { return InternalChildren; }
+ }
+
+ public void ForceLayout()
+ {
+ SizeAllocated(Width, Height);
+ }
+
+ [Obsolete("Use Measure")]
+ public sealed override SizeRequest GetSizeRequest(double widthConstraint, double heightConstraint)
+ {
+ SizeRequest size = base.GetSizeRequest(widthConstraint - Padding.HorizontalThickness, heightConstraint - Padding.VerticalThickness);
+ return new SizeRequest(new Size(size.Request.Width + Padding.HorizontalThickness, size.Request.Height + Padding.VerticalThickness),
+ new Size(size.Minimum.Width + Padding.HorizontalThickness, size.Minimum.Height + Padding.VerticalThickness));
+ }
+
+ public static void LayoutChildIntoBoundingRegion(VisualElement child, Rectangle region)
+ {
+ var view = child as View;
+ if (view == null)
+ {
+ child.Layout(region);
+ return;
+ }
+
+ LayoutOptions horizontalOptions = view.HorizontalOptions;
+ if (horizontalOptions.Alignment != LayoutAlignment.Fill)
+ {
+ SizeRequest request = child.Measure(region.Width, region.Height, MeasureFlags.IncludeMargins);
+ double diff = Math.Max(0, region.Width - request.Request.Width);
+ region.X += (int)(diff * horizontalOptions.Alignment.ToDouble());
+ region.Width -= diff;
+ }
+
+ LayoutOptions verticalOptions = view.VerticalOptions;
+ if (verticalOptions.Alignment != LayoutAlignment.Fill)
+ {
+ SizeRequest request = child.Measure(region.Width, region.Height, MeasureFlags.IncludeMargins);
+ double diff = Math.Max(0, region.Height - request.Request.Height);
+ region.Y += (int)(diff * verticalOptions.Alignment.ToDouble());
+ region.Height -= diff;
+ }
+
+ Thickness margin = view.Margin;
+ region.X += margin.Left;
+ region.Width -= margin.HorizontalThickness;
+ region.Y += margin.Top;
+ region.Height -= margin.VerticalThickness;
+
+ child.Layout(region);
+ }
+
+ public void LowerChild(View view)
+ {
+ if (!InternalChildren.Contains(view) || InternalChildren.First() == view)
+ return;
+
+ InternalChildren.Move(InternalChildren.IndexOf(view), 0);
+ OnChildrenReordered();
+ }
+
+ public void RaiseChild(View view)
+ {
+ if (!InternalChildren.Contains(view) || InternalChildren.Last() == view)
+ return;
+
+ InternalChildren.Move(InternalChildren.IndexOf(view), InternalChildren.Count - 1);
+ OnChildrenReordered();
+ }
+
+ protected virtual void InvalidateLayout()
+ {
+ _hasDoneLayout = false;
+ InvalidateMeasure(InvalidationTrigger.MeasureChanged);
+ if (!_hasDoneLayout)
+ ForceLayout();
+ }
+
+ protected abstract void LayoutChildren(double x, double y, double width, double height);
+
+ protected void OnChildMeasureInvalidated(object sender, EventArgs e)
+ {
+ InvalidationTrigger trigger = (e as InvalidationEventArgs)?.Trigger ?? InvalidationTrigger.Undefined;
+ OnChildMeasureInvalidated((VisualElement)sender, trigger);
+ OnChildMeasureInvalidated();
+ }
+
+ protected virtual void OnChildMeasureInvalidated()
+ {
+ }
+
+ protected override void OnSizeAllocated(double width, double height)
+ {
+ _allocatedFlag = true;
+ base.OnSizeAllocated(width, height);
+ UpdateChildrenLayout();
+ }
+
+ protected virtual bool ShouldInvalidateOnChildAdded(View child)
+ {
+ return true;
+ }
+
+ protected virtual bool ShouldInvalidateOnChildRemoved(View child)
+ {
+ return true;
+ }
+
+ protected void UpdateChildrenLayout()
+ {
+ _hasDoneLayout = true;
+
+ if (!ShouldLayoutChildren())
+ return;
+
+ var oldBounds = new Rectangle[LogicalChildren.Count];
+ for (var index = 0; index < oldBounds.Length; index++)
+ {
+ var c = (VisualElement)LogicalChildren[index];
+ oldBounds[index] = c.Bounds;
+ }
+
+ double width = Width;
+ double height = Height;
+
+ double x = Padding.Left;
+ double y = Padding.Top;
+ double w = Math.Max(0, width - Padding.HorizontalThickness);
+ double h = Math.Max(0, height - Padding.VerticalThickness);
+
+ LayoutChildren(x, y, w, h);
+
+ for (var i = 0; i < oldBounds.Length; i++)
+ {
+ Rectangle oldBound = oldBounds[i];
+ Rectangle newBound = ((VisualElement)LogicalChildren[i]).Bounds;
+ if (oldBound != newBound)
+ {
+ EventHandler handler = LayoutChanged;
+ if (handler != null)
+ handler(this, EventArgs.Empty);
+ return;
+ }
+ }
+
+ _lastLayoutSize = new Size(width, height);
+ }
+
+ internal static void LayoutChildIntoBoundingRegion(View child, Rectangle region, SizeRequest childSizeRequest)
+ {
+ if (region.Size != childSizeRequest.Request)
+ {
+ bool canUseAlreadyDoneRequest = region.Width >= childSizeRequest.Request.Width && region.Height >= childSizeRequest.Request.Height;
+
+ if (child.HorizontalOptions.Alignment != LayoutAlignment.Fill)
+ {
+ SizeRequest request = canUseAlreadyDoneRequest ? childSizeRequest : child.Measure(region.Width, region.Height, MeasureFlags.IncludeMargins);
+ double diff = Math.Max(0, region.Width - request.Request.Width);
+ region.X += (int)(diff * child.HorizontalOptions.Alignment.ToDouble());
+ region.Width -= diff;
+ }
+
+ if (child.VerticalOptions.Alignment != LayoutAlignment.Fill)
+ {
+ SizeRequest request = canUseAlreadyDoneRequest ? childSizeRequest : child.Measure(region.Width, region.Height, MeasureFlags.IncludeMargins);
+ double diff = Math.Max(0, region.Height - request.Request.Height);
+ region.Y += (int)(diff * child.VerticalOptions.Alignment.ToDouble());
+ region.Height -= diff;
+ }
+ }
+
+ Thickness margin = child.Margin;
+ region.X += margin.Left;
+ region.Width -= margin.HorizontalThickness;
+ region.Y += margin.Top;
+ region.Height -= margin.VerticalThickness;
+
+ child.Layout(region);
+ }
+
+ internal virtual void OnChildMeasureInvalidated(VisualElement child, InvalidationTrigger trigger)
+ {
+ ReadOnlyCollection<Element> children = LogicalChildren;
+ int count = children.Count;
+ for (var index = 0; index < count; index++)
+ {
+ var v = LogicalChildren[index] as VisualElement;
+ if (v != null && v.IsVisible && (!v.IsPlatformEnabled || !v.IsNativeStateConsistent))
+ return;
+ }
+
+ var view = child as View;
+ if (view != null)
+ {
+ // we can ignore the request if we are either fully constrained or when the size request changes and we were already fully constrainted
+ if ((trigger == InvalidationTrigger.MeasureChanged && view.Constraint == LayoutConstraint.Fixed) ||
+ (trigger == InvalidationTrigger.SizeRequestChanged && view.ComputedConstraint == LayoutConstraint.Fixed))
+ {
+ return;
+ }
+ if (trigger == InvalidationTrigger.HorizontalOptionsChanged || trigger == InvalidationTrigger.VerticalOptionsChanged)
+ {
+ ComputeConstraintForView(view);
+ }
+ }
+
+ _allocatedFlag = false;
+ if (trigger == InvalidationTrigger.RendererReady)
+ {
+ InvalidateMeasure(InvalidationTrigger.RendererReady);
+ }
+ else
+ {
+ InvalidateMeasure(InvalidationTrigger.MeasureChanged);
+ }
+
+ s_resolutionList.Add(new KeyValuePair<Layout, int>(this, GetElementDepth(this)));
+ if (!s_relayoutInProgress)
+ {
+ s_relayoutInProgress = true;
+ Device.BeginInvokeOnMainThread(() =>
+ {
+ // if thread safety mattered we would need to lock this and compareexchange above
+ IList<KeyValuePair<Layout, int>> copy = s_resolutionList;
+ s_resolutionList = new List<KeyValuePair<Layout, int>>();
+ s_relayoutInProgress = false;
+
+ foreach (KeyValuePair<Layout, int> kvp in copy.OrderBy(kvp => kvp.Value))
+ {
+ Layout layout = kvp.Key;
+ double width = layout.Width, height = layout.Height;
+ if (!layout._allocatedFlag && width >= 0 && height >= 0)
+ {
+ layout.SizeAllocated(width, height);
+ }
+ }
+ });
+ }
+ }
+
+ internal override void OnIsVisibleChanged(bool oldValue, bool newValue)
+ {
+ base.OnIsVisibleChanged(oldValue, newValue);
+ if (newValue)
+ {
+ if (_lastLayoutSize != new Size(Width, Height))
+ {
+ UpdateChildrenLayout();
+ }
+ }
+ }
+
+ static int GetElementDepth(Element view)
+ {
+ var result = 0;
+ while (view.Parent != null)
+ {
+ result++;
+ view = view.Parent;
+ }
+ return result;
+ }
+
+ void InternalChildrenOnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
+ {
+ if (e.Action == NotifyCollectionChangedAction.Move)
+ {
+ return;
+ }
+
+ if (e.OldItems != null)
+ {
+ foreach (object item in e.OldItems)
+ {
+ var v = item as View;
+ if (v == null)
+ continue;
+
+ OnInternalRemoved(v);
+ }
+ }
+
+ if (e.NewItems != null)
+ {
+ foreach (object item in e.NewItems)
+ {
+ var v = item as View;
+ if (v == null)
+ continue;
+
+ if (item == this)
+ throw new InvalidOperationException("Can not add self to own child collection.");
+
+ OnInternalAdded(v);
+ }
+ }
+ }
+
+ void OnInternalAdded(View view)
+ {
+ OnChildAdded(view);
+ if (ShouldInvalidateOnChildAdded(view))
+ InvalidateLayout();
+
+ view.MeasureInvalidated += OnChildMeasureInvalidated;
+ }
+
+ void OnInternalRemoved(View view)
+ {
+ view.MeasureInvalidated -= OnChildMeasureInvalidated;
+
+ OnChildRemoved(view);
+ if (ShouldInvalidateOnChildRemoved(view))
+ InvalidateLayout();
+ }
+
+ bool ShouldLayoutChildren()
+ {
+ if (!LogicalChildren.Any() || Width <= 0 || Height <= 0 || !IsVisible || !IsNativeStateConsistent || DisableLayout)
+ return false;
+
+ foreach (Element element in VisibleDescendants())
+ {
+ var visual = element as VisualElement;
+ if (visual == null || !visual.IsVisible)
+ continue;
+
+ if (!visual.IsPlatformEnabled || !visual.IsNativeStateConsistent)
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/LayoutAlignment.cs b/Xamarin.Forms.Core/LayoutAlignment.cs
new file mode 100644
index 00000000..04b03e95
--- /dev/null
+++ b/Xamarin.Forms.Core/LayoutAlignment.cs
@@ -0,0 +1,13 @@
+using System;
+
+namespace Xamarin.Forms
+{
+ [Flags]
+ public enum LayoutAlignment
+ {
+ Start = 0,
+ Center = 1,
+ End = 2,
+ Fill = 3
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/LayoutAlignmentExtensions.cs b/Xamarin.Forms.Core/LayoutAlignmentExtensions.cs
new file mode 100644
index 00000000..a7efcf39
--- /dev/null
+++ b/Xamarin.Forms.Core/LayoutAlignmentExtensions.cs
@@ -0,0 +1,21 @@
+using System;
+
+namespace Xamarin.Forms
+{
+ internal static class LayoutAlignmentExtensions
+ {
+ public static double ToDouble(this LayoutAlignment align)
+ {
+ switch (align)
+ {
+ case LayoutAlignment.Start:
+ return 0;
+ case LayoutAlignment.Center:
+ return 0.5;
+ case LayoutAlignment.End:
+ return 1;
+ }
+ throw new ArgumentOutOfRangeException("align");
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/LayoutConstraint.cs b/Xamarin.Forms.Core/LayoutConstraint.cs
new file mode 100644
index 00000000..a9ddc8ec
--- /dev/null
+++ b/Xamarin.Forms.Core/LayoutConstraint.cs
@@ -0,0 +1,13 @@
+using System;
+
+namespace Xamarin.Forms
+{
+ [Flags]
+ internal enum LayoutConstraint
+ {
+ None = 0,
+ HorizontallyFixed = 1 << 0,
+ VerticallyFixed = 1 << 1,
+ Fixed = HorizontallyFixed | VerticallyFixed
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/LayoutExpandFlag.cs b/Xamarin.Forms.Core/LayoutExpandFlag.cs
new file mode 100644
index 00000000..613d0b4a
--- /dev/null
+++ b/Xamarin.Forms.Core/LayoutExpandFlag.cs
@@ -0,0 +1,10 @@
+using System;
+
+namespace Xamarin.Forms
+{
+ [Flags]
+ internal enum LayoutExpandFlag
+ {
+ Expand = 4
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/LayoutOptions.cs b/Xamarin.Forms.Core/LayoutOptions.cs
new file mode 100644
index 00000000..a3a900b0
--- /dev/null
+++ b/Xamarin.Forms.Core/LayoutOptions.cs
@@ -0,0 +1,39 @@
+using System;
+
+namespace Xamarin.Forms
+{
+ [TypeConverter(typeof(LayoutOptionsConverter))]
+ public struct LayoutOptions
+ {
+ int _flags;
+
+ public static readonly LayoutOptions Start = new LayoutOptions(LayoutAlignment.Start, false);
+ public static readonly LayoutOptions Center = new LayoutOptions(LayoutAlignment.Center, false);
+ public static readonly LayoutOptions End = new LayoutOptions(LayoutAlignment.End, false);
+ public static readonly LayoutOptions Fill = new LayoutOptions(LayoutAlignment.Fill, false);
+ public static readonly LayoutOptions StartAndExpand = new LayoutOptions(LayoutAlignment.Start, true);
+ public static readonly LayoutOptions CenterAndExpand = new LayoutOptions(LayoutAlignment.Center, true);
+ public static readonly LayoutOptions EndAndExpand = new LayoutOptions(LayoutAlignment.End, true);
+ public static readonly LayoutOptions FillAndExpand = new LayoutOptions(LayoutAlignment.Fill, true);
+
+ public LayoutOptions(LayoutAlignment alignment, bool expands)
+ {
+ var a = (int)alignment;
+ if (a < 0 || a > 3)
+ throw new ArgumentOutOfRangeException();
+ _flags = (int)alignment | (expands ? (int)LayoutExpandFlag.Expand : 0);
+ }
+
+ public LayoutAlignment Alignment
+ {
+ get { return (LayoutAlignment)(_flags & 3); }
+ set { _flags = (_flags & ~3) | (int)value; }
+ }
+
+ public bool Expands
+ {
+ get { return (_flags & (int)LayoutExpandFlag.Expand) != 0; }
+ set { _flags = (_flags & 3) | (value ? (int)LayoutExpandFlag.Expand : 0); }
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/LayoutOptionsConverter.cs b/Xamarin.Forms.Core/LayoutOptionsConverter.cs
new file mode 100644
index 00000000..746e56ce
--- /dev/null
+++ b/Xamarin.Forms.Core/LayoutOptionsConverter.cs
@@ -0,0 +1,25 @@
+using System;
+using System.Linq;
+using System.Reflection;
+
+namespace Xamarin.Forms
+{
+ public sealed class LayoutOptionsConverter : TypeConverter
+ {
+ public override object ConvertFromInvariantString(string value)
+ {
+ if (value != null)
+ {
+ string[] parts = value.Split('.');
+ if (parts.Length > 2 || (parts.Length == 2 && parts[0] != "LayoutOptions"))
+ throw new InvalidOperationException(string.Format("Cannot convert \"{0}\" into {1}", value, typeof(LayoutOptions)));
+ value = parts[parts.Length - 1];
+ FieldInfo field = typeof(LayoutOptions).GetFields().FirstOrDefault(fi => fi.IsStatic && fi.Name == value);
+ if (field != null)
+ return (LayoutOptions)field.GetValue(null);
+ }
+
+ throw new InvalidOperationException(string.Format("Cannot convert \"{0}\" into {1}", value, typeof(LayoutOptions)));
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/LineBreakMode.cs b/Xamarin.Forms.Core/LineBreakMode.cs
new file mode 100644
index 00000000..c7a36bb8
--- /dev/null
+++ b/Xamarin.Forms.Core/LineBreakMode.cs
@@ -0,0 +1,12 @@
+namespace Xamarin.Forms
+{
+ public enum LineBreakMode
+ {
+ NoWrap,
+ WordWrap,
+ CharacterWrap,
+ HeadTruncation,
+ TailTruncation,
+ MiddleTruncation
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/ListProxy.cs b/Xamarin.Forms.Core/ListProxy.cs
new file mode 100644
index 00000000..229dcb6b
--- /dev/null
+++ b/Xamarin.Forms.Core/ListProxy.cs
@@ -0,0 +1,488 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Collections.Specialized;
+using System.Linq;
+
+namespace Xamarin.Forms
+{
+ internal sealed class ListProxy : IReadOnlyList<object>, IList, INotifyCollectionChanged
+ {
+ readonly ICollection _collection;
+ readonly IList _list;
+ readonly int _windowSize;
+
+ IEnumerator _enumerator;
+ int _enumeratorIndex;
+
+ bool _finished;
+ HashSet<int> _indexesCounted;
+
+ Dictionary<int, object> _items;
+ int _version;
+
+ int _windowIndex;
+
+ internal ListProxy(IEnumerable enumerable, int windowSize = int.MaxValue)
+ {
+ _windowSize = windowSize;
+
+ ProxiedEnumerable = enumerable;
+ _collection = enumerable as ICollection;
+
+ if (_collection == null && enumerable is IReadOnlyCollection<object>)
+ _collection = new ReadOnlyListAdapter((IReadOnlyCollection<object>)enumerable);
+
+ _list = enumerable as IList;
+ if (_list == null && enumerable is IReadOnlyList<object>)
+ _list = new ReadOnlyListAdapter((IReadOnlyList<object>)enumerable);
+
+ var changed = enumerable as INotifyCollectionChanged;
+ if (changed != null)
+ new WeakNotifyProxy(this, changed);
+ }
+
+ public IEnumerable ProxiedEnumerable { get; }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return GetEnumerator();
+ }
+
+ public IEnumerator<object> GetEnumerator()
+ {
+ return new ProxyEnumerator(this);
+ }
+
+ /// <summary>
+ /// Gets whether or not the current window contains the <paramref name="item" />.
+ /// </summary>
+ /// <param name="item">The item to search for.</param>
+ /// <returns><c>true</c> if the item was found in a list or the current window, <c>false</c> otherwise.</returns>
+ public bool Contains(object item)
+ {
+ if (_list != null)
+ return _list.Contains(item);
+
+ EnsureWindowCreated();
+
+ if (_items != null)
+ return _items.Values.Contains(item);
+
+ return false;
+ }
+
+ /// <summary>
+ /// Gets the index for the <paramref name="item" /> if in a list or the current window.
+ /// </summary>
+ /// <param name="item">The item to search for.</param>
+ /// <returns>The index of the item if in a list or the current window, -1 otherwise.</returns>
+ public int IndexOf(object item)
+ {
+ if (_list != null)
+ return _list.IndexOf(item);
+
+ EnsureWindowCreated();
+
+ if (_items != null)
+ {
+ foreach (KeyValuePair<int, object> kvp in _items)
+ {
+ if (Equals(kvp.Value, item))
+ return kvp.Key;
+ }
+ }
+
+ return -1;
+ }
+
+ public event NotifyCollectionChangedEventHandler CollectionChanged;
+
+ public int Count
+ {
+ get
+ {
+ if (_collection != null)
+ return _collection.Count;
+
+ EnsureWindowCreated();
+
+ if (_indexesCounted != null)
+ return _indexesCounted.Count;
+
+ return 0;
+ }
+ }
+
+ public object this[int index]
+ {
+ get
+ {
+ object value;
+ if (!TryGetValue(index, out value))
+ throw new ArgumentOutOfRangeException("index");
+
+ return value;
+ }
+ }
+
+ public void Clear()
+ {
+ _version++;
+ _finished = false;
+ _windowIndex = 0;
+ _enumeratorIndex = 0;
+
+ if (_enumerator != null)
+ {
+ var dispose = _enumerator as IDisposable;
+ if (dispose != null)
+ dispose.Dispose();
+
+ _enumerator = null;
+ }
+
+ if (_items != null)
+ _items.Clear();
+ if (_indexesCounted != null)
+ _indexesCounted.Clear();
+
+ OnCountChanged();
+ OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
+ }
+
+ public event EventHandler CountChanged;
+
+ void ClearRange(int index, int clearCount)
+ {
+ if (_items == null)
+ return;
+
+ for (int i = index; i < index + clearCount; i++)
+ _items.Remove(i);
+ }
+
+ bool CountIndex(int index)
+ {
+ if (_collection != null)
+ return false;
+
+ // A collection is used in case TryGetValue is called out of order.
+ if (_indexesCounted == null)
+ _indexesCounted = new HashSet<int>();
+
+ if (_indexesCounted.Contains(index))
+ return false;
+
+ _indexesCounted.Add(index);
+ return true;
+ }
+
+ void EnsureWindowCreated()
+ {
+ if (_items != null && _items.Count > 0)
+ return;
+
+ object value;
+ TryGetValue(0, out value);
+ }
+
+ void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
+ {
+ Action action;
+ if (_list == null)
+ {
+ action = Clear;
+ }
+ else
+ {
+ action = () =>
+ {
+ _version++;
+ OnCollectionChanged(e);
+ };
+ }
+
+ CollectionSynchronizationContext sync;
+ if (BindingBase.TryGetSynchronizedCollection(ProxiedEnumerable, out sync))
+ {
+ sync.Callback(ProxiedEnumerable, sync.Context, () =>
+ {
+ e = e.WithCount(Count);
+ Device.BeginInvokeOnMainThread(action);
+ }, false);
+ }
+ else
+ {
+ e = e.WithCount(Count);
+ if (Device.IsInvokeRequired)
+ Device.BeginInvokeOnMainThread(action);
+ else
+ action();
+ }
+ }
+
+ void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
+ {
+ NotifyCollectionChangedEventHandler changed = CollectionChanged;
+ if (changed != null)
+ changed(this, e);
+ }
+
+ void OnCountChanged()
+ {
+ EventHandler changed = CountChanged;
+ if (changed != null)
+ changed(this, EventArgs.Empty);
+ }
+
+ bool TryGetValue(int index, out object value)
+ {
+ value = null;
+
+ CollectionSynchronizationContext syncContext;
+ BindingBase.TryGetSynchronizedCollection(ProxiedEnumerable, out syncContext);
+
+ if (_list != null)
+ {
+ object indexedValue = null;
+ var inRange = false;
+ Action getFromList = () =>
+ {
+ if (index >= _list.Count)
+ return;
+
+ indexedValue = _list[index];
+ inRange = true;
+ };
+
+ if (syncContext != null)
+ syncContext.Callback(ProxiedEnumerable, syncContext.Context, getFromList, false);
+ else
+ getFromList();
+
+ value = indexedValue;
+ return inRange;
+ }
+
+ if (_collection != null && index >= _collection.Count)
+ return false;
+ if (_items != null)
+ {
+ bool found = _items.TryGetValue(index, out value);
+ if (found || _finished)
+ return found;
+ }
+
+ if (index >= _windowIndex + _windowSize)
+ {
+ int newIndex = index - _windowSize / 2;
+ ClearRange(_windowIndex, newIndex - _windowIndex);
+ _windowIndex = newIndex;
+ }
+ else if (index < _windowIndex)
+ {
+ int clearIndex = _windowIndex;
+ int clearSize = _windowSize;
+ if (clearIndex <= index + clearSize)
+ {
+ int diff = index + clearSize - clearIndex;
+ clearIndex += diff + 1;
+ clearSize -= diff;
+ }
+
+ ClearRange(clearIndex, clearSize);
+ _windowIndex = 0;
+
+ var dispose = _enumerator as IDisposable;
+ if (dispose != null)
+ dispose.Dispose();
+
+ _enumerator = null;
+ _enumeratorIndex = 0;
+ }
+
+ if (_enumerator == null)
+ _enumerator = ProxiedEnumerable.GetEnumerator();
+ if (_items == null)
+ _items = new Dictionary<int, object>();
+
+ var countChanged = false;
+ int end = _windowIndex + _windowSize;
+
+ for (; _enumeratorIndex < end; _enumeratorIndex++)
+ {
+ var moved = false;
+ Action move = () =>
+ {
+ try
+ {
+ moved = _enumerator.MoveNext();
+ }
+ catch (InvalidOperationException ioex)
+ {
+ throw new InvalidOperationException("You must call UpdateNonNotifyingList() after updating a list that does not implement INotifyCollectionChanged", ioex);
+ }
+
+ if (!moved)
+ {
+ var dispose = _enumerator as IDisposable;
+ if (dispose != null)
+ dispose.Dispose();
+
+ _enumerator = null;
+ _enumeratorIndex = 0;
+ _finished = true;
+ }
+ };
+
+ if (syncContext == null)
+ move();
+ else
+ syncContext.Callback(ProxiedEnumerable, syncContext.Context, move, false);
+
+ if (!moved)
+ break;
+
+ if (CountIndex(_enumeratorIndex))
+ countChanged = true;
+
+ if (_enumeratorIndex >= _windowIndex)
+ _items.Add(_enumeratorIndex, _enumerator.Current);
+ }
+
+ if (countChanged)
+ OnCountChanged();
+
+ return _items.TryGetValue(index, out value);
+ }
+
+ class WeakNotifyProxy
+ {
+ readonly WeakReference<INotifyCollectionChanged> _weakCollection;
+ readonly WeakReference<ListProxy> _weakProxy;
+
+ public WeakNotifyProxy(ListProxy proxy, INotifyCollectionChanged incc)
+ {
+ incc.CollectionChanged += OnCollectionChanged;
+
+ _weakProxy = new WeakReference<ListProxy>(proxy);
+ _weakCollection = new WeakReference<INotifyCollectionChanged>(incc);
+ }
+
+ void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
+ {
+ ListProxy proxy;
+ if (!_weakProxy.TryGetTarget(out proxy))
+ {
+ INotifyCollectionChanged collection;
+ if (_weakCollection.TryGetTarget(out collection))
+ collection.CollectionChanged -= OnCollectionChanged;
+
+ return;
+ }
+
+ proxy.OnCollectionChanged(sender, e);
+ }
+ }
+
+ class ProxyEnumerator : IEnumerator<object>
+ {
+ readonly ListProxy _proxy;
+ readonly int _version;
+
+ int _index;
+
+ public ProxyEnumerator(ListProxy proxy)
+ {
+ _proxy = proxy;
+ _version = proxy._version;
+ }
+
+ public void Dispose()
+ {
+ }
+
+ public bool MoveNext()
+ {
+ if (_proxy._version != _version)
+ throw new InvalidOperationException();
+
+ object value;
+ bool next = _proxy.TryGetValue(_index++, out value);
+ if (next)
+ Current = value;
+
+ return next;
+ }
+
+ public void Reset()
+ {
+ _index = 0;
+ Current = null;
+ }
+
+ public object Current { get; private set; }
+ }
+
+ #region IList
+
+ object IList.this[int index]
+ {
+ get { return this[index]; }
+ set { throw new NotSupportedException(); }
+ }
+
+ bool IList.IsReadOnly
+ {
+ get { return true; }
+ }
+
+ bool IList.IsFixedSize
+ {
+ get { return false; }
+ }
+
+ bool ICollection.IsSynchronized
+ {
+ get { return false; }
+ }
+
+ object ICollection.SyncRoot
+ {
+ get { throw new NotSupportedException(); }
+ }
+
+ void ICollection.CopyTo(Array array, int index)
+ {
+ throw new NotSupportedException();
+ }
+
+ int IList.Add(object item)
+ {
+ throw new NotSupportedException();
+ }
+
+ void IList.Remove(object item)
+ {
+ throw new NotSupportedException();
+ }
+
+ void IList.Insert(int index, object item)
+ {
+ throw new NotSupportedException();
+ }
+
+ void IList.RemoveAt(int index)
+ {
+ throw new NotSupportedException();
+ }
+
+ void IList.Clear()
+ {
+ throw new NotSupportedException();
+ }
+
+ #endregion
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/ListView.cs b/Xamarin.Forms.Core/ListView.cs
new file mode 100644
index 00000000..3d29033a
--- /dev/null
+++ b/Xamarin.Forms.Core/ListView.cs
@@ -0,0 +1,540 @@
+using System;
+using System.Collections;
+using System.Diagnostics;
+using System.Windows.Input;
+using Xamarin.Forms.Platform;
+
+namespace Xamarin.Forms
+{
+ [RenderWith(typeof(_ListViewRenderer))]
+ public class ListView : ItemsView<Cell>, IListViewController
+
+ {
+ public static readonly BindableProperty IsPullToRefreshEnabledProperty = BindableProperty.Create("IsPullToRefreshEnabled", typeof(bool), typeof(ListView), false);
+
+ public static readonly BindableProperty IsRefreshingProperty = BindableProperty.Create("IsRefreshing", typeof(bool), typeof(ListView), false, BindingMode.TwoWay);
+
+ public static readonly BindableProperty RefreshCommandProperty = BindableProperty.Create("RefreshCommand", typeof(ICommand), typeof(ListView), null, propertyChanged: OnRefreshCommandChanged);
+
+ public static readonly BindableProperty HeaderProperty = BindableProperty.Create("Header", typeof(object), typeof(ListView), null, propertyChanged: OnHeaderChanged);
+
+ public static readonly BindableProperty HeaderTemplateProperty = BindableProperty.Create("HeaderTemplate", typeof(DataTemplate), typeof(ListView), null, propertyChanged: OnHeaderTemplateChanged,
+ validateValue: ValidateHeaderFooterTemplate);
+
+ public static readonly BindableProperty FooterProperty = BindableProperty.Create("Footer", typeof(object), typeof(ListView), null, propertyChanged: OnFooterChanged);
+
+ public static readonly BindableProperty FooterTemplateProperty = BindableProperty.Create("FooterTemplate", typeof(DataTemplate), typeof(ListView), null, propertyChanged: OnFooterTemplateChanged,
+ validateValue: ValidateHeaderFooterTemplate);
+
+ public static readonly BindableProperty SelectedItemProperty = BindableProperty.Create("SelectedItem", typeof(object), typeof(ListView), null, BindingMode.OneWayToSource,
+ propertyChanged: OnSelectedItemChanged);
+
+ public static readonly BindableProperty HasUnevenRowsProperty = BindableProperty.Create("HasUnevenRows", typeof(bool), typeof(ListView), false);
+
+ public static readonly BindableProperty RowHeightProperty = BindableProperty.Create("RowHeight", typeof(int), typeof(ListView), -1);
+
+ public static readonly BindableProperty GroupHeaderTemplateProperty = BindableProperty.Create("GroupHeaderTemplate", typeof(DataTemplate), typeof(ListView), null,
+ propertyChanged: OnGroupHeaderTemplateChanged);
+
+ public static readonly BindableProperty IsGroupingEnabledProperty = BindableProperty.Create("IsGroupingEnabled", typeof(bool), typeof(ListView), false);
+
+ public static readonly BindableProperty SeparatorVisibilityProperty = BindableProperty.Create("SeparatorVisibility", typeof(SeparatorVisibility), typeof(ListView), SeparatorVisibility.Default);
+
+ public static readonly BindableProperty SeparatorColorProperty = BindableProperty.Create("SeparatorColor", typeof(Color), typeof(ListView), Color.Default);
+
+ BindingBase _groupDisplayBinding;
+
+ BindingBase _groupShortNameBinding;
+ Element _headerElement;
+ Element _footerElement;
+
+ ScrollToRequestedEventArgs _pendingScroll;
+ int _previousGroupSelected = -1;
+ int _previousRowSelected = -1;
+
+ /// <summary>
+ /// Controls whether anything happens in BeginRefresh(), is set based on RefreshCommand.CanExecute
+ /// </summary>
+ bool _refreshAllowed = true;
+
+ public ListView()
+ {
+ TakePerformanceHit = false;
+
+ VerticalOptions = HorizontalOptions = LayoutOptions.FillAndExpand;
+
+ TemplatedItems.IsGroupingEnabledProperty = IsGroupingEnabledProperty;
+ TemplatedItems.GroupHeaderTemplateProperty = GroupHeaderTemplateProperty;
+ }
+
+ public ListView([Parameter("CachingStrategy")] ListViewCachingStrategy cachingStrategy) : this()
+ {
+ if (Device.OS == TargetPlatform.Android || Device.OS == TargetPlatform.iOS)
+ CachingStrategy = cachingStrategy;
+ }
+
+ public object Footer
+ {
+ get { return GetValue(FooterProperty); }
+ set { SetValue(FooterProperty, value); }
+ }
+
+ public DataTemplate FooterTemplate
+ {
+ get { return (DataTemplate)GetValue(FooterTemplateProperty); }
+ set { SetValue(FooterTemplateProperty, value); }
+ }
+
+ public BindingBase GroupDisplayBinding
+ {
+ get { return _groupDisplayBinding; }
+ set
+ {
+ if (_groupDisplayBinding == value)
+ return;
+
+ OnPropertyChanging();
+ BindingBase oldValue = value;
+ _groupDisplayBinding = value;
+ OnGroupDisplayBindingChanged(this, oldValue, _groupDisplayBinding);
+ TemplatedItems.GroupDisplayBinding = value;
+ OnPropertyChanged();
+ }
+ }
+
+ public DataTemplate GroupHeaderTemplate
+ {
+ get { return (DataTemplate)GetValue(GroupHeaderTemplateProperty); }
+ set { SetValue(GroupHeaderTemplateProperty, value); }
+ }
+
+ public BindingBase GroupShortNameBinding
+ {
+ get { return _groupShortNameBinding; }
+ set
+ {
+ if (_groupShortNameBinding == value)
+ return;
+
+ OnPropertyChanging();
+ _groupShortNameBinding = value;
+ TemplatedItems.GroupShortNameBinding = value;
+ OnPropertyChanged();
+ }
+ }
+
+ public bool HasUnevenRows
+ {
+ get { return (bool)GetValue(HasUnevenRowsProperty); }
+ set { SetValue(HasUnevenRowsProperty, value); }
+ }
+
+ public object Header
+ {
+ get { return GetValue(HeaderProperty); }
+ set { SetValue(HeaderProperty, value); }
+ }
+
+ public DataTemplate HeaderTemplate
+ {
+ get { return (DataTemplate)GetValue(HeaderTemplateProperty); }
+ set { SetValue(HeaderTemplateProperty, value); }
+ }
+
+ public bool IsGroupingEnabled
+ {
+ get { return (bool)GetValue(IsGroupingEnabledProperty); }
+ set { SetValue(IsGroupingEnabledProperty, value); }
+ }
+
+ public bool IsPullToRefreshEnabled
+ {
+ get { return (bool)GetValue(IsPullToRefreshEnabledProperty); }
+ set { SetValue(IsPullToRefreshEnabledProperty, value); }
+ }
+
+ public bool IsRefreshing
+ {
+ get { return (bool)GetValue(IsRefreshingProperty); }
+ set { SetValue(IsRefreshingProperty, value); }
+ }
+
+ public ICommand RefreshCommand
+ {
+ get { return (ICommand)GetValue(RefreshCommandProperty); }
+ set { SetValue(RefreshCommandProperty, value); }
+ }
+
+ public int RowHeight
+ {
+ get { return (int)GetValue(RowHeightProperty); }
+ set { SetValue(RowHeightProperty, value); }
+ }
+
+ public object SelectedItem
+ {
+ get { return GetValue(SelectedItemProperty); }
+ set { SetValue(SelectedItemProperty, value); }
+ }
+
+ public Color SeparatorColor
+ {
+ get { return (Color)GetValue(SeparatorColorProperty); }
+ set { SetValue(SeparatorColorProperty, value); }
+ }
+
+ public SeparatorVisibility SeparatorVisibility
+ {
+ get { return (SeparatorVisibility)GetValue(SeparatorVisibilityProperty); }
+ set { SetValue(SeparatorVisibilityProperty, value); }
+ }
+
+ internal ListViewCachingStrategy CachingStrategy { get; private set; }
+
+ internal bool TakePerformanceHit { get; set; }
+
+ bool RefreshAllowed
+ {
+ set
+ {
+ if (_refreshAllowed == value)
+ return;
+
+ _refreshAllowed = value;
+ OnPropertyChanged();
+ }
+ get { return _refreshAllowed; }
+ }
+
+ Element IListViewController.FooterElement
+ {
+ get { return _footerElement; }
+ }
+
+ Element IListViewController.HeaderElement
+ {
+ get { return _headerElement; }
+ }
+
+ bool IListViewController.RefreshAllowed
+ {
+ get { return RefreshAllowed; }
+ }
+
+ void IListViewController.SendCellAppearing(Cell cell)
+ {
+ EventHandler<ItemVisibilityEventArgs> handler = ItemAppearing;
+ if (handler != null)
+ handler(this, new ItemVisibilityEventArgs(cell.BindingContext));
+ }
+
+ void IListViewController.SendCellDisappearing(Cell cell)
+ {
+ EventHandler<ItemVisibilityEventArgs> handler = ItemDisappearing;
+ if (handler != null)
+ handler(this, new ItemVisibilityEventArgs(cell.BindingContext));
+ }
+
+ void IListViewController.SendRefreshing()
+ {
+ BeginRefresh();
+ }
+
+ public void BeginRefresh()
+ {
+ if (!RefreshAllowed)
+ return;
+
+ SetValueCore(IsRefreshingProperty, true);
+ OnRefreshing(EventArgs.Empty);
+
+ ICommand command = RefreshCommand;
+ if (command != null)
+ command.Execute(null);
+ }
+
+ public void EndRefresh()
+ {
+ SetValueCore(IsRefreshingProperty, false);
+ }
+
+ public event EventHandler<ItemVisibilityEventArgs> ItemAppearing;
+
+ public event EventHandler<ItemVisibilityEventArgs> ItemDisappearing;
+
+ public event EventHandler<SelectedItemChangedEventArgs> ItemSelected;
+
+ public event EventHandler<ItemTappedEventArgs> ItemTapped;
+
+ public event EventHandler Refreshing;
+
+ public void ScrollTo(object item, ScrollToPosition position, bool animated)
+ {
+ if (!Enum.IsDefined(typeof(ScrollToPosition), position))
+ throw new ArgumentException("position is not a valid ScrollToPosition", "position");
+
+ var args = new ScrollToRequestedEventArgs(item, position, animated);
+ if (IsPlatformEnabled)
+ OnScrollToRequested(args);
+ else
+ _pendingScroll = args;
+ }
+
+ public void ScrollTo(object item, object group, ScrollToPosition position, bool animated)
+ {
+ if (!IsGroupingEnabled)
+ throw new InvalidOperationException("Grouping is not enabled");
+ if (!Enum.IsDefined(typeof(ScrollToPosition), position))
+ throw new ArgumentException("position is not a valid ScrollToPosition", "position");
+
+ var args = new ScrollToRequestedEventArgs(item, group, position, animated);
+ if (IsPlatformEnabled)
+ OnScrollToRequested(args);
+ else
+ _pendingScroll = args;
+ }
+
+ protected override Cell CreateDefault(object item)
+ {
+ string text = null;
+ if (item != null)
+ text = item.ToString();
+
+ return new TextCell { Text = text };
+ }
+
+ [Obsolete("Use OnMeasure")]
+ protected override SizeRequest OnSizeRequest(double widthConstraint, double heightConstraint)
+ {
+ var minimumSize = new Size(40, 40);
+ Size request;
+
+ double width = Math.Min(Device.Info.ScaledScreenSize.Width, Device.Info.ScaledScreenSize.Height);
+
+ var list = ItemsSource as IList;
+ if (list != null && HasUnevenRows == false && RowHeight > 0 && !IsGroupingEnabled)
+ {
+ // we can calculate this
+ request = new Size(width, list.Count * RowHeight);
+ }
+ else
+ {
+ // probably not worth it
+ request = new Size(width, Math.Max(Device.Info.ScaledScreenSize.Width, Device.Info.ScaledScreenSize.Height));
+ }
+
+ return new SizeRequest(request, minimumSize);
+ }
+
+ protected override void SetupContent(Cell content, int index)
+ {
+ base.SetupContent(content, index);
+ content.Parent = this;
+ }
+
+ protected override void UnhookContent(Cell content)
+ {
+ base.UnhookContent(content);
+ content.Parent = null;
+ }
+
+ internal Cell CreateDefaultCell(object item)
+ {
+ return CreateDefault(item);
+ }
+
+ internal void NotifyRowTapped(int groupIndex, int inGroupIndex, Cell cell = null)
+ {
+ TemplatedItemsList<ItemsView<Cell>, Cell> group = TemplatedItems.GetGroup(groupIndex);
+
+ bool changed = _previousGroupSelected != groupIndex || _previousRowSelected != inGroupIndex;
+
+ _previousRowSelected = inGroupIndex;
+ _previousGroupSelected = groupIndex;
+ if (cell == null)
+ {
+ cell = group[inGroupIndex];
+ }
+
+ // Set SelectedItem before any events so we don't override any changes they may have made.
+ SetValueCore(SelectedItemProperty, cell.BindingContext, SetValueFlags.ClearOneWayBindings | SetValueFlags.ClearDynamicResource | (changed ? SetValueFlags.RaiseOnEqual : 0));
+
+ cell.OnTapped();
+
+ ItemTapped?.Invoke(this, new ItemTappedEventArgs(group, cell.BindingContext));
+ }
+
+ internal void NotifyRowTapped(int index, Cell cell = null)
+ {
+ if (IsGroupingEnabled)
+ {
+ int leftOver;
+ int groupIndex = TemplatedItems.GetGroupIndexFromGlobal(index, out leftOver);
+
+ NotifyRowTapped(groupIndex, leftOver - 1, cell);
+ }
+ else
+ NotifyRowTapped(0, index, cell);
+ }
+
+ internal override void OnIsPlatformEnabledChanged()
+ {
+ base.OnIsPlatformEnabledChanged();
+
+ if (IsPlatformEnabled && _pendingScroll != null)
+ {
+ OnScrollToRequested(_pendingScroll);
+ _pendingScroll = null;
+ }
+ }
+
+ internal event EventHandler<ScrollToRequestedEventArgs> ScrollToRequested;
+
+ void OnCommandCanExecuteChanged(object sender, EventArgs eventArgs)
+ {
+ RefreshAllowed = RefreshCommand.CanExecute(null);
+ }
+
+ static void OnFooterChanged(BindableObject bindable, object oldValue, object newValue)
+ {
+ var lv = (ListView)bindable;
+ lv.OnHeaderOrFooterChanged(ref lv._footerElement, "FooterElement", newValue, lv.FooterTemplate, false);
+ }
+
+ static void OnFooterTemplateChanged(BindableObject bindable, object oldValue, object newValue)
+ {
+ var lv = (ListView)bindable;
+ lv.OnHeaderOrFooterChanged(ref lv._footerElement, "FooterElement", lv.Footer, (DataTemplate)newValue, true);
+ }
+
+ static void OnGroupDisplayBindingChanged(BindableObject bindable, BindingBase oldValue, BindingBase newValue)
+ {
+ var lv = (ListView)bindable;
+ if (newValue != null && lv.GroupHeaderTemplate != null)
+ {
+ lv.GroupHeaderTemplate = null;
+ Log.Warning("ListView", "GroupHeaderTemplate and GroupDisplayBinding can not be set at the same time, setting GroupHeaderTemplate to null");
+ }
+ }
+
+ static void OnGroupHeaderTemplateChanged(BindableObject bindable, object oldvalue, object newValue)
+ {
+ var lv = (ListView)bindable;
+ if (newValue != null && lv.GroupDisplayBinding != null)
+ {
+ lv.GroupDisplayBinding = null;
+ Debug.WriteLine("GroupHeaderTemplate and GroupDisplayBinding can not be set at the same time, setting GroupDisplayBinding to null");
+ }
+ }
+
+ static void OnHeaderChanged(BindableObject bindable, object oldValue, object newValue)
+ {
+ var lv = (ListView)bindable;
+ lv.OnHeaderOrFooterChanged(ref lv._headerElement, "HeaderElement", newValue, lv.HeaderTemplate, false);
+ }
+
+ void OnHeaderOrFooterChanged(ref Element storage, string property, object dataObject, DataTemplate template, bool templateChanged)
+ {
+ if (dataObject == null)
+ {
+ if (!templateChanged)
+ {
+ OnPropertyChanging(property);
+ storage = null;
+ OnPropertyChanged(property);
+ }
+
+ return;
+ }
+
+ if (template == null)
+ {
+ var view = dataObject as Element;
+ if (view == null || view is Page)
+ view = new Label { Text = dataObject.ToString() };
+
+ view.Parent = this;
+ OnPropertyChanging(property);
+ storage = view;
+ OnPropertyChanged(property);
+ }
+ else if (storage == null || templateChanged)
+ {
+ OnPropertyChanging(property);
+ storage = template.CreateContent() as Element;
+ if (storage != null)
+ {
+ storage.BindingContext = dataObject;
+ storage.Parent = this;
+ }
+ OnPropertyChanged(property);
+ }
+ else
+ {
+ storage.BindingContext = dataObject;
+ }
+ }
+
+ static void OnHeaderTemplateChanged(BindableObject bindable, object oldValue, object newValue)
+ {
+ var lv = (ListView)bindable;
+ lv.OnHeaderOrFooterChanged(ref lv._headerElement, "HeaderElement", lv.Header, (DataTemplate)newValue, true);
+ }
+
+ static void OnRefreshCommandChanged(BindableObject bindable, object oldValue, object newValue)
+ {
+ var lv = (ListView)bindable;
+ var oldCommand = (ICommand)oldValue;
+ var command = (ICommand)newValue;
+
+ lv.OnRefreshCommandChanged(oldCommand, command);
+ }
+
+ void OnRefreshCommandChanged(ICommand oldCommand, ICommand newCommand)
+ {
+ if (oldCommand != null)
+ {
+ oldCommand.CanExecuteChanged -= OnCommandCanExecuteChanged;
+ }
+
+ if (newCommand != null)
+ {
+ newCommand.CanExecuteChanged += OnCommandCanExecuteChanged;
+ RefreshAllowed = newCommand.CanExecute(null);
+ }
+ else
+ {
+ RefreshAllowed = true;
+ }
+ }
+
+ void OnRefreshing(EventArgs e)
+ {
+ EventHandler handler = Refreshing;
+ if (handler != null)
+ handler(this, e);
+ }
+
+ void OnScrollToRequested(ScrollToRequestedEventArgs e)
+ {
+ EventHandler<ScrollToRequestedEventArgs> handler = ScrollToRequested;
+ if (handler != null)
+ handler(this, e);
+ }
+
+ static void OnSelectedItemChanged(BindableObject bindable, object oldValue, object newValue)
+ {
+ var list = (ListView)bindable;
+ if (list.ItemSelected != null)
+ list.ItemSelected(list, new SelectedItemChangedEventArgs(newValue));
+ }
+
+ static bool ValidateHeaderFooterTemplate(BindableObject bindable, object value)
+ {
+ if (value == null)
+ return true;
+ var template = (DataTemplate)value;
+ return template.CreateContent() is View;
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/ListViewCachingStrategy.cs b/Xamarin.Forms.Core/ListViewCachingStrategy.cs
new file mode 100644
index 00000000..7dd90196
--- /dev/null
+++ b/Xamarin.Forms.Core/ListViewCachingStrategy.cs
@@ -0,0 +1,8 @@
+namespace Xamarin.Forms
+{
+ public enum ListViewCachingStrategy
+ {
+ RetainElement = 0,
+ RecycleElement
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/LockingSemaphore.cs b/Xamarin.Forms.Core/LockingSemaphore.cs
new file mode 100644
index 00000000..b9fd20a9
--- /dev/null
+++ b/Xamarin.Forms.Core/LockingSemaphore.cs
@@ -0,0 +1,51 @@
+using System;
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Xamarin.Forms
+{
+ internal class LockingSemaphore
+ {
+ static readonly Task Completed = Task.FromResult(true);
+ readonly Queue<TaskCompletionSource<bool>> _waiters = new Queue<TaskCompletionSource<bool>>();
+ int _currentCount;
+
+ public LockingSemaphore(int initialCount)
+ {
+ if (initialCount < 0)
+ throw new ArgumentOutOfRangeException("initialCount");
+ _currentCount = initialCount;
+ }
+
+ public void Release()
+ {
+ TaskCompletionSource<bool> toRelease = null;
+ lock(_waiters)
+ {
+ if (_waiters.Count > 0)
+ toRelease = _waiters.Dequeue();
+ else
+ ++_currentCount;
+ }
+ if (toRelease != null)
+ toRelease.TrySetResult(true);
+ }
+
+ public Task WaitAsync(CancellationToken token)
+ {
+ lock(_waiters)
+ {
+ if (_currentCount > 0)
+ {
+ --_currentCount;
+ return Completed;
+ }
+ var waiter = new TaskCompletionSource<bool>();
+ _waiters.Enqueue(waiter);
+ token.Register(() => waiter.TrySetCanceled());
+ return waiter.Task;
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/Log.cs b/Xamarin.Forms.Core/Log.cs
new file mode 100644
index 00000000..b8053e5d
--- /dev/null
+++ b/Xamarin.Forms.Core/Log.cs
@@ -0,0 +1,25 @@
+using System.Collections.Generic;
+
+namespace Xamarin.Forms
+{
+ internal static class Log
+ {
+ static Log()
+ {
+ Listeners = new SynchronizedList<LogListener>();
+ }
+
+ public static IList<LogListener> Listeners { get; }
+
+ public static void Warning(string category, string message)
+ {
+ foreach (LogListener listener in Listeners)
+ listener.Warning(category, message);
+ }
+
+ public static void Warning(string category, string format, params object[] args)
+ {
+ Warning(category, string.Format(format, args));
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/LogListener.cs b/Xamarin.Forms.Core/LogListener.cs
new file mode 100644
index 00000000..78222565
--- /dev/null
+++ b/Xamarin.Forms.Core/LogListener.cs
@@ -0,0 +1,7 @@
+namespace Xamarin.Forms
+{
+ internal abstract class LogListener
+ {
+ public abstract void Warning(string category, string message);
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/MasterBehavior.cs b/Xamarin.Forms.Core/MasterBehavior.cs
new file mode 100644
index 00000000..cd2dae2f
--- /dev/null
+++ b/Xamarin.Forms.Core/MasterBehavior.cs
@@ -0,0 +1,11 @@
+namespace Xamarin.Forms
+{
+ public enum MasterBehavior
+ {
+ Default = 0,
+ SplitOnLandscape = 1,
+ Split = 2,
+ Popover = 3,
+ SplitOnPortrait = 4
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/MasterDetailPage.cs b/Xamarin.Forms.Core/MasterDetailPage.cs
new file mode 100644
index 00000000..a0849aa4
--- /dev/null
+++ b/Xamarin.Forms.Core/MasterDetailPage.cs
@@ -0,0 +1,229 @@
+using System;
+using Xamarin.Forms.Platform;
+
+namespace Xamarin.Forms
+{
+ [RenderWith(typeof(_MasterDetailPageRenderer))]
+ public class MasterDetailPage : Page
+ {
+ public static readonly BindableProperty IsGestureEnabledProperty = BindableProperty.Create("IsGestureEnabled", typeof(bool), typeof(MasterDetailPage), true);
+
+ public static readonly BindableProperty IsPresentedProperty = BindableProperty.Create("IsPresented", typeof(bool), typeof(MasterDetailPage), default(bool),
+ propertyChanged: OnIsPresentedPropertyChanged, propertyChanging: OnIsPresentedPropertyChanging);
+
+ public static readonly BindableProperty MasterBehaviorProperty = BindableProperty.Create("MasterBehavior", typeof(MasterBehavior), typeof(MasterDetailPage), default(MasterBehavior),
+ propertyChanged: OnMasterBehaviorPropertyChanged);
+
+ Page _detail;
+
+ Rectangle _detailBounds;
+
+ Page _master;
+
+ Rectangle _masterBounds;
+
+ public Page Detail
+ {
+ get { return _detail; }
+ set
+ {
+ if (_detail != null && value == null)
+ throw new ArgumentNullException("value", "Detail cannot be set to null once a value is set.");
+
+ if (_detail == value)
+ return;
+
+ if (value.RealParent != null)
+ throw new InvalidOperationException("Detail must not already have a parent.");
+
+ OnPropertyChanging();
+ if (_detail != null)
+ InternalChildren.Remove(_detail);
+ _detail = value;
+ InternalChildren.Add(_detail);
+ OnPropertyChanged();
+ }
+ }
+
+ public bool IsGestureEnabled
+ {
+ get { return (bool)GetValue(IsGestureEnabledProperty); }
+ set { SetValue(IsGestureEnabledProperty, value); }
+ }
+
+ public bool IsPresented
+ {
+ get { return (bool)GetValue(IsPresentedProperty); }
+ set { SetValue(IsPresentedProperty, value); }
+ }
+
+ public Page Master
+ {
+ get { return _master; }
+ set
+ {
+ if (_master != null && value == null)
+ throw new ArgumentNullException("value", "Master cannot be set to null once a value is set");
+
+ if (string.IsNullOrEmpty(value.Title))
+ throw new InvalidOperationException("Title property must be set on Master page");
+
+ if (_master == value)
+ return;
+
+ if (value.RealParent != null)
+ throw new InvalidOperationException("Master must not already have a parent.");
+
+ OnPropertyChanging();
+ if (_master != null)
+ InternalChildren.Remove(_master);
+ _master = value;
+ InternalChildren.Add(_master);
+ OnPropertyChanged();
+ }
+ }
+
+ public MasterBehavior MasterBehavior
+ {
+ get { return (MasterBehavior)GetValue(MasterBehaviorProperty); }
+ set { SetValue(MasterBehaviorProperty, value); }
+ }
+
+ internal bool CanChangeIsPresented { get; set; } = true;
+
+ internal Rectangle DetailBounds
+ {
+ get { return _detailBounds; }
+ set
+ {
+ _detailBounds = value;
+ if (_detail == null)
+ throw new InvalidOperationException("Detail must be set before using a MasterDetailPage");
+ _detail.Layout(value);
+ }
+ }
+
+ internal Rectangle MasterBounds
+ {
+ get { return _masterBounds; }
+ set
+ {
+ _masterBounds = value;
+ if (_master == null)
+ throw new InvalidOperationException("Master must be set before using a MasterDetailPage");
+ _master.Layout(value);
+ }
+ }
+
+ internal bool ShouldShowSplitMode
+ {
+ get
+ {
+ if (Device.Idiom == TargetIdiom.Phone)
+ return false;
+
+ MasterBehavior behavior = MasterBehavior;
+ DeviceOrientation orientation = Device.Info.CurrentOrientation;
+
+ bool isSplitOnLandscape = (behavior == MasterBehavior.SplitOnLandscape || behavior == MasterBehavior.Default) && orientation.IsLandscape();
+ bool isSplitOnPortrait = behavior == MasterBehavior.SplitOnPortrait && orientation.IsPortrait();
+ return behavior == MasterBehavior.Split || isSplitOnLandscape || isSplitOnPortrait;
+ }
+ }
+
+ public event EventHandler IsPresentedChanged;
+
+ public virtual bool ShouldShowToolbarButton()
+ {
+ if (Device.Idiom == TargetIdiom.Phone)
+ return true;
+
+ MasterBehavior behavior = MasterBehavior;
+ DeviceOrientation orientation = Device.Info.CurrentOrientation;
+
+ bool isSplitOnLandscape = (behavior == MasterBehavior.SplitOnLandscape || behavior == MasterBehavior.Default) && orientation.IsLandscape();
+ bool isSplitOnPortrait = behavior == MasterBehavior.SplitOnPortrait && orientation.IsPortrait();
+ return behavior != MasterBehavior.Split && !isSplitOnLandscape && !isSplitOnPortrait;
+ }
+
+ protected override void LayoutChildren(double x, double y, double width, double height)
+ {
+ if (Master == null || Detail == null)
+ throw new InvalidOperationException("Master and Detail must be set before using a MasterDetailPage");
+ _master.Layout(_masterBounds);
+ _detail.Layout(_detailBounds);
+ }
+
+ protected override void OnAppearing()
+ {
+ CanChangeIsPresented = true;
+ UpdateMasterBehavior(this);
+ base.OnAppearing();
+ }
+
+ protected override bool OnBackButtonPressed()
+ {
+ if (IsPresented)
+ {
+ if (Master.SendBackButtonPressed())
+ return true;
+ }
+
+ EventHandler<BackButtonPressedEventArgs> handler = BackButtonPressed;
+ if (handler != null)
+ {
+ var args = new BackButtonPressedEventArgs();
+ handler(this, args);
+ if (args.Handled)
+ return true;
+ }
+
+ if (Detail.SendBackButtonPressed())
+ {
+ return true;
+ }
+
+ return base.OnBackButtonPressed();
+ }
+
+ protected override void OnParentSet()
+ {
+ if (RealParent != null && (Master == null || Detail == null))
+ throw new InvalidOperationException("Master and Detail must be set before adding MasterDetailPage to a container");
+ base.OnParentSet();
+ }
+
+ internal event EventHandler<BackButtonPressedEventArgs> BackButtonPressed;
+
+ internal static void UpdateMasterBehavior(MasterDetailPage page)
+ {
+ if (page.ShouldShowSplitMode)
+ {
+ page.SetValueCore(IsPresentedProperty, true);
+ if (page.MasterBehavior != MasterBehavior.Default)
+ page.CanChangeIsPresented = false;
+ }
+ }
+
+ static void OnIsPresentedPropertyChanged(BindableObject sender, object oldValue, object newValue)
+ {
+ var page = (MasterDetailPage)sender;
+ EventHandler handler = page.IsPresentedChanged;
+ if (handler != null)
+ handler(page, EventArgs.Empty);
+ }
+
+ static void OnIsPresentedPropertyChanging(BindableObject sender, object oldValue, object newValue)
+ {
+ var page = (MasterDetailPage)sender;
+ if (!page.CanChangeIsPresented)
+ throw new InvalidOperationException(string.Format("Can't change IsPresented when setting {0}", page.MasterBehavior));
+ }
+
+ static void OnMasterBehaviorPropertyChanged(BindableObject sender, object oldValue, object newValue)
+ {
+ var page = (MasterDetailPage)sender;
+ UpdateMasterBehavior(page);
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/MeasureFlags.cs b/Xamarin.Forms.Core/MeasureFlags.cs
new file mode 100644
index 00000000..c2e5c80a
--- /dev/null
+++ b/Xamarin.Forms.Core/MeasureFlags.cs
@@ -0,0 +1,11 @@
+using System;
+
+namespace Xamarin.Forms
+{
+ [Flags]
+ public enum MeasureFlags
+ {
+ None = 0,
+ IncludeMargins = 1 << 0
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/MenuItem.cs b/Xamarin.Forms.Core/MenuItem.cs
new file mode 100644
index 00000000..3e830445
--- /dev/null
+++ b/Xamarin.Forms.Core/MenuItem.cs
@@ -0,0 +1,117 @@
+using System;
+using System.Windows.Input;
+
+namespace Xamarin.Forms
+{
+ public class MenuItem : BaseMenuItem
+ {
+ public static readonly BindableProperty TextProperty = BindableProperty.Create("Text", typeof(string), typeof(MenuItem), null);
+
+ public static readonly BindableProperty CommandProperty = BindableProperty.Create("Command", typeof(ICommand), typeof(MenuItem), null,
+ propertyChanging: (bo, o, n) => ((MenuItem)bo).OnCommandChanging(), propertyChanged: (bo, o, n) => ((MenuItem)bo).OnCommandChanged());
+
+ public static readonly BindableProperty CommandParameterProperty = BindableProperty.Create("CommandParameter", typeof(object), typeof(MenuItem), null,
+ propertyChanged: (bo, o, n) => ((MenuItem)bo).OnCommandParameterChanged());
+
+ public static readonly BindableProperty IsDestructiveProperty = BindableProperty.Create("IsDestructive", typeof(bool), typeof(MenuItem), false);
+
+ public static readonly BindableProperty IconProperty = BindableProperty.Create("Icon", typeof(FileImageSource), typeof(MenuItem), default(FileImageSource));
+
+ internal static readonly BindableProperty IsEnabledProperty = BindableProperty.Create("IsEnabled", typeof(bool), typeof(ToolbarItem), true);
+
+ public ICommand Command
+ {
+ get { return (ICommand)GetValue(CommandProperty); }
+ set { SetValue(CommandProperty, value); }
+ }
+
+ public object CommandParameter
+ {
+ get { return GetValue(CommandParameterProperty); }
+ set { SetValue(CommandParameterProperty, value); }
+ }
+
+ public FileImageSource Icon
+ {
+ get { return (FileImageSource)GetValue(IconProperty); }
+ set { SetValue(IconProperty, value); }
+ }
+
+ public bool IsDestructive
+ {
+ get { return (bool)GetValue(IsDestructiveProperty); }
+ set { SetValue(IsDestructiveProperty, value); }
+ }
+
+ public string Text
+ {
+ get { return (string)GetValue(TextProperty); }
+ set { SetValue(TextProperty, value); }
+ }
+
+ internal bool IsEnabled
+ {
+ get { return (bool)GetValue(IsEnabledProperty); }
+ set { SetValue(IsEnabledProperty, value); }
+ }
+
+ bool IsEnabledCore
+ {
+ set { SetValueCore(IsEnabledProperty, value); }
+ }
+
+ public event EventHandler Clicked;
+
+ protected virtual void OnClicked()
+ {
+ EventHandler handler = Clicked;
+ if (handler != null)
+ handler(this, EventArgs.Empty);
+ }
+
+ internal void Activate()
+ {
+ if (Command != null)
+ {
+ if (IsEnabled)
+ Command.Execute(CommandParameter);
+ }
+
+ OnClicked();
+ }
+
+ void OnCommandCanExecuteChanged(object sender, EventArgs eventArgs)
+ {
+ IsEnabledCore = Command.CanExecute(CommandParameter);
+ }
+
+ void OnCommandChanged()
+ {
+ if (Command == null)
+ {
+ IsEnabledCore = true;
+ return;
+ }
+
+ IsEnabledCore = Command.CanExecute(CommandParameter);
+
+ Command.CanExecuteChanged += OnCommandCanExecuteChanged;
+ }
+
+ void OnCommandChanging()
+ {
+ if (Command == null)
+ return;
+
+ Command.CanExecuteChanged -= OnCommandCanExecuteChanged;
+ }
+
+ void OnCommandParameterChanged()
+ {
+ if (Command == null)
+ return;
+
+ IsEnabledCore = Command.CanExecute(CommandParameter);
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/MergedStyle.cs b/Xamarin.Forms.Core/MergedStyle.cs
new file mode 100644
index 00000000..9f9c68bf
--- /dev/null
+++ b/Xamarin.Forms.Core/MergedStyle.cs
@@ -0,0 +1,162 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+
+namespace Xamarin.Forms
+{
+ public partial class VisualElement
+ {
+ sealed class MergedStyle : IStyle
+ {
+ ////If the base type is one of these, stop registering dynamic resources further
+ ////The last one (typeof(Element)) is a safety guard as we might be creating VisualElement directly in internal code
+ static readonly IList<Type> s_stopAtTypes = new List<Type> { typeof(View), typeof(Layout<>), typeof(VisualElement), typeof(Element) };
+
+ readonly BindableProperty _classStyleProperty = BindableProperty.Create("ClassStyle", typeof(IList<Style>), typeof(VisualElement), default(IList<Style>),
+ propertyChanged: (bindable, oldvalue, newvalue) => ((VisualElement)bindable)._mergedStyle.OnClassStyleChanged());
+
+ readonly List<BindableProperty> _implicitStyles = new List<BindableProperty>();
+
+ IStyle _classStyle;
+
+ IStyle _implicitStyle;
+
+ IStyle _style;
+
+ string _styleClass;
+
+ public MergedStyle(Type targetType, BindableObject target)
+ {
+ Target = target;
+ TargetType = targetType;
+ RegisterImplicitStyles();
+ Apply(Target);
+ }
+
+ public IStyle Style
+ {
+ get { return _style; }
+ set { SetStyle(ImplicitStyle, ClassStyle, value); }
+ }
+
+ public string StyleClass
+ {
+ get { return _styleClass; }
+ set
+ {
+ string val = string.IsNullOrWhiteSpace(value) ? null : value.Trim();
+ if (_styleClass == val)
+ return;
+
+ if (_styleClass != null)
+ Target.RemoveDynamicResource(_classStyleProperty);
+
+ _styleClass = val;
+
+ if (_styleClass != null)
+ Target.SetDynamicResource(_classStyleProperty, Forms.Style.StyleClassPrefix + _styleClass);
+ }
+ }
+
+ public BindableObject Target { get; }
+
+ IStyle ClassStyle
+ {
+ get { return _classStyle; }
+ set { SetStyle(ImplicitStyle, value, Style); }
+ }
+
+ IStyle ImplicitStyle
+ {
+ get { return _implicitStyle; }
+ set { SetStyle(value, ClassStyle, Style); }
+ }
+
+ public void Apply(BindableObject bindable)
+ {
+ ImplicitStyle?.Apply(bindable);
+ ClassStyle?.Apply(bindable);
+ Style?.Apply(bindable);
+ }
+
+ public Type TargetType { get; }
+
+ public void UnApply(BindableObject bindable)
+ {
+ Style?.UnApply(bindable);
+ ClassStyle?.UnApply(bindable);
+ ImplicitStyle?.UnApply(bindable);
+ }
+
+ void OnClassStyleChanged()
+ {
+ var classStyles = Target.GetValue(_classStyleProperty) as IList<Style>;
+ if (classStyles == null)
+ ClassStyle = null;
+ else
+ {
+ ClassStyle = classStyles.FirstOrDefault(s => s.CanBeAppliedTo(TargetType));
+ }
+ }
+
+ void OnImplicitStyleChanged()
+ {
+ var first = true;
+ foreach (BindableProperty implicitStyleProperty in _implicitStyles)
+ {
+ var implicitStyle = (Style)Target.GetValue(implicitStyleProperty);
+ if (implicitStyle != null)
+ {
+ if (first || implicitStyle.ApplyToDerivedTypes)
+ {
+ ImplicitStyle = implicitStyle;
+ return;
+ }
+ }
+ first = false;
+ }
+ }
+
+ void RegisterImplicitStyles()
+ {
+ Type type = TargetType;
+ while (true)
+ {
+ BindableProperty implicitStyleProperty = BindableProperty.Create("ImplicitStyle", typeof(Style), typeof(VisualElement), default(Style),
+ propertyChanged: (bindable, oldvalue, newvalue) => ((VisualElement)bindable)._mergedStyle.OnImplicitStyleChanged());
+ Target.SetDynamicResource(implicitStyleProperty, type.FullName);
+ _implicitStyles.Add(implicitStyleProperty);
+ type = type.GetTypeInfo().BaseType;
+ if (s_stopAtTypes.Contains(type))
+ return;
+ }
+ }
+
+ void SetStyle(IStyle implicitStyle, IStyle classStyle, IStyle style)
+ {
+ bool shouldReApplyStyle = implicitStyle != ImplicitStyle || classStyle != ClassStyle || Style != style;
+ bool shouldReApplyClassStyle = implicitStyle != ImplicitStyle || classStyle != ClassStyle;
+ bool shouldReApplyImplicitStyle = implicitStyle != ImplicitStyle && (Style as Style == null || ((Style)Style).CanCascade);
+
+ if (shouldReApplyStyle)
+ Style?.UnApply(Target);
+ if (shouldReApplyClassStyle)
+ ClassStyle?.UnApply(Target);
+ if (shouldReApplyImplicitStyle)
+ ImplicitStyle?.UnApply(Target);
+
+ _implicitStyle = implicitStyle;
+ _classStyle = classStyle;
+ _style = style;
+
+ if (shouldReApplyImplicitStyle)
+ ImplicitStyle?.Apply(Target);
+ if (shouldReApplyClassStyle)
+ ClassStyle?.Apply(Target);
+ if (shouldReApplyStyle)
+ Style?.Apply(Target);
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/MessagingCenter.cs b/Xamarin.Forms.Core/MessagingCenter.cs
new file mode 100644
index 00000000..973531ab
--- /dev/null
+++ b/Xamarin.Forms.Core/MessagingCenter.cs
@@ -0,0 +1,131 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Xamarin.Forms
+{
+ public static class MessagingCenter
+ {
+ static readonly Dictionary<Tuple<string, Type, Type>, List<Tuple<WeakReference, Action<object, object>>>> s_callbacks =
+ new Dictionary<Tuple<string, Type, Type>, List<Tuple<WeakReference, Action<object, object>>>>();
+
+ public static void Send<TSender, TArgs>(TSender sender, string message, TArgs args) where TSender : class
+ {
+ if (sender == null)
+ throw new ArgumentNullException("sender");
+ InnerSend(message, typeof(TSender), typeof(TArgs), sender, args);
+ }
+
+ public static void Send<TSender>(TSender sender, string message) where TSender : class
+ {
+ if (sender == null)
+ throw new ArgumentNullException("sender");
+ InnerSend(message, typeof(TSender), null, sender, null);
+ }
+
+ public static void Subscribe<TSender, TArgs>(object subscriber, string message, Action<TSender, TArgs> callback, TSender source = null) where TSender : class
+ {
+ if (subscriber == null)
+ throw new ArgumentNullException("subscriber");
+ if (callback == null)
+ throw new ArgumentNullException("callback");
+
+ Action<object, object> wrap = (sender, args) =>
+ {
+ var send = (TSender)sender;
+ if (source == null || send == source)
+ callback((TSender)sender, (TArgs)args);
+ };
+
+ InnerSubscribe(subscriber, message, typeof(TSender), typeof(TArgs), wrap);
+ }
+
+ public static void Subscribe<TSender>(object subscriber, string message, Action<TSender> callback, TSender source = null) where TSender : class
+ {
+ if (subscriber == null)
+ throw new ArgumentNullException("subscriber");
+ if (callback == null)
+ throw new ArgumentNullException("callback");
+
+ Action<object, object> wrap = (sender, args) =>
+ {
+ var send = (TSender)sender;
+ if (source == null || send == source)
+ callback((TSender)sender);
+ };
+
+ InnerSubscribe(subscriber, message, typeof(TSender), null, wrap);
+ }
+
+ public static void Unsubscribe<TSender, TArgs>(object subscriber, string message) where TSender : class
+ {
+ InnerUnsubscribe(message, typeof(TSender), typeof(TArgs), subscriber);
+ }
+
+ public static void Unsubscribe<TSender>(object subscriber, string message) where TSender : class
+ {
+ InnerUnsubscribe(message, typeof(TSender), null, subscriber);
+ }
+
+ internal static void ClearSubscribers()
+ {
+ s_callbacks.Clear();
+ }
+
+ static void InnerSend(string message, Type senderType, Type argType, object sender, object args)
+ {
+ if (message == null)
+ throw new ArgumentNullException("message");
+ var key = new Tuple<string, Type, Type>(message, senderType, argType);
+ if (!s_callbacks.ContainsKey(key))
+ return;
+ List<Tuple<WeakReference, Action<object, object>>> actions = s_callbacks[key];
+ if (actions == null || !actions.Any())
+ return; // should not be reachable
+
+ // ok so this code looks a bit funky but here is the gist of the problem. It is possible that in the course
+ // of executing the callbacks for this message someone will subscribe/unsubscribe from the same message in
+ // the callback. This would invalidate the enumerator. To work around this we make a copy. However if you unsubscribe
+ // from a message you can fairly reasonably expect that you will therefor not receive a call. To fix this we then
+ // check that the item we are about to send the message to actually exists in the live list.
+ List<Tuple<WeakReference, Action<object, object>>> actionsCopy = actions.ToList();
+ foreach (Tuple<WeakReference, Action<object, object>> action in actionsCopy)
+ {
+ if (action.Item1.IsAlive && actions.Contains(action))
+ action.Item2(sender, args);
+ }
+ }
+
+ static void InnerSubscribe(object subscriber, string message, Type senderType, Type argType, Action<object, object> callback)
+ {
+ if (message == null)
+ throw new ArgumentNullException("message");
+ var key = new Tuple<string, Type, Type>(message, senderType, argType);
+ var value = new Tuple<WeakReference, Action<object, object>>(new WeakReference(subscriber), callback);
+ if (s_callbacks.ContainsKey(key))
+ {
+ s_callbacks[key].Add(value);
+ }
+ else
+ {
+ var list = new List<Tuple<WeakReference, Action<object, object>>> { value };
+ s_callbacks[key] = list;
+ }
+ }
+
+ static void InnerUnsubscribe(string message, Type senderType, Type argType, object subscriber)
+ {
+ if (subscriber == null)
+ throw new ArgumentNullException("subscriber");
+ if (message == null)
+ throw new ArgumentNullException("message");
+
+ var key = new Tuple<string, Type, Type>(message, senderType, argType);
+ if (!s_callbacks.ContainsKey(key))
+ return;
+ s_callbacks[key].RemoveAll(tuple => !tuple.Item1.IsAlive || tuple.Item1.Target == subscriber);
+ if (!s_callbacks[key].Any())
+ s_callbacks.Remove(key);
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/ModalEventArgs.cs b/Xamarin.Forms.Core/ModalEventArgs.cs
new file mode 100644
index 00000000..483ea5ad
--- /dev/null
+++ b/Xamarin.Forms.Core/ModalEventArgs.cs
@@ -0,0 +1,14 @@
+using System;
+
+namespace Xamarin.Forms
+{
+ public abstract class ModalEventArgs : EventArgs
+ {
+ protected ModalEventArgs(Page modal)
+ {
+ Modal = modal;
+ }
+
+ public Page Modal { get; private set; }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/ModalPoppedEventArgs.cs b/Xamarin.Forms.Core/ModalPoppedEventArgs.cs
new file mode 100644
index 00000000..3c17f009
--- /dev/null
+++ b/Xamarin.Forms.Core/ModalPoppedEventArgs.cs
@@ -0,0 +1,9 @@
+namespace Xamarin.Forms
+{
+ public class ModalPoppedEventArgs : ModalEventArgs
+ {
+ public ModalPoppedEventArgs(Page modal) : base(modal)
+ {
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/ModalPoppingEventArgs.cs b/Xamarin.Forms.Core/ModalPoppingEventArgs.cs
new file mode 100644
index 00000000..57c7a657
--- /dev/null
+++ b/Xamarin.Forms.Core/ModalPoppingEventArgs.cs
@@ -0,0 +1,11 @@
+namespace Xamarin.Forms
+{
+ public class ModalPoppingEventArgs : ModalEventArgs
+ {
+ public ModalPoppingEventArgs(Page modal) : base(modal)
+ {
+ }
+
+ public bool Cancel { get; set; }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/ModalPushedEventArgs.cs b/Xamarin.Forms.Core/ModalPushedEventArgs.cs
new file mode 100644
index 00000000..d09caf4d
--- /dev/null
+++ b/Xamarin.Forms.Core/ModalPushedEventArgs.cs
@@ -0,0 +1,9 @@
+namespace Xamarin.Forms
+{
+ public class ModalPushedEventArgs : ModalEventArgs
+ {
+ public ModalPushedEventArgs(Page modal) : base(modal)
+ {
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/ModalPushingEventArgs.cs b/Xamarin.Forms.Core/ModalPushingEventArgs.cs
new file mode 100644
index 00000000..12396c9e
--- /dev/null
+++ b/Xamarin.Forms.Core/ModalPushingEventArgs.cs
@@ -0,0 +1,9 @@
+namespace Xamarin.Forms
+{
+ public class ModalPushingEventArgs : ModalEventArgs
+ {
+ public ModalPushingEventArgs(Page modal) : base(modal)
+ {
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/MultiPage.cs b/Xamarin.Forms.Core/MultiPage.cs
new file mode 100644
index 00000000..89fd7e9c
--- /dev/null
+++ b/Xamarin.Forms.Core/MultiPage.cs
@@ -0,0 +1,359 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Collections.Specialized;
+using System.Linq;
+using System.Runtime.CompilerServices;
+
+namespace Xamarin.Forms
+{
+ [ContentProperty("Children")]
+ public abstract class MultiPage<T> : Page, IViewContainer<T>, IPageContainer<T>, IItemsView<T> where T : Page
+ {
+ public static readonly BindableProperty ItemsSourceProperty = BindableProperty.Create("ItemsSource", typeof(IEnumerable), typeof(MultiPage<>), null);
+
+ public static readonly BindableProperty ItemTemplateProperty = BindableProperty.Create("ItemTemplate", typeof(DataTemplate), typeof(MultiPage<>), null);
+
+ public static readonly BindableProperty SelectedItemProperty = BindableProperty.Create("SelectedItem", typeof(object), typeof(MultiPage<>), null, BindingMode.TwoWay);
+
+ internal static readonly BindableProperty IndexProperty = BindableProperty.Create("Index", typeof(int), typeof(Page), -1);
+
+ readonly ElementCollection<T> _children;
+ readonly TemplatedItemsList<MultiPage<T>, T> _templatedItems;
+
+ T _current;
+
+ protected MultiPage()
+ {
+ _templatedItems = new TemplatedItemsList<MultiPage<T>, T>(this, ItemsSourceProperty, ItemTemplateProperty);
+ _templatedItems.CollectionChanged += OnTemplatedItemsChanged;
+
+ _children = new ElementCollection<T>(InternalChildren);
+ InternalChildren.CollectionChanged += OnChildrenChanged;
+ }
+
+ public IEnumerable ItemsSource
+ {
+ get { return (IEnumerable)GetValue(ItemsSourceProperty); }
+ set { SetValue(ItemsSourceProperty, value); }
+ }
+
+ public DataTemplate ItemTemplate
+ {
+ get { return (DataTemplate)GetValue(ItemTemplateProperty); }
+ set { SetValue(ItemTemplateProperty, value); }
+ }
+
+ public object SelectedItem
+ {
+ get { return GetValue(SelectedItemProperty); }
+ set { SetValue(SelectedItemProperty, value); }
+ }
+
+ T IItemsView<T>.CreateDefault(object item)
+ {
+ return CreateDefault(item);
+ }
+
+ void IItemsView<T>.SetupContent(T content, int index)
+ {
+ SetupContent(content, index);
+ }
+
+ void IItemsView<T>.UnhookContent(T content)
+ {
+ UnhookContent(content);
+ }
+
+ public T CurrentPage
+ {
+ get { return _current; }
+ set
+ {
+ if (_current == value)
+ return;
+
+ OnPropertyChanging();
+ _current = value;
+ OnPropertyChanged();
+ OnCurrentPageChanged();
+ }
+ }
+
+ public IList<T> Children
+ {
+ get { return _children; }
+ }
+
+ public event EventHandler CurrentPageChanged;
+
+ public event NotifyCollectionChangedEventHandler PagesChanged;
+
+ protected abstract T CreateDefault(object item);
+
+ protected override bool OnBackButtonPressed()
+ {
+ if (CurrentPage != null)
+ {
+ bool handled = CurrentPage.SendBackButtonPressed();
+ if (handled)
+ return true;
+ }
+
+ return base.OnBackButtonPressed();
+ }
+
+ protected override void OnChildAdded(Element child)
+ {
+ base.OnChildAdded(child);
+
+ ForceLayout();
+ }
+
+ protected virtual void OnCurrentPageChanged()
+ {
+ EventHandler changed = CurrentPageChanged;
+ if (changed != null)
+ changed(this, EventArgs.Empty);
+ }
+
+ protected virtual void OnPagesChanged(NotifyCollectionChangedEventArgs e)
+ {
+ NotifyCollectionChangedEventHandler handler = PagesChanged;
+ if (handler != null)
+ handler(this, e);
+ }
+
+ protected override void OnPropertyChanged([CallerMemberName] string propertyName = null)
+ {
+ if (propertyName == ItemsSourceProperty.PropertyName)
+ _children.IsReadOnly = ItemsSource != null;
+ else if (propertyName == SelectedItemProperty.PropertyName)
+ {
+ UpdateCurrentPage();
+ }
+ else if (propertyName == "CurrentPage" && ItemsSource != null)
+ {
+ if (CurrentPage == null)
+ {
+ SelectedItem = null;
+ }
+ else
+ {
+ int index = _templatedItems.IndexOf(CurrentPage);
+ SelectedItem = index != -1 ? _templatedItems.ListProxy[index] : null;
+ }
+ }
+
+ base.OnPropertyChanged(propertyName);
+ }
+
+ protected virtual void SetupContent(T content, int index)
+ {
+ }
+
+ protected virtual void UnhookContent(T content)
+ {
+ }
+
+ internal static int GetIndex(T page)
+ {
+ if (page == null)
+ throw new ArgumentNullException("page");
+
+ return (int)page.GetValue(IndexProperty);
+ }
+
+ internal T GetPageByIndex(int index)
+ {
+ foreach (T page in InternalChildren)
+ {
+ if (index == GetIndex(page))
+ return page;
+ }
+ return null;
+ }
+
+ internal static void SetIndex(Page page, int index)
+ {
+ if (page == null)
+ throw new ArgumentNullException("page");
+
+ page.SetValue(IndexProperty, index);
+ }
+
+ void OnChildrenChanged(object sender, NotifyCollectionChangedEventArgs e)
+ {
+ if (Children.IsReadOnly)
+ return;
+
+ var i = 0;
+ foreach (T page in Children)
+ SetIndex(page, i++);
+
+ OnPagesChanged(e);
+
+ if (CurrentPage == null || Children.IndexOf(CurrentPage) == -1)
+ CurrentPage = Children.FirstOrDefault();
+ }
+
+ void OnTemplatedItemsChanged(object sender, NotifyCollectionChangedEventArgs e)
+ {
+ switch (e.Action)
+ {
+ case NotifyCollectionChangedAction.Add:
+ if (e.NewStartingIndex < 0)
+ goto case NotifyCollectionChangedAction.Reset;
+
+ for (int i = e.NewStartingIndex; i < Children.Count; i++)
+ SetIndex((T)InternalChildren[i], i + e.NewItems.Count);
+
+ for (var i = 0; i < e.NewItems.Count; i++)
+ {
+ var page = (T)e.NewItems[i];
+ page.Owned = true;
+ int index = i + e.NewStartingIndex;
+ SetIndex(page, index);
+ InternalChildren.Insert(index, (T)e.NewItems[i]);
+ }
+
+ break;
+
+ case NotifyCollectionChangedAction.Remove:
+ if (e.OldStartingIndex < 0)
+ goto case NotifyCollectionChangedAction.Reset;
+
+ int removeIndex = e.OldStartingIndex;
+ for (int i = removeIndex + e.OldItems.Count; i < Children.Count; i++)
+ SetIndex((T)InternalChildren[i], removeIndex++);
+
+ for (var i = 0; i < e.OldItems.Count; i++)
+ {
+ Element element = InternalChildren[e.OldStartingIndex];
+ InternalChildren.RemoveAt(e.OldStartingIndex);
+ element.Owned = false;
+ }
+
+ break;
+
+ case NotifyCollectionChangedAction.Move:
+ if (e.NewStartingIndex < 0 || e.OldStartingIndex < 0)
+ goto case NotifyCollectionChangedAction.Reset;
+
+ if (e.NewStartingIndex == e.OldStartingIndex)
+ return;
+
+ bool movingForward = e.OldStartingIndex < e.NewStartingIndex;
+
+ if (movingForward)
+ {
+ int moveIndex = e.OldStartingIndex;
+ for (int i = moveIndex + e.OldItems.Count; i <= e.NewStartingIndex; i++)
+ SetIndex((T)InternalChildren[i], moveIndex++);
+ }
+ else
+ {
+ for (var i = 0; i < e.OldStartingIndex - e.NewStartingIndex; i++)
+ {
+ var page = (T)InternalChildren[i + e.NewStartingIndex];
+ SetIndex(page, GetIndex(page) + e.OldItems.Count);
+ }
+ }
+
+ for (var i = 0; i < e.OldItems.Count; i++)
+ InternalChildren.RemoveAt(e.OldStartingIndex);
+
+ int insertIndex = e.NewStartingIndex;
+ if (movingForward)
+ insertIndex -= e.OldItems.Count - 1;
+
+ for (var i = 0; i < e.OldItems.Count; i++)
+ {
+ var page = (T)e.OldItems[i];
+ SetIndex(page, insertIndex + i);
+ InternalChildren.Insert(insertIndex + i, page);
+ }
+
+ break;
+
+ case NotifyCollectionChangedAction.Replace:
+ if (e.OldStartingIndex < 0)
+ goto case NotifyCollectionChangedAction.Reset;
+
+ for (int i = e.OldStartingIndex; i - e.OldStartingIndex < e.OldItems.Count; i++)
+ {
+ Element element = InternalChildren[i];
+ InternalChildren.RemoveAt(i);
+ element.Owned = false;
+
+ T page = _templatedItems.GetOrCreateContent(i, e.NewItems[i - e.OldStartingIndex]);
+ page.Owned = true;
+ SetIndex(page, i);
+ InternalChildren.Insert(i, page);
+ }
+
+ break;
+
+ case NotifyCollectionChangedAction.Reset:
+ Reset();
+ return;
+ }
+
+ OnPagesChanged(e);
+ UpdateCurrentPage();
+ }
+
+ void Reset()
+ {
+ List<Element> snapshot = InternalChildren.ToList();
+
+ InternalChildren.Clear();
+
+ foreach (Element element in snapshot)
+ element.Owned = false;
+
+ for (var i = 0; i < _templatedItems.Count; i++)
+ {
+ T page = _templatedItems.GetOrCreateContent(i, _templatedItems.ListProxy[i]);
+ page.Owned = true;
+ SetIndex(page, i);
+ InternalChildren.Add(page);
+ }
+
+ var currentNeedsUpdate = true;
+
+ BatchBegin();
+
+ if (ItemsSource != null)
+ {
+ object selected = SelectedItem;
+ if (selected == null || !ItemsSource.Cast<object>().Contains(selected))
+ {
+ SelectedItem = ItemsSource.Cast<object>().FirstOrDefault();
+ currentNeedsUpdate = false;
+ }
+ }
+
+ if (currentNeedsUpdate)
+ UpdateCurrentPage();
+
+ OnPagesChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
+
+ BatchCommit();
+ }
+
+ void UpdateCurrentPage()
+ {
+ if (ItemsSource != null)
+ {
+ int index = _templatedItems.ListProxy.IndexOf(SelectedItem);
+ if (index == -1)
+ CurrentPage = (T)InternalChildren.FirstOrDefault();
+ else
+ CurrentPage = _templatedItems.GetOrCreateContent(index, SelectedItem);
+ }
+ else if (SelectedItem is T)
+ CurrentPage = (T)SelectedItem;
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/NameScopeExtensions.cs b/Xamarin.Forms.Core/NameScopeExtensions.cs
new file mode 100644
index 00000000..b9acb460
--- /dev/null
+++ b/Xamarin.Forms.Core/NameScopeExtensions.cs
@@ -0,0 +1,17 @@
+using Xamarin.Forms.Internals;
+
+namespace Xamarin.Forms
+{
+ public static class NameScopeExtensions
+ {
+ public static T FindByName<T>(this Element element, string name)
+ {
+ return ((INameScope)element).FindByName<T>(name);
+ }
+
+ internal static T FindByName<T>(this INameScope namescope, string name)
+ {
+ return (T)namescope.FindByName(name);
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/NamedSize.cs b/Xamarin.Forms.Core/NamedSize.cs
new file mode 100644
index 00000000..92f7a579
--- /dev/null
+++ b/Xamarin.Forms.Core/NamedSize.cs
@@ -0,0 +1,11 @@
+namespace Xamarin.Forms
+{
+ public enum NamedSize
+ {
+ Default = 0,
+ Micro = 1,
+ Small = 2,
+ Medium = 3,
+ Large = 4
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/NavigationEventArgs.cs b/Xamarin.Forms.Core/NavigationEventArgs.cs
new file mode 100644
index 00000000..0ccd4343
--- /dev/null
+++ b/Xamarin.Forms.Core/NavigationEventArgs.cs
@@ -0,0 +1,17 @@
+using System;
+
+namespace Xamarin.Forms
+{
+ public class NavigationEventArgs : EventArgs
+ {
+ public NavigationEventArgs(Page page)
+ {
+ if (page == null)
+ throw new ArgumentNullException("page");
+
+ Page = page;
+ }
+
+ public Page Page { get; private set; }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/NavigationMenu.cs b/Xamarin.Forms.Core/NavigationMenu.cs
new file mode 100644
index 00000000..2386dd29
--- /dev/null
+++ b/Xamarin.Forms.Core/NavigationMenu.cs
@@ -0,0 +1,71 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Xamarin.Forms.Platform;
+
+namespace Xamarin.Forms
+{
+ // Mark as internal until renderers are ready for release after 1.0
+ [RenderWith(typeof(_NavigationMenuRenderer))]
+ internal class NavigationMenu : View
+ {
+ readonly List<Page> _targets = new List<Page>();
+
+ public IEnumerable<Page> Targets
+ {
+ get { return _targets; }
+ set
+ {
+ if (_targets.AsEnumerable().SequenceEqual(value))
+ return;
+
+ foreach (Page page in value)
+ {
+ VerifyTarget(page);
+ }
+
+ OnPropertyChanging();
+ _targets.Clear();
+ _targets.AddRange(value);
+ OnPropertyChanged();
+ }
+ }
+
+ public void Add(Page target)
+ {
+ if (_targets.Contains(target))
+ return;
+ VerifyTarget(target);
+
+ OnPropertyChanging("Targets");
+ _targets.Add(target);
+ OnPropertyChanged("Targets");
+ }
+
+ public void Remove(Page target)
+ {
+ if (_targets.Contains(target))
+ {
+ OnPropertyChanging("Targets");
+ if (_targets.Remove(target))
+ OnPropertyChanged("Targets");
+ }
+ }
+
+ internal void SendTargetSelected(Page target)
+ {
+ TargetSelected(target);
+ }
+
+ void TargetSelected(Page target)
+ {
+ Navigation.PushAsync(target);
+ }
+
+ void VerifyTarget(Page target)
+ {
+ if (target.Icon == null || string.IsNullOrWhiteSpace(target.Icon.File))
+ throw new Exception("Icon must be set for each page before adding them to a Navigation Menu");
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/NavigationModel.cs b/Xamarin.Forms.Core/NavigationModel.cs
new file mode 100644
index 00000000..4591d4a4
--- /dev/null
+++ b/Xamarin.Forms.Core/NavigationModel.cs
@@ -0,0 +1,177 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Xamarin.Forms
+{
+ internal class NavigationModel
+ {
+ readonly List<Page> _modalStack = new List<Page>();
+ readonly List<List<Page>> _navTree = new List<List<Page>>();
+
+ public Page CurrentPage
+ {
+ get
+ {
+ if (_navTree.Any())
+ return _navTree.Last().Last();
+ return null;
+ }
+ }
+
+ public IEnumerable<Page> Modals
+ {
+ get { return _modalStack; }
+ }
+
+ public IEnumerable<Page> Roots
+ {
+ get
+ {
+ foreach (List<Page> list in _navTree)
+ {
+ yield return list[0];
+ }
+ }
+ }
+
+ public IReadOnlyList<IReadOnlyList<Page>> Tree
+ {
+ get { return _navTree; }
+ }
+
+ public void Clear()
+ {
+ _navTree.Clear();
+ }
+
+ public void InsertPageBefore(Page page, Page before)
+ {
+ List<Page> currentStack = _navTree.Last();
+ int index = currentStack.IndexOf(before);
+
+ if (index == -1)
+ throw new ArgumentException("before must be in the current navigation context");
+
+ currentStack.Insert(index, page);
+ }
+
+ public Page Pop(Page ancestralNav)
+ {
+ ancestralNav = AncestorToRoot(ancestralNav);
+ foreach (List<Page> stack in _navTree)
+ {
+ if (stack.Contains(ancestralNav))
+ {
+ if (stack.Count <= 1)
+ throw new InvalidNavigationException("Can not pop final item in stack");
+ Page result = stack.Last();
+ stack.Remove(result);
+ return result;
+ }
+ }
+
+ throw new InvalidNavigationException("Popped from unpushed item?");
+ }
+
+ public Page PopModal()
+ {
+ if (_navTree.Count <= 1)
+ throw new InvalidNavigationException("Can't pop modal without any modals pushed");
+ Page modal = _navTree.Last().First();
+ _modalStack.Remove(modal);
+ _navTree.Remove(_navTree.Last());
+ return modal;
+ }
+
+ public Page PopTopPage()
+ {
+ Page itemToRemove;
+ if (_navTree.Count == 1)
+ {
+ if (_navTree[0].Count > 1)
+ {
+ itemToRemove = _navTree[0].Last();
+ _navTree[0].Remove(itemToRemove);
+ return itemToRemove;
+ }
+ return null;
+ }
+ itemToRemove = _navTree.Last().Last();
+ _navTree.Last().Remove(itemToRemove);
+ if (!_navTree.Last().Any())
+ {
+ _navTree.RemoveAt(_navTree.Count - 1);
+ }
+ return itemToRemove;
+ }
+
+ public void PopToRoot(Page ancestralNav)
+ {
+ ancestralNav = AncestorToRoot(ancestralNav);
+ foreach (List<Page> stack in _navTree)
+ {
+ if (stack.Contains(ancestralNav))
+ {
+ if (stack.Count <= 1)
+ throw new InvalidNavigationException("Can not pop final item in stack");
+ stack.RemoveRange(1, stack.Count - 1);
+ return;
+ }
+ }
+
+ throw new InvalidNavigationException("Popped from unpushed item?");
+ }
+
+ public void Push(Page page, Page ancestralNav)
+ {
+ if (ancestralNav == null)
+ {
+ if (_navTree.Any())
+ throw new InvalidNavigationException("Ancestor must be provided for all pushes except first");
+ _navTree.Add(new List<Page> { page });
+ return;
+ }
+
+ ancestralNav = AncestorToRoot(ancestralNav);
+
+ foreach (List<Page> stack in _navTree)
+ {
+ if (stack.Contains(ancestralNav))
+ {
+ stack.Add(page);
+ return;
+ }
+ }
+
+ throw new InvalidNavigationException("Invalid ancestor passed");
+ }
+
+ public void PushModal(Page page)
+ {
+ _navTree.Add(new List<Page> { page });
+ _modalStack.Add(page);
+ }
+
+ public bool RemovePage(Page page)
+ {
+ bool found;
+ List<Page> currentStack = _navTree.Last();
+ var i = 0;
+ while (!(found = currentStack.Remove(page)) && i < _navTree.Count - 1)
+ {
+ currentStack = _navTree[i++];
+ }
+
+ return found;
+ }
+
+ Page AncestorToRoot(Page ancestor)
+ {
+ Page result = ancestor;
+ while (!Application.IsApplicationOrNull(result.RealParent))
+ result = (Page)result.RealParent;
+ return result;
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/NavigationPage.cs b/Xamarin.Forms.Core/NavigationPage.cs
new file mode 100644
index 00000000..61545b11
--- /dev/null
+++ b/Xamarin.Forms.Core/NavigationPage.cs
@@ -0,0 +1,411 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using Xamarin.Forms.Platform;
+
+namespace Xamarin.Forms
+{
+ [RenderWith(typeof(_NavigationPageRenderer))]
+ public class NavigationPage : Page, IPageContainer<Page>
+ {
+ public static readonly BindableProperty BackButtonTitleProperty = BindableProperty.CreateAttached("BackButtonTitle", typeof(string), typeof(Page), null);
+
+ public static readonly BindableProperty HasNavigationBarProperty = BindableProperty.CreateAttached("HasNavigationBar", typeof(bool), typeof(Page), true);
+
+ public static readonly BindableProperty HasBackButtonProperty = BindableProperty.CreateAttached("HasBackButton", typeof(bool), typeof(NavigationPage), true);
+
+ [Obsolete("Use BarBackgroundColorProperty and BarTextColorProperty to change NavigationPage bar color properties")] public static readonly BindableProperty TintProperty =
+ BindableProperty.Create("Tint", typeof(Color), typeof(NavigationPage), Color.Default);
+
+ public static readonly BindableProperty BarBackgroundColorProperty = BindableProperty.Create("BarBackgroundColor", typeof(Color), typeof(NavigationPage), Color.Default);
+
+ public static readonly BindableProperty BarTextColorProperty = BindableProperty.Create("BarTextColor", typeof(Color), typeof(NavigationPage), Color.Default);
+
+ public static readonly BindableProperty TitleIconProperty = BindableProperty.CreateAttached("TitleIcon", typeof(FileImageSource), typeof(NavigationPage), default(FileImageSource));
+
+ static readonly BindablePropertyKey CurrentPagePropertyKey = BindableProperty.CreateReadOnly("CurrentPage", typeof(Page), typeof(NavigationPage), null);
+ public static readonly BindableProperty CurrentPageProperty = CurrentPagePropertyKey.BindableProperty;
+
+ public NavigationPage()
+ {
+ Navigation = new NavigationImpl(this);
+ }
+
+ public NavigationPage(Page root) : this()
+ {
+ PushPage(root);
+ }
+
+ public Color BarBackgroundColor
+ {
+ get { return (Color)GetValue(BarBackgroundColorProperty); }
+ set { SetValue(BarBackgroundColorProperty, value); }
+ }
+
+ public Color BarTextColor
+ {
+ get { return (Color)GetValue(BarTextColorProperty); }
+ set { SetValue(BarTextColorProperty, value); }
+ }
+
+ [Obsolete("Use BarBackgroundColor and BarTextColor to change NavigationPage bar color properties")]
+ public Color Tint
+ {
+ get { return (Color)GetValue(TintProperty); }
+ set { SetValue(TintProperty, value); }
+ }
+
+ internal Task CurrentNavigationTask { get; set; }
+
+ internal Stack<Page> StackCopy
+ {
+ get
+ {
+ var result = new Stack<Page>(InternalChildren.Count);
+ foreach (Page page in InternalChildren)
+ result.Push(page);
+ return result;
+ }
+ }
+
+ internal int StackDepth
+ {
+ get { return InternalChildren.Count; }
+ }
+
+ public Page CurrentPage
+ {
+ get { return (Page)GetValue(CurrentPageProperty); }
+ private set { SetValue(CurrentPagePropertyKey, value); }
+ }
+
+ public static string GetBackButtonTitle(BindableObject page)
+ {
+ return (string)page.GetValue(BackButtonTitleProperty);
+ }
+
+ public static bool GetHasBackButton(Page page)
+ {
+ if (page == null)
+ throw new ArgumentNullException("page");
+ return (bool)page.GetValue(HasBackButtonProperty);
+ }
+
+ public static bool GetHasNavigationBar(BindableObject page)
+ {
+ return (bool)page.GetValue(HasNavigationBarProperty);
+ }
+
+ public static FileImageSource GetTitleIcon(BindableObject bindable)
+ {
+ return (FileImageSource)bindable.GetValue(TitleIconProperty);
+ }
+
+ public Task<Page> PopAsync()
+ {
+ return PopAsync(true);
+