summaryrefslogtreecommitdiff
path: root/Xamarin.Forms.Platform.WinRT
diff options
context:
space:
mode:
authorJason Smith <jason.smith@xamarin.com>2016-03-22 13:02:25 -0700
committerJason Smith <jason.smith@xamarin.com>2016-03-22 16:13:41 -0700
commit17fdde66d94155fc62a034fa6658995bef6fd6e5 (patch)
treeb5e5073a2a7b15cdbe826faa5c763e270a505729 /Xamarin.Forms.Platform.WinRT
downloadxamarin-forms-17fdde66d94155fc62a034fa6658995bef6fd6e5.tar.gz
xamarin-forms-17fdde66d94155fc62a034fa6658995bef6fd6e5.tar.bz2
xamarin-forms-17fdde66d94155fc62a034fa6658995bef6fd6e5.zip
Initial import
Diffstat (limited to 'Xamarin.Forms.Platform.WinRT')
-rw-r--r--Xamarin.Forms.Platform.WinRT/ActivityIndicatorRenderer.cs68
-rw-r--r--Xamarin.Forms.Platform.WinRT/AlignmentExtensions.cs39
-rw-r--r--Xamarin.Forms.Platform.WinRT/AsyncValue.cs95
-rw-r--r--Xamarin.Forms.Platform.WinRT/BackgroundTracker.cs82
-rw-r--r--Xamarin.Forms.Platform.WinRT/BoolToVisibilityConverter.cs30
-rw-r--r--Xamarin.Forms.Platform.WinRT/BoxViewRenderer.cs31
-rw-r--r--Xamarin.Forms.Platform.WinRT/ButtonRenderer.cs160
-rw-r--r--Xamarin.Forms.Platform.WinRT/CarouselPageRenderer.cs180
-rw-r--r--Xamarin.Forms.Platform.WinRT/CarouselViewRenderer.cs220
-rw-r--r--Xamarin.Forms.Platform.WinRT/CaseConverter.cs29
-rw-r--r--Xamarin.Forms.Platform.WinRT/CellControl.cs342
-rw-r--r--Xamarin.Forms.Platform.WinRT/CollapseWhenEmptyConverter.cs33
-rw-r--r--Xamarin.Forms.Platform.WinRT/ColorConverter.cs30
-rw-r--r--Xamarin.Forms.Platform.WinRT/ConvertExtensions.cs23
-rw-r--r--Xamarin.Forms.Platform.WinRT/DatePickerRenderer.cs97
-rw-r--r--Xamarin.Forms.Platform.WinRT/DefaultRenderer.cs14
-rw-r--r--Xamarin.Forms.Platform.WinRT/EditorRenderer.cs155
-rw-r--r--Xamarin.Forms.Platform.WinRT/EntryCellTextBox.cs30
-rw-r--r--Xamarin.Forms.Platform.WinRT/EntryRenderer.cs212
-rw-r--r--Xamarin.Forms.Platform.WinRT/ExportRendererAttribute.cs34
-rw-r--r--Xamarin.Forms.Platform.WinRT/Extensions.cs46
-rw-r--r--Xamarin.Forms.Platform.WinRT/FileImageSourceHandler.cs29
-rw-r--r--Xamarin.Forms.Platform.WinRT/FileImageSourcePathConverter.cs26
-rw-r--r--Xamarin.Forms.Platform.WinRT/FontExtensions.cs75
-rw-r--r--Xamarin.Forms.Platform.WinRT/FormsButton.cs46
-rw-r--r--Xamarin.Forms.Platform.WinRT/FormsComboBox.cs68
-rw-r--r--Xamarin.Forms.Platform.WinRT/FormsDatePicker.cs76
-rw-r--r--Xamarin.Forms.Platform.WinRT/FormsTextBox.cs332
-rw-r--r--Xamarin.Forms.Platform.WinRT/FormsTimePicker.cs76
-rw-r--r--Xamarin.Forms.Platform.WinRT/FrameRenderer.cs71
-rw-r--r--Xamarin.Forms.Platform.WinRT/FrameworkElementExtensions.cs114
-rw-r--r--Xamarin.Forms.Platform.WinRT/HeightConverter.cs32
-rw-r--r--Xamarin.Forms.Platform.WinRT/HorizontalTextAlignmentConverter.cs24
-rw-r--r--Xamarin.Forms.Platform.WinRT/ICellRenderer.cs14
-rw-r--r--Xamarin.Forms.Platform.WinRT/IImageSourceHandler.cs16
-rw-r--r--Xamarin.Forms.Platform.WinRT/ITitleProvider.cs21
-rw-r--r--Xamarin.Forms.Platform.WinRT/IToolbarProvider.cs16
-rw-r--r--Xamarin.Forms.Platform.WinRT/IVisualElementRenderer.cs23
-rw-r--r--Xamarin.Forms.Platform.WinRT/IWrapperAware.cs14
-rw-r--r--Xamarin.Forms.Platform.WinRT/ImageConverter.cs33
-rw-r--r--Xamarin.Forms.Platform.WinRT/ImageLoaderSourceHandler.cs52
-rw-r--r--Xamarin.Forms.Platform.WinRT/ImageRenderer.cs132
-rw-r--r--Xamarin.Forms.Platform.WinRT/KeyboardConverter.cs27
-rw-r--r--Xamarin.Forms.Platform.WinRT/KeyboardExtensions.cs57
-rw-r--r--Xamarin.Forms.Platform.WinRT/LabelRenderer.cs202
-rw-r--r--Xamarin.Forms.Platform.WinRT/LayoutExtensions.cs33
-rw-r--r--Xamarin.Forms.Platform.WinRT/LayoutRenderer.cs55
-rw-r--r--Xamarin.Forms.Platform.WinRT/ListGroupHeaderPresenter.cs46
-rw-r--r--Xamarin.Forms.Platform.WinRT/ListViewGroupStyleSelector.cs18
-rw-r--r--Xamarin.Forms.Platform.WinRT/ListViewRenderer.cs634
-rw-r--r--Xamarin.Forms.Platform.WinRT/MasterBackgroundConverter.cs75
-rw-r--r--Xamarin.Forms.Platform.WinRT/MasterDetailControl.cs168
-rw-r--r--Xamarin.Forms.Platform.WinRT/MasterDetailPageRenderer.cs291
-rw-r--r--Xamarin.Forms.Platform.WinRT/MenuItemCommand.cs48
-rw-r--r--Xamarin.Forms.Platform.WinRT/NativeViewWrapper.cs30
-rw-r--r--Xamarin.Forms.Platform.WinRT/NativeViewWrapperRenderer.cs74
-rw-r--r--Xamarin.Forms.Platform.WinRT/NavigationPageRenderer.cs554
-rw-r--r--Xamarin.Forms.Platform.WinRT/PageControl.xaml10
-rw-r--r--Xamarin.Forms.Platform.WinRT/PageControl.xaml.cs166
-rw-r--r--Xamarin.Forms.Platform.WinRT/PageRenderer.cs80
-rw-r--r--Xamarin.Forms.Platform.WinRT/PageToRenderedElementConverter.cs31
-rw-r--r--Xamarin.Forms.Platform.WinRT/PickerRenderer.cs147
-rw-r--r--Xamarin.Forms.Platform.WinRT/Platform.cs721
-rw-r--r--Xamarin.Forms.Platform.WinRT/PlatformEffect.cs14
-rw-r--r--Xamarin.Forms.Platform.WinRT/ProgressBarRenderer.cs59
-rw-r--r--Xamarin.Forms.Platform.WinRT/Properties/AssemblyInfo.cs57
-rw-r--r--Xamarin.Forms.Platform.WinRT/RendererFactory.cs19
-rw-r--r--Xamarin.Forms.Platform.WinRT/Resources.xaml87
-rw-r--r--Xamarin.Forms.Platform.WinRT/ScrollViewRenderer.cs194
-rw-r--r--Xamarin.Forms.Platform.WinRT/SliderRenderer.cs59
-rw-r--r--Xamarin.Forms.Platform.WinRT/StepperControl.xaml90
-rw-r--r--Xamarin.Forms.Platform.WinRT/StepperControl.xaml.cs94
-rw-r--r--Xamarin.Forms.Platform.WinRT/StepperRenderer.cs72
-rw-r--r--Xamarin.Forms.Platform.WinRT/StreamImagesourceHandler.cs36
-rw-r--r--Xamarin.Forms.Platform.WinRT/SwitchRenderer.cs50
-rw-r--r--Xamarin.Forms.Platform.WinRT/TableViewRenderer.cs85
-rw-r--r--Xamarin.Forms.Platform.WinRT/TaskExtensions.cs40
-rw-r--r--Xamarin.Forms.Platform.WinRT/TextAlignmentToHorizontalAlignmentConverter.cs48
-rw-r--r--Xamarin.Forms.Platform.WinRT/TextCellRenderer.cs79
-rw-r--r--Xamarin.Forms.Platform.WinRT/TimePickerRenderer.cs75
-rw-r--r--Xamarin.Forms.Platform.WinRT/ViewExtensions.cs26
-rw-r--r--Xamarin.Forms.Platform.WinRT/ViewRenderer.cs37
-rw-r--r--Xamarin.Forms.Platform.WinRT/ViewToRendererConverter.cs113
-rw-r--r--Xamarin.Forms.Platform.WinRT/VisualElementChangedEventArgs.cs30
-rw-r--r--Xamarin.Forms.Platform.WinRT/VisualElementExtensions.cs56
-rw-r--r--Xamarin.Forms.Platform.WinRT/VisualElementPackager.cs147
-rw-r--r--Xamarin.Forms.Platform.WinRT/VisualElementRenderer.cs401
-rw-r--r--Xamarin.Forms.Platform.WinRT/VisualElementTracker.cs534
-rw-r--r--Xamarin.Forms.Platform.WinRT/WebViewRenderer.cs174
-rw-r--r--Xamarin.Forms.Platform.WinRT/WindowsBasePage.cs56
-rw-r--r--Xamarin.Forms.Platform.WinRT/WindowsBasePlatformServices.cs179
-rw-r--r--Xamarin.Forms.Platform.WinRT/WindowsDeviceInfo.cs110
-rw-r--r--Xamarin.Forms.Platform.WinRT/WindowsExpressionSearch.cs170
-rw-r--r--Xamarin.Forms.Platform.WinRT/WindowsIsolatedStorage.cs119
-rw-r--r--Xamarin.Forms.Platform.WinRT/WindowsSerializer.cs60
-rw-r--r--Xamarin.Forms.Platform.WinRT/WindowsTicker.cs32
-rw-r--r--Xamarin.Forms.Platform.WinRT/Xamarin.Forms.Platform.WinRT.csproj179
97 files changed, 10288 insertions, 0 deletions
diff --git a/Xamarin.Forms.Platform.WinRT/ActivityIndicatorRenderer.cs b/Xamarin.Forms.Platform.WinRT/ActivityIndicatorRenderer.cs
new file mode 100644
index 00000000..5c36afda
--- /dev/null
+++ b/Xamarin.Forms.Platform.WinRT/ActivityIndicatorRenderer.cs
@@ -0,0 +1,68 @@
+using System.ComponentModel;
+using Windows.UI.Xaml;
+
+#if WINDOWS_UWP
+
+namespace Xamarin.Forms.Platform.UWP
+#else
+
+namespace Xamarin.Forms.Platform.WinRT
+#endif
+{
+ public class ActivityIndicatorRenderer : ViewRenderer<ActivityIndicator, Windows.UI.Xaml.Controls.ProgressBar>
+ {
+ object _foregroundDefault;
+
+ protected override void OnElementChanged(ElementChangedEventArgs<ActivityIndicator> e)
+ {
+ base.OnElementChanged(e);
+
+ if (e.NewElement != null)
+ {
+ if (Control == null)
+ {
+ SetNativeControl(new Windows.UI.Xaml.Controls.ProgressBar { IsIndeterminate = true });
+
+ Control.Loaded += OnControlLoaded;
+ }
+
+ // UpdateColor() called when loaded to ensure we can cache dynamic default colors
+ UpdateIsRunning();
+ }
+ }
+
+ protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ base.OnElementPropertyChanged(sender, e);
+
+ if (e.PropertyName == ActivityIndicator.IsRunningProperty.PropertyName)
+ UpdateIsRunning();
+ else if (e.PropertyName == ActivityIndicator.ColorProperty.PropertyName)
+ UpdateColor();
+ }
+
+ void OnControlLoaded(object sender, RoutedEventArgs routedEventArgs)
+ {
+ _foregroundDefault = Control.GetForegroundCache();
+ UpdateColor();
+ }
+
+ void UpdateColor()
+ {
+ Color color = Element.Color;
+ if (color.IsDefault)
+ {
+ Control.RestoreForegroundCache(_foregroundDefault);
+ }
+ else
+ {
+ Control.Foreground = color.ToBrush();
+ }
+ }
+
+ void UpdateIsRunning()
+ {
+ Opacity = Element.IsRunning ? 1 : 0;
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WinRT/AlignmentExtensions.cs b/Xamarin.Forms.Platform.WinRT/AlignmentExtensions.cs
new file mode 100644
index 00000000..aa6f926a
--- /dev/null
+++ b/Xamarin.Forms.Platform.WinRT/AlignmentExtensions.cs
@@ -0,0 +1,39 @@
+using Windows.UI.Xaml;
+
+#if WINDOWS_UWP
+
+namespace Xamarin.Forms.Platform.UWP
+#else
+
+namespace Xamarin.Forms.Platform.WinRT
+#endif
+{
+ internal static class AlignmentExtensions
+ {
+ internal static Windows.UI.Xaml.TextAlignment ToNativeTextAlignment(this TextAlignment alignment)
+ {
+ switch (alignment)
+ {
+ case TextAlignment.Center:
+ return Windows.UI.Xaml.TextAlignment.Center;
+ case TextAlignment.End:
+ return Windows.UI.Xaml.TextAlignment.Right;
+ default:
+ return Windows.UI.Xaml.TextAlignment.Left;
+ }
+ }
+
+ internal static VerticalAlignment ToNativeVerticalAlignment(this TextAlignment alignment)
+ {
+ switch (alignment)
+ {
+ case TextAlignment.Center:
+ return VerticalAlignment.Center;
+ case TextAlignment.End:
+ return VerticalAlignment.Bottom;
+ default:
+ return VerticalAlignment.Top;
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WinRT/AsyncValue.cs b/Xamarin.Forms.Platform.WinRT/AsyncValue.cs
new file mode 100644
index 00000000..4cfbb833
--- /dev/null
+++ b/Xamarin.Forms.Platform.WinRT/AsyncValue.cs
@@ -0,0 +1,95 @@
+//
+// AsyncValue.cs
+//
+// Author:
+// Eric Maupin <me@ermau.com>
+//
+// Copyright (c) 2013-2014 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.ComponentModel;
+using System.Runtime.CompilerServices;
+using System.Threading;
+using System.Threading.Tasks;
+
+#if WINDOWS_UWP
+
+namespace Xamarin.Forms.Platform.UWP
+#else
+
+namespace Xamarin.Forms.Platform.WinRT
+#endif
+{
+ internal sealed class AsyncValue<T> : INotifyPropertyChanged
+ {
+ readonly T _defaultValue;
+ readonly Task<T> _valueTask;
+ bool _isRunning = true;
+
+ public AsyncValue(Task<T> valueTask, T defaultValue)
+ {
+ if (valueTask == null)
+ throw new ArgumentNullException("valueTask");
+
+ _valueTask = valueTask;
+ _defaultValue = defaultValue;
+
+ TaskScheduler scheduler = TaskScheduler.FromCurrentSynchronizationContext();
+
+ _valueTask.ContinueWith(t => { IsRunning = false; }, scheduler);
+
+ _valueTask.ContinueWith(t => { OnPropertyChanged("Value"); }, CancellationToken.None, TaskContinuationOptions.OnlyOnRanToCompletion, scheduler);
+ }
+
+ public bool IsRunning
+ {
+ get { return _isRunning; }
+ set
+ {
+ if (_isRunning == value)
+ return;
+
+ _isRunning = value;
+ OnPropertyChanged();
+ }
+ }
+
+ public T Value
+ {
+ get
+ {
+ if (_valueTask.Status != TaskStatus.RanToCompletion)
+ return _defaultValue;
+
+ return _valueTask.Result;
+ }
+ }
+
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ 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.Platform.WinRT/BackgroundTracker.cs b/Xamarin.Forms.Platform.WinRT/BackgroundTracker.cs
new file mode 100644
index 00000000..c7474015
--- /dev/null
+++ b/Xamarin.Forms.Platform.WinRT/BackgroundTracker.cs
@@ -0,0 +1,82 @@
+using System;
+using System.ComponentModel;
+using Windows.UI.Xaml;
+using Windows.UI.Xaml.Media;
+using Windows.UI.Xaml.Media.Imaging;
+
+#if WINDOWS_UWP
+
+namespace Xamarin.Forms.Platform.UWP
+#else
+
+namespace Xamarin.Forms.Platform.WinRT
+#endif
+{
+ internal sealed class BackgroundTracker<T> : VisualElementTracker<Page, T> where T : FrameworkElement
+ {
+ readonly DependencyProperty _backgroundProperty;
+ bool _backgroundNeedsUpdate = true;
+
+ public BackgroundTracker(DependencyProperty backgroundProperty)
+ {
+ if (backgroundProperty == null)
+ throw new ArgumentNullException("backgroundProperty");
+
+ _backgroundProperty = backgroundProperty;
+ }
+
+ protected override void OnPropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ if (e.PropertyName == VisualElement.BackgroundColorProperty.PropertyName || e.PropertyName == Page.BackgroundImageProperty.PropertyName)
+ {
+ UpdateBackground();
+ }
+
+ base.OnPropertyChanged(sender, e);
+ }
+
+ protected override void UpdateNativeControl()
+ {
+ base.UpdateNativeControl();
+
+ if (_backgroundNeedsUpdate)
+ UpdateBackground();
+ }
+
+ void UpdateBackground()
+ {
+ if (Element == null)
+ return;
+
+ FrameworkElement element = Control ?? Container;
+ if (element == null)
+ return;
+
+ string backgroundImage = Element.BackgroundImage;
+ if (backgroundImage != null)
+ {
+ Uri uri;
+ if (!Uri.TryCreate(backgroundImage, UriKind.RelativeOrAbsolute, out uri) || !uri.IsAbsoluteUri)
+ uri = new Uri("ms-appx:///" + backgroundImage);
+
+ element.SetValue(_backgroundProperty, new ImageBrush { ImageSource = new BitmapImage(uri) });
+ }
+ else
+ {
+ Color backgroundColor = Element.BackgroundColor;
+ if (!backgroundColor.IsDefault)
+ {
+ element.SetValue(_backgroundProperty, backgroundColor.ToBrush());
+ }
+ else
+ {
+ object localBackground = element.ReadLocalValue(_backgroundProperty);
+ if (localBackground != null && localBackground != DependencyProperty.UnsetValue)
+ element.ClearValue(_backgroundProperty);
+ }
+ }
+
+ _backgroundNeedsUpdate = false;
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WinRT/BoolToVisibilityConverter.cs b/Xamarin.Forms.Platform.WinRT/BoolToVisibilityConverter.cs
new file mode 100644
index 00000000..357cba3f
--- /dev/null
+++ b/Xamarin.Forms.Platform.WinRT/BoolToVisibilityConverter.cs
@@ -0,0 +1,30 @@
+using System;
+using Windows.UI.Xaml;
+
+#if WINDOWS_UWP
+
+namespace Xamarin.Forms.Platform.UWP
+#else
+
+namespace Xamarin.Forms.Platform.WinRT
+#endif
+{
+ public sealed class BoolToVisibilityConverter : Windows.UI.Xaml.Data.IValueConverter
+ {
+ public bool FalseIsVisible { get; set; }
+
+ public object Convert(object value, Type targetType, object parameter, string language)
+ {
+ var v = (bool)value;
+ if (FalseIsVisible)
+ v = !v;
+
+ return v ? Visibility.Visible : Visibility.Collapsed;
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter, string language)
+ {
+ throw new NotImplementedException();
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WinRT/BoxViewRenderer.cs b/Xamarin.Forms.Platform.WinRT/BoxViewRenderer.cs
new file mode 100644
index 00000000..2e77c89d
--- /dev/null
+++ b/Xamarin.Forms.Platform.WinRT/BoxViewRenderer.cs
@@ -0,0 +1,31 @@
+using Windows.UI.Xaml;
+using Windows.UI.Xaml.Shapes;
+
+#if WINDOWS_UWP
+
+namespace Xamarin.Forms.Platform.UWP
+#else
+
+namespace Xamarin.Forms.Platform.WinRT
+#endif
+{
+ public class BoxViewRenderer : ViewRenderer<BoxView, Windows.UI.Xaml.Shapes.Rectangle>
+ {
+ protected override void OnElementChanged(ElementChangedEventArgs<BoxView> e)
+ {
+ base.OnElementChanged(e);
+
+ if (e.NewElement != null)
+ {
+ if (Control == null)
+ {
+ var rect = new Windows.UI.Xaml.Shapes.Rectangle();
+ rect.DataContext = Element;
+ rect.SetBinding(Shape.FillProperty, new Windows.UI.Xaml.Data.Binding { Converter = new ColorConverter(), Path = new PropertyPath("Color") });
+
+ SetNativeControl(rect);
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WinRT/ButtonRenderer.cs b/Xamarin.Forms.Platform.WinRT/ButtonRenderer.cs
new file mode 100644
index 00000000..60ee6418
--- /dev/null
+++ b/Xamarin.Forms.Platform.WinRT/ButtonRenderer.cs
@@ -0,0 +1,160 @@
+using System;
+using System.ComponentModel;
+using Windows.UI.Xaml;
+using Windows.UI.Xaml.Controls;
+using Windows.UI.Xaml.Media;
+using Windows.UI.Xaml.Media.Imaging;
+using WThickness = Windows.UI.Xaml.Thickness;
+using WButton = Windows.UI.Xaml.Controls.Button;
+using WImage = Windows.UI.Xaml.Controls.Image;
+
+#if WINDOWS_UWP
+
+namespace Xamarin.Forms.Platform.UWP
+#else
+
+namespace Xamarin.Forms.Platform.WinRT
+#endif
+{
+ public class ButtonRenderer : ViewRenderer<Button, FormsButton>
+ {
+ bool _fontApplied;
+
+ protected override void OnElementChanged(ElementChangedEventArgs<Button> e)
+ {
+ base.OnElementChanged(e);
+
+ if (e.NewElement != null)
+ {
+ if (Control == null)
+ {
+ var button = new FormsButton();
+ button.Click += OnButtonClick;
+ SetNativeControl(button);
+ }
+
+ UpdateContent();
+
+ if (Element.BackgroundColor != Color.Default)
+ UpdateBackground();
+
+ if (Element.TextColor != Color.Default)
+ UpdateTextColor();
+
+ if (Element.BorderColor != Color.Default)
+ UpdateBorderColor();
+
+ if (Element.BorderWidth != 0)
+ UpdateBorderWidth();
+
+ if (Element.BorderRadius != (int)Button.BorderRadiusProperty.DefaultValue)
+ UpdateBorderRadius();
+
+ UpdateFont();
+ }
+ }
+
+ protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ base.OnElementPropertyChanged(sender, e);
+
+ if (e.PropertyName == Button.TextProperty.PropertyName || e.PropertyName == Button.ImageProperty.PropertyName)
+ {
+ UpdateContent();
+ }
+ else if (e.PropertyName == VisualElement.BackgroundColorProperty.PropertyName)
+ {
+ UpdateBackground();
+ }
+ else if (e.PropertyName == Button.TextColorProperty.PropertyName)
+ {
+ UpdateTextColor();
+ }
+ else if (e.PropertyName == Button.FontProperty.PropertyName)
+ {
+ UpdateFont();
+ }
+ else if (e.PropertyName == Button.BorderColorProperty.PropertyName)
+ {
+ UpdateBorderColor();
+ }
+ else if (e.PropertyName == Button.BorderWidthProperty.PropertyName)
+ {
+ UpdateBorderWidth();
+ }
+ else if (e.PropertyName == Button.BorderRadiusProperty.PropertyName)
+ {
+ UpdateBorderRadius();
+ }
+ }
+
+ void OnButtonClick(object sender, RoutedEventArgs e)
+ {
+ Button buttonView = Element;
+ if (buttonView != null)
+ ((IButtonController)buttonView).SendClicked();
+ }
+
+ void UpdateBackground()
+ {
+ Control.Background = Element.BackgroundColor != Color.Default ? Element.BackgroundColor.ToBrush() : (Brush)Windows.UI.Xaml.Application.Current.Resources["ButtonBackgroundThemeBrush"];
+ }
+
+ void UpdateBorderColor()
+ {
+ Control.BorderBrush = Element.BorderColor != Color.Default ? Element.BorderColor.ToBrush() : (Brush)Windows.UI.Xaml.Application.Current.Resources["ButtonBorderThemeBrush"];
+ }
+
+ void UpdateBorderRadius()
+ {
+ Control.BorderRadius = Element.BorderRadius;
+ }
+
+ void UpdateBorderWidth()
+ {
+ Control.BorderThickness = Element.BorderWidth == 0d ? new WThickness(3) : new WThickness(Element.BorderWidth);
+ }
+
+ void UpdateContent()
+ {
+ if (Element.Image != null)
+ {
+ var panel = new StackPanel { Orientation = Orientation.Horizontal };
+
+ var image = new WImage { Source = new BitmapImage(new Uri("ms-appx:///" + Element.Image.File)), Width = 30, Height = 30, Margin = new WThickness(0, 0, 20, 0) };
+ panel.Children.Add(image);
+ image.ImageOpened += (sender, args) => { ((IButtonController)Element).NativeSizeChanged(); };
+
+ if (Element.Text != null)
+ {
+ panel.Children.Add(new TextBlock { Text = Element.Text });
+ }
+
+ Control.Content = panel;
+ }
+ else
+ {
+ Control.Content = Element.Text;
+ }
+ }
+
+ void UpdateFont()
+ {
+ if (Control == null || Element == null)
+ return;
+
+ if (Element.Font == Font.Default && !_fontApplied)
+ return;
+
+ Font fontToApply = Element.Font == Font.Default ? Font.SystemFontOfSize(NamedSize.Medium) : Element.Font;
+
+ Control.ApplyFont(fontToApply);
+ _fontApplied = true;
+ }
+
+ void UpdateTextColor()
+ {
+ Control.Foreground = Element.TextColor != Color.Default ? Element.TextColor.ToBrush() : (Brush)Windows.UI.Xaml.Application.Current.Resources["DefaultTextForegroundThemeBrush"];
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WinRT/CarouselPageRenderer.cs b/Xamarin.Forms.Platform.WinRT/CarouselPageRenderer.cs
new file mode 100644
index 00000000..8b5e52b5
--- /dev/null
+++ b/Xamarin.Forms.Platform.WinRT/CarouselPageRenderer.cs
@@ -0,0 +1,180 @@
+using System;
+using System.Collections.Specialized;
+using System.ComponentModel;
+using Windows.UI.Xaml;
+using Windows.UI.Xaml.Automation;
+using Windows.UI.Xaml.Controls;
+
+#if WINDOWS_UWP
+
+namespace Xamarin.Forms.Platform.UWP
+#else
+
+namespace Xamarin.Forms.Platform.WinRT
+#endif
+{
+ public class CarouselPageRenderer : FlipView, IVisualElementRenderer
+ {
+ bool _fromUpdate;
+ bool _disposed;
+
+ BackgroundTracker<FlipView> _tracker;
+
+ public CarouselPageRenderer()
+ {
+ VirtualizingStackPanel.SetVirtualizationMode(this, VirtualizationMode.Standard);
+ ItemTemplate = (Windows.UI.Xaml.DataTemplate)Windows.UI.Xaml.Application.Current.Resources["ContainedPageTemplate"];
+ SelectionChanged += OnSelectionChanged;
+ Loaded += OnLoaded;
+ Unloaded += OnUnloaded;
+ }
+
+ public CarouselPage Element { get; private set; }
+
+ public void Dispose()
+ {
+ Dispose(true);
+ }
+
+ public FrameworkElement ContainerElement
+ {
+ get { return this; }
+ }
+
+ VisualElement IVisualElementRenderer.Element
+ {
+ get { return Element; }
+ }
+
+ public event EventHandler<VisualElementChangedEventArgs> ElementChanged;
+
+ public SizeRequest GetDesiredSize(double widthConstraint, double heightConstraint)
+ {
+ var constraint = new Windows.Foundation.Size(widthConstraint, heightConstraint);
+
+ double oldWidth = Width;
+ double oldHeight = Height;
+
+ Height = double.NaN;
+ Width = double.NaN;
+
+ Measure(constraint);
+ var result = new Size(Math.Ceiling(DesiredSize.Width), Math.Ceiling(DesiredSize.Height));
+
+ Width = oldWidth;
+ Height = oldHeight;
+
+ return new SizeRequest(result);
+ }
+
+ public void SetElement(VisualElement element)
+ {
+ var newPage = element as CarouselPage;
+ if (element != null && newPage == null)
+ throw new ArgumentException("element must be a CarouselPage");
+
+ CarouselPage oldPage = Element;
+ Element = newPage;
+
+ if (oldPage != null)
+ {
+ oldPage.SendDisappearing();
+ ((INotifyCollectionChanged)oldPage.Children).CollectionChanged -= OnChildrenChanged;
+ oldPage.PropertyChanged -= OnElementPropertyChanged;
+ }
+
+ if (newPage != null)
+ {
+ if (_tracker == null)
+ {
+ _tracker = new BackgroundTracker<FlipView>(BackgroundProperty) { Control = this };
+ }
+
+ _tracker.Element = newPage;
+
+ for (var i = 0; i < newPage.Children.Count; i++)
+ Items.Add(newPage.Children[i]);
+
+ ((INotifyCollectionChanged)newPage.Children).CollectionChanged += OnChildrenChanged;
+ newPage.PropertyChanged += OnElementPropertyChanged;
+
+ UpdateCurrentPage();
+ newPage.SendAppearing();
+ }
+
+ OnElementChanged(new ElementChangedEventArgs<CarouselPage>(oldPage, newPage));
+
+ if (!string.IsNullOrEmpty(Element?.AutomationId))
+ SetValue(AutomationProperties.AutomationIdProperty, Element.AutomationId);
+ }
+
+ protected virtual void Dispose(bool disposing)
+ {
+ if (!disposing || _disposed)
+ return;
+
+ if (_tracker != null)
+ {
+ _tracker.Dispose();
+ _tracker = null;
+ }
+
+ _disposed = true;
+ Element?.SendDisappearing();
+ SetElement(null);
+ }
+
+ protected virtual void OnElementChanged(ElementChangedEventArgs<CarouselPage> e)
+ {
+ EventHandler<VisualElementChangedEventArgs> changed = ElementChanged;
+ if (changed != null)
+ changed(this, new VisualElementChangedEventArgs(e.OldElement, e.NewElement));
+ }
+
+ protected virtual void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ if (e.PropertyName == "CurrentPage")
+ {
+ UpdateCurrentPage();
+ }
+ }
+
+ void OnChildrenChanged(object sender, NotifyCollectionChangedEventArgs e)
+ {
+ e.Apply(Element.Children, Items);
+ }
+
+ void OnLoaded(object sender, RoutedEventArgs e)
+ {
+ Element?.SendAppearing();
+ }
+
+ void OnSelectionChanged(object sender, SelectionChangedEventArgs e)
+ {
+ if (_fromUpdate)
+ return;
+
+ var page = (ContentPage)SelectedItem;
+ ContentPage currentPage = Element.CurrentPage;
+ if (currentPage == page)
+ return;
+ currentPage?.SendDisappearing();
+ Element.CurrentPage = page;
+ page?.SendAppearing();
+ }
+
+ void OnUnloaded(object sender, RoutedEventArgs e)
+ {
+ Element?.SendDisappearing();
+ }
+
+ void UpdateCurrentPage()
+ {
+ _fromUpdate = true;
+
+ SelectedItem = Element.CurrentPage;
+
+ _fromUpdate = false;
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WinRT/CarouselViewRenderer.cs b/Xamarin.Forms.Platform.WinRT/CarouselViewRenderer.cs
new file mode 100644
index 00000000..3e1fd096
--- /dev/null
+++ b/Xamarin.Forms.Platform.WinRT/CarouselViewRenderer.cs
@@ -0,0 +1,220 @@
+using System;
+using System.Collections.Specialized;
+using System.ComponentModel;
+using System.Linq;
+using Windows.UI.Xaml;
+using Windows.UI.Xaml.Controls;
+using Windows.UI.Xaml.Media;
+using WFlipView = Windows.UI.Xaml.Controls.FlipView;
+using WBinding = Windows.UI.Xaml.Data.Binding;
+using WApp = Windows.UI.Xaml.Application;
+using WSize = Windows.Foundation.Size;
+using WDataTemplate = Windows.UI.Xaml.DataTemplate;
+
+#if WINDOWS_UWP
+
+namespace Xamarin.Forms.Platform.UWP
+#else
+
+namespace Xamarin.Forms.Platform.WinRT
+#endif
+{
+ public class CarouselViewRenderer : ViewRenderer<CarouselView, FrameworkElement>
+ {
+ WFlipView _flipView;
+
+ bool _leftAdd;
+
+ ICarouselViewController Controller
+ {
+ get { return Element; }
+ }
+
+ protected override void OnElementChanged(ElementChangedEventArgs<CarouselView> e)
+ {
+ base.OnElementChanged(e);
+
+ if (e.OldElement != null)
+ {
+ _flipView.SelectionChanged -= SelectionChanged;
+ _flipView.ItemsSource = null;
+ Element.CollectionChanged -= CollectionChanged;
+ }
+
+ if (e.NewElement != null)
+ {
+ if (_flipView == null)
+ {
+ _flipView = new FlipView { IsSynchronizedWithCurrentItem = false, ItemTemplate = (WDataTemplate)WApp.Current.Resources["ItemTemplate"] };
+ }
+
+ _flipView.ItemsSource = Element.ItemsSource;
+ _flipView.SelectedIndex = Element.Position;
+ _flipView.SelectionChanged += SelectionChanged;
+ Element.CollectionChanged += CollectionChanged;
+ }
+
+ if (_flipView != Control)
+ SetNativeControl(_flipView);
+ }
+
+ protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ if (e.PropertyName == "Position" && _flipView.SelectedIndex != Element.Position)
+ {
+ if (!_leftAdd)
+ _flipView.SelectedIndex = Element.Position;
+ _leftAdd = false;
+ }
+
+ base.OnElementPropertyChanged(sender, e);
+ }
+
+ void CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
+ {
+ Controller.SendSelectedPositionChanged(_flipView.SelectedIndex);
+
+ switch (e.Action)
+ {
+ case NotifyCollectionChangedAction.Add:
+ if (e.NewStartingIndex <= Element.Position)
+ {
+ _leftAdd = true;
+ int position = Element.Position + e.NewItems.Count;
+ PositionChanged(position);
+ }
+ break;
+
+ case NotifyCollectionChangedAction.Move:
+ break;
+
+ case NotifyCollectionChangedAction.Remove:
+ if (Element.Count == 0)
+ throw new InvalidOperationException("CarouselView must retain a least one item.");
+
+ if (e.OldStartingIndex < Element.Position)
+ PositionChanged(Element.Position - e.OldItems.Count);
+ break;
+
+ case NotifyCollectionChangedAction.Replace:
+ break;
+
+ case NotifyCollectionChangedAction.Reset:
+ break;
+
+ default:
+ throw new Exception($"Enum value '{(int)e.Action}' is not a member of NotifyCollectionChangedAction enumeration.");
+ }
+ }
+
+ void PositionChanged(int position)
+ {
+ if (!_leftAdd)
+ _flipView.SelectedIndex = position;
+ Element.Position = position;
+ Controller.SendSelectedPositionChanged(position);
+ }
+
+ void SelectionChanged(object sender, SelectionChangedEventArgs e)
+ {
+ object[] addedItems = e.AddedItems.ToArray();
+ object[] removedItems = e.RemovedItems.ToArray();
+
+ object addedItem = addedItems.SingleOrDefault();
+ if (addedItem != null)
+ {
+ PositionChanged(_flipView.SelectedIndex);
+ Controller.SendSelectedItemChanged(addedItems.Single());
+ }
+ }
+ }
+
+ public class ItemControl : ContentControl
+ {
+ CarouselView _carouselView;
+ object _item;
+ View _view;
+
+ public ItemControl()
+ {
+ DataContextChanged += OnDataContextChanged;
+ }
+
+ CarouselView CarouselView => LoadCarouselView();
+
+ IItemViewController Controller => CarouselView;
+
+ protected override WSize ArrangeOverride(WSize finalSize)
+ {
+ _view.Layout(new Rectangle(0, 0, CarouselView.Width, CarouselView.Height));
+ return base.ArrangeOverride(finalSize);
+ }
+
+ protected override WSize MeasureOverride(WSize availableSize)
+ {
+ LoadCarouselView();
+
+ if (_item != null)
+ {
+ SetDataContext(_item);
+ _item = null;
+ }
+
+ return base.MeasureOverride(availableSize);
+ }
+
+ CarouselView LoadCarouselView()
+ {
+ if (_carouselView != null)
+ return _carouselView;
+
+ DependencyObject parent = VisualTreeHelper.GetParent(this);
+ CarouselViewRenderer renderer = default(CarouselViewRenderer);
+
+ do
+ {
+ if (parent == null)
+ return null;
+
+ renderer = parent as CarouselViewRenderer;
+ if (renderer != null)
+ break;
+
+ parent = VisualTreeHelper.GetParent(parent);
+ } while (true);
+
+ _carouselView = renderer.Element;
+ return _carouselView;
+ }
+
+ void OnDataContextChanged(FrameworkElement sender, DataContextChangedEventArgs args)
+ {
+ object item = args.NewValue;
+
+ if (_carouselView != null)
+ SetDataContext(item);
+
+ else if (item != null)
+ _item = item;
+ }
+
+ void SetDataContext(object item)
+ {
+ // type item
+ object type = Controller.GetItemType(item);
+
+ // activate item
+ _view = Controller.CreateView(type);
+ _view.Parent = CarouselView;
+ _view.Layout(new Rectangle(0, 0, CarouselView.Width, CarouselView.Height));
+
+ // render item
+ IVisualElementRenderer renderer = Platform.CreateRenderer(_view);
+ Platform.SetRenderer(_view, renderer);
+ Content = renderer;
+
+ // bind item
+ Controller.BindView(_view, item);
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WinRT/CaseConverter.cs b/Xamarin.Forms.Platform.WinRT/CaseConverter.cs
new file mode 100644
index 00000000..7f8a9009
--- /dev/null
+++ b/Xamarin.Forms.Platform.WinRT/CaseConverter.cs
@@ -0,0 +1,29 @@
+using System;
+
+#if WINDOWS_UWP
+
+namespace Xamarin.Forms.Platform.UWP
+#else
+
+namespace Xamarin.Forms.Platform.WinRT
+#endif
+{
+ public class CaseConverter : Windows.UI.Xaml.Data.IValueConverter
+ {
+ public bool ConvertToUpper { get; set; }
+
+ public object Convert(object value, Type targetType, object parameter, string language)
+ {
+ if (value == null)
+ return null;
+
+ var v = (string)value;
+ return ConvertToUpper ? v.ToUpper() : v.ToLower();
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter, string language)
+ {
+ throw new NotSupportedException();
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WinRT/CellControl.cs b/Xamarin.Forms.Platform.WinRT/CellControl.cs
new file mode 100644
index 00000000..a47c9646
--- /dev/null
+++ b/Xamarin.Forms.Platform.WinRT/CellControl.cs
@@ -0,0 +1,342 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.Specialized;
+using System.ComponentModel;
+using Windows.UI.Input;
+using Windows.UI.Xaml;
+using Windows.UI.Xaml.Controls;
+using Windows.UI.Xaml.Controls.Primitives;
+using Windows.UI.Xaml.Input;
+using Windows.UI.Xaml.Media;
+
+#if WINDOWS_UWP
+
+namespace Xamarin.Forms.Platform.UWP
+#else
+
+namespace Xamarin.Forms.Platform.WinRT
+#endif
+{
+ public class CellControl : ContentControl
+ {
+ public static readonly DependencyProperty CellProperty = DependencyProperty.Register("Cell", typeof(object), typeof(CellControl),
+ new PropertyMetadata(null, (o, e) => ((CellControl)o).SetSource((Cell)e.OldValue, (Cell)e.NewValue)));
+
+ public static readonly DependencyProperty IsGroupHeaderProperty = DependencyProperty.Register("IsGroupHeader", typeof(bool), typeof(CellControl), null);
+
+ internal static readonly BindableProperty MeasuredEstimateProperty = BindableProperty.Create("MeasuredEstimate", typeof(double), typeof(ListView), -1d);
+ readonly Lazy<ListView> _listView;
+ readonly PropertyChangedEventHandler _propertyChangedHandler;
+
+ IList<MenuItem> _contextActions;
+ Windows.UI.Xaml.DataTemplate _currentTemplate;
+ bool _isListViewRealized;
+ object _newValue;
+
+ public CellControl()
+ {
+ _listView = new Lazy<ListView>(GetListView);
+
+ DataContextChanged += OnDataContextChanged;
+
+ Unloaded += (sender, args) =>
+ {
+ Cell cell = Cell;
+ if (cell != null)
+ cell.SendDisappearing();
+ };
+
+ _propertyChangedHandler = OnCellPropertyChanged;
+ }
+
+ public Cell Cell
+ {
+ get { return (Cell)GetValue(CellProperty); }
+ set { SetValue(CellProperty, value); }
+ }
+
+ public bool IsGroupHeader
+ {
+ get { return (bool)GetValue(IsGroupHeaderProperty); }
+ set { SetValue(IsGroupHeaderProperty, value); }
+ }
+
+ protected FrameworkElement CellContent
+ {
+ get { return (FrameworkElement)Content; }
+ }
+
+ protected override Windows.Foundation.Size MeasureOverride(Windows.Foundation.Size availableSize)
+ {
+ ListView lv = _listView.Value;
+
+ // set the Cell now that we have a reference to the ListView, since it will have been skipped
+ // on DataContextChanged.
+ if (_newValue != null)
+ {
+ SetCell(_newValue);
+ _newValue = null;
+ }
+
+ if (Content == null)
+ {
+ if (lv != null)
+ {
+ if (lv.HasUnevenRows)
+ {
+ var estimate = (double)lv.GetValue(MeasuredEstimateProperty);
+ if (estimate > -1)
+ return new Windows.Foundation.Size(availableSize.Width, estimate);
+ }
+ else
+ {
+ double rowHeight = lv.RowHeight;
+ if (rowHeight > -1)
+ return new Windows.Foundation.Size(availableSize.Width, rowHeight);
+ }
+ }
+
+ return new Windows.Foundation.Size(0, 0);
+ }
+
+ // Children still need measure called on them
+ Windows.Foundation.Size result = base.MeasureOverride(availableSize);
+
+ if (lv != null)
+ {
+ lv.SetValue(MeasuredEstimateProperty, result.Height);
+ }
+
+ return result;
+ }
+
+ static string GetDisplayTextFromGroup(ListView lv, TemplatedItemsList<ItemsView<Cell>, Cell> group)
+ {
+ string displayBinding = null;
+
+ if (lv.GroupDisplayBinding != null)
+ displayBinding = group.Name;
+
+ if (lv.GroupShortNameBinding != null)
+ displayBinding = group.ShortName;
+
+ // TODO: what if they set both? should it default to the ShortName, like it will here?
+ // ShortNames binding did not appear to be functional before.
+ return displayBinding;
+ }
+
+ static TemplatedItemsList<ItemsView<Cell>, Cell> GetGroup(object newContext, ListView lv)
+ {
+ int groupIndex = lv.TemplatedItems.GetGlobalIndexOfGroup(newContext);
+ TemplatedItemsList<ItemsView<Cell>, Cell> group = lv.TemplatedItems.GetGroup(groupIndex);
+ return group;
+ }
+
+ ListView GetListView()
+ {
+ DependencyObject parent = VisualTreeHelper.GetParent(this);
+ while (parent != null)
+ {
+ var lv = parent as ListViewRenderer;
+ if (lv != null)
+ {
+ _isListViewRealized = true;
+ return lv.Element;
+ }
+
+ parent = VisualTreeHelper.GetParent(parent);
+ }
+
+ return null;
+ }
+
+ Windows.UI.Xaml.DataTemplate GetTemplate(Cell cell)
+ {
+ var renderer = Registrar.Registered.GetHandler<ICellRenderer>(cell.GetType());
+ return renderer.GetTemplate(cell);
+ }
+
+ void OnCellPropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ if (e.PropertyName == "HasContextActions")
+ {
+ SetupContextMenu();
+ }
+ }
+
+ void OnClick(object sender, PointerRoutedEventArgs e)
+ {
+ PointerPoint point = e.GetCurrentPoint(CellContent);
+ if (point.Properties.PointerUpdateKind != PointerUpdateKind.RightButtonReleased)
+ return;
+
+ OpenContextMenu();
+ }
+
+ void OnContextActionsChanged(object sender, NotifyCollectionChangedEventArgs e)
+ {
+ var flyout = FlyoutBase.GetAttachedFlyout(CellContent) as MenuFlyout;
+ if (flyout != null)
+ {
+ flyout.Items.Clear();
+ SetupMenuItems(flyout);
+ }
+ }
+
+ void OnDataContextChanged(FrameworkElement sender, DataContextChangedEventArgs args)
+ {
+ // We don't want to set the Cell until the ListView is realized, just in case the
+ // Cell has an ItemTemplate. Instead, we'll store the new data item, and it will be
+ // set on MeasureOverrideDelegate. However, if the parent is a TableView, we'll already
+ // have a complete Cell object to work with, so we can move ahead.
+ if (_isListViewRealized || args.NewValue is Cell)
+ SetCell(args.NewValue);
+ else if (args.NewValue != null)
+ _newValue = args.NewValue;
+ }
+
+ void OnLongTap(object sender, HoldingRoutedEventArgs e)
+ {
+ if (e.HoldingState == HoldingState.Started)
+ OpenContextMenu();
+ }
+
+ void OnOpenContext(object sender, RightTappedRoutedEventArgs e)
+ {
+ FlyoutBase.ShowAttachedFlyout(CellContent);
+ }
+
+ void OpenContextMenu()
+ {
+ if (FlyoutBase.GetAttachedFlyout(CellContent) == null)
+ {
+ var flyout = new MenuFlyout();
+ SetupMenuItems(flyout);
+
+ ((INotifyCollectionChanged)Cell.ContextActions).CollectionChanged += OnContextActionsChanged;
+
+ _contextActions = Cell.ContextActions;
+ FlyoutBase.SetAttachedFlyout(CellContent, flyout);
+ }
+
+ FlyoutBase.ShowAttachedFlyout(CellContent);
+ }
+
+ void SetCell(object newContext)
+ {
+ var cell = newContext as Cell;
+
+ if (ReferenceEquals(Cell?.BindingContext, newContext))
+ return;
+
+ // If there is a ListView, load the Cell content from the ItemTemplate.
+ // Otherwise, the given Cell is already a templated Cell from a TableView.
+ ListView lv = _listView.Value;
+ if (lv != null)
+ {
+ bool isGroupHeader = IsGroupHeader;
+ DataTemplate template = isGroupHeader ? lv.GroupHeaderTemplate : lv.ItemTemplate;
+
+ if (template is DataTemplateSelector)
+ {
+ template = ((DataTemplateSelector)template).SelectTemplate(newContext, lv);
+ }
+
+ if (template != null)
+ {
+ cell = template.CreateContent() as Cell;
+ cell.BindingContext = newContext;
+ }
+ else
+ {
+ string textContent = newContext.ToString();
+
+ if (isGroupHeader)
+ {
+ TemplatedItemsList<ItemsView<Cell>, Cell> group = GetGroup(newContext, lv);
+ textContent = GetDisplayTextFromGroup(lv, group);
+ }
+
+ cell = lv.CreateDefaultCell(textContent);
+ }
+
+ // A TableView cell should already have its parent,
+ // but we need to set the parent for a ListView cell.
+ cell.Parent = lv;
+
+ // This provides the Group Header styling (e.g., larger font, etc.) when the
+ // template is loaded later.
+ TemplatedItemsList<ItemsView<Cell>, Cell>.SetIsGroupHeader(cell, isGroupHeader);
+ }
+
+ Cell = cell;
+ }
+
+ void SetSource(Cell oldCell, Cell newCell)
+ {
+ if (oldCell != null)
+ {
+ oldCell.PropertyChanged -= _propertyChangedHandler;
+ oldCell.SendDisappearing();
+ }
+
+ if (newCell != null)
+ {
+ newCell.SendAppearing();
+
+ UpdateContent(newCell);
+ SetupContextMenu();
+
+ newCell.PropertyChanged += _propertyChangedHandler;
+ }
+ }
+
+ void SetupContextMenu()
+ {
+ if (CellContent == null || Cell == null)
+ return;
+
+ if (!Cell.HasContextActions)
+ {
+ CellContent.Holding -= OnLongTap;
+ CellContent.PointerReleased -= OnClick;
+ if (_contextActions != null)
+ {
+ ((INotifyCollectionChanged)_contextActions).CollectionChanged -= OnContextActionsChanged;
+ _contextActions = null;
+ }
+
+ FlyoutBase.SetAttachedFlyout(CellContent, null);
+ return;
+ }
+
+ CellContent.PointerReleased += OnClick;
+ CellContent.Holding += OnLongTap;
+ }
+
+ void SetupMenuItems(MenuFlyout flyout)
+ {
+ foreach (MenuItem item in Cell.ContextActions)
+ {
+ var flyoutItem = new MenuFlyoutItem();
+ flyoutItem.SetBinding(MenuFlyoutItem.TextProperty, "Text");
+ flyoutItem.Command = new MenuItemCommand(item);
+ flyoutItem.DataContext = item;
+
+ flyout.Items.Add(flyoutItem);
+ }
+ }
+
+ void UpdateContent(Cell newCell)
+ {
+ Windows.UI.Xaml.DataTemplate dt = GetTemplate(newCell);
+ if (dt != _currentTemplate || Content == null)
+ {
+ _currentTemplate = dt;
+ Content = dt.LoadContent();
+ }
+
+ ((FrameworkElement)Content).DataContext = newCell;
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WinRT/CollapseWhenEmptyConverter.cs b/Xamarin.Forms.Platform.WinRT/CollapseWhenEmptyConverter.cs
new file mode 100644
index 00000000..311d315b
--- /dev/null
+++ b/Xamarin.Forms.Platform.WinRT/CollapseWhenEmptyConverter.cs
@@ -0,0 +1,33 @@
+using System;
+using Windows.UI.Xaml;
+
+#if WINDOWS_UWP
+
+namespace Xamarin.Forms.Platform.UWP
+#else
+
+namespace Xamarin.Forms.Platform.WinRT
+#endif
+{
+ public class CollapseWhenEmptyConverter : Windows.UI.Xaml.Data.IValueConverter
+ {
+ public object Convert(object value, Type targetType, object parameter, string language)
+ {
+ var length = 0;
+
+ var s = value as string;
+ if (s != null)
+ length = s.Length;
+
+ if (value is int)
+ length = (int)value;
+
+ return length > 0 ? Visibility.Visible : Visibility.Collapsed;
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter, string language)
+ {
+ throw new NotSupportedException();
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WinRT/ColorConverter.cs b/Xamarin.Forms.Platform.WinRT/ColorConverter.cs
new file mode 100644
index 00000000..3866e435
--- /dev/null
+++ b/Xamarin.Forms.Platform.WinRT/ColorConverter.cs
@@ -0,0 +1,30 @@
+using System;
+using Windows.UI;
+using Windows.UI.Xaml.Media;
+
+#if WINDOWS_UWP
+
+namespace Xamarin.Forms.Platform.UWP
+#else
+
+namespace Xamarin.Forms.Platform.WinRT
+#endif
+{
+ public sealed class ColorConverter : Windows.UI.Xaml.Data.IValueConverter
+ {
+ public object Convert(object value, Type targetType, object parameter, string language)
+ {
+ var color = (Color)value;
+ var defaultColorKey = (string)parameter;
+
+ Brush defaultBrush = defaultColorKey != null ? (Brush)Windows.UI.Xaml.Application.Current.Resources[defaultColorKey] : new SolidColorBrush(Colors.Transparent);
+
+ return color == Color.Default ? defaultBrush : color.ToBrush();
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter, string language)
+ {
+ throw new NotImplementedException();
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WinRT/ConvertExtensions.cs b/Xamarin.Forms.Platform.WinRT/ConvertExtensions.cs
new file mode 100644
index 00000000..961c0422
--- /dev/null
+++ b/Xamarin.Forms.Platform.WinRT/ConvertExtensions.cs
@@ -0,0 +1,23 @@
+using Windows.UI.Xaml.Media;
+
+#if WINDOWS_UWP
+
+namespace Xamarin.Forms.Platform.UWP
+#else
+
+namespace Xamarin.Forms.Platform.WinRT
+#endif
+{
+ internal static class ConvertExtensions
+ {
+ public static Brush ToBrush(this Color color)
+ {
+ return new SolidColorBrush(color.ToWindowsColor());
+ }
+
+ public static Windows.UI.Color ToWindowsColor(this Color color)
+ {
+ return Windows.UI.Color.FromArgb((byte)(color.A * 255), (byte)(color.R * 255), (byte)(color.G * 255), (byte)(color.B * 255));
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WinRT/DatePickerRenderer.cs b/Xamarin.Forms.Platform.WinRT/DatePickerRenderer.cs
new file mode 100644
index 00000000..23f134f2
--- /dev/null
+++ b/Xamarin.Forms.Platform.WinRT/DatePickerRenderer.cs
@@ -0,0 +1,97 @@
+using System;
+using System.ComponentModel;
+using Windows.UI.Xaml.Controls;
+
+#if WINDOWS_UWP
+
+namespace Xamarin.Forms.Platform.UWP
+#else
+
+namespace Xamarin.Forms.Platform.WinRT
+#endif
+{
+ public class DatePickerRenderer : ViewRenderer<DatePicker, FormsDatePicker>, IWrapperAware
+ {
+ public void NotifyWrapped()
+ {
+ if (Control != null)
+ {
+ Control.ForceInvalidate += PickerOnForceInvalidate;
+ }
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && Control != null)
+ {
+ Control.ForceInvalidate -= PickerOnForceInvalidate;
+ Control.DateChanged -= OnControlDateChanged;
+ }
+
+ base.Dispose(disposing);
+ }
+
+ protected override void OnElementChanged(ElementChangedEventArgs<DatePicker> e)
+ {
+ if (e.NewElement != null)
+ {
+ if (Control == null)
+ {
+ var picker = new FormsDatePicker();
+ picker.DateChanged += OnControlDateChanged;
+ SetNativeControl(picker);
+ }
+
+ UpdateMinimumDate();
+ UpdateMaximumDate();
+ UpdateDate(e.NewElement.Date);
+ }
+
+ base.OnElementChanged(e);
+ }
+
+ protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ base.OnElementPropertyChanged(sender, e);
+
+ if (e.PropertyName == DatePicker.DateProperty.PropertyName)
+ UpdateDate(Element.Date);
+ else if (e.PropertyName == DatePicker.MaximumDateProperty.PropertyName)
+ UpdateMaximumDate();
+ else if (e.PropertyName == DatePicker.MinimumDateProperty.PropertyName)
+ UpdateMinimumDate();
+ }
+
+ void OnControlDateChanged(object sender, DatePickerValueChangedEventArgs e)
+ {
+ Element.Date = e.NewDate.Date;
+ DateTime currentDate = Element.Date;
+ if (currentDate != e.NewDate.Date) // Match coerced value
+ UpdateDate(currentDate);
+
+ Element.InvalidateMeasure(InvalidationTrigger.SizeRequestChanged);
+ }
+
+ void PickerOnForceInvalidate(object sender, EventArgs eventArgs)
+ {
+ Element?.InvalidateMeasure(InvalidationTrigger.SizeRequestChanged);
+ }
+
+ void UpdateDate(DateTime date)
+ {
+ Control.Date = date;
+ }
+
+ void UpdateMaximumDate()
+ {
+ DateTime maxdate = Element.MaximumDate;
+ Control.MaxYear = new DateTimeOffset(maxdate.Date);
+ }
+
+ void UpdateMinimumDate()
+ {
+ DateTime mindate = Element.MinimumDate;
+ Control.MinYear = new DateTimeOffset(mindate);
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WinRT/DefaultRenderer.cs b/Xamarin.Forms.Platform.WinRT/DefaultRenderer.cs
new file mode 100644
index 00000000..8f08dfa8
--- /dev/null
+++ b/Xamarin.Forms.Platform.WinRT/DefaultRenderer.cs
@@ -0,0 +1,14 @@
+using Windows.UI.Xaml;
+
+#if WINDOWS_UWP
+
+namespace Xamarin.Forms.Platform.UWP
+#else
+
+namespace Xamarin.Forms.Platform.WinRT
+#endif
+{
+ internal sealed class DefaultRenderer : ViewRenderer<View, FrameworkElement>
+ {
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WinRT/EditorRenderer.cs b/Xamarin.Forms.Platform.WinRT/EditorRenderer.cs
new file mode 100644
index 00000000..d6236871
--- /dev/null
+++ b/Xamarin.Forms.Platform.WinRT/EditorRenderer.cs
@@ -0,0 +1,155 @@
+using System.ComponentModel;
+using Windows.UI.Xaml;
+using Windows.UI.Xaml.Controls;
+
+#if WINDOWS_UWP
+
+namespace Xamarin.Forms.Platform.UWP
+#else
+
+namespace Xamarin.Forms.Platform.WinRT
+#endif
+{
+ public class EditorRenderer : ViewRenderer<Editor, TextBox>
+ {
+ bool _fontApplied;
+
+ protected override void OnElementChanged(ElementChangedEventArgs<Editor> e)
+ {
+ if (e.NewElement != null)
+ {
+ if (Control == null)
+ {
+ var textBox = new TextBox { AcceptsReturn = true, TextWrapping = TextWrapping.Wrap };
+
+ textBox.TextChanged += OnNativeTextChanged;
+ textBox.LostFocus += OnLostFocus;
+
+ SetNativeControl(textBox);
+ }
+
+ UpdateText();
+ UpdateInputScope();
+ UpdateTextColor();
+ UpdateFont();
+ }
+
+ base.OnElementChanged(e);
+ }
+
+ protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ if (e.PropertyName == Editor.TextColorProperty.PropertyName)
+ {
+ UpdateTextColor();
+ }
+ else if (e.PropertyName == Editor.FontAttributesProperty.PropertyName)
+ {
+ UpdateFont();
+ }
+ else if (e.PropertyName == Editor.FontFamilyProperty.PropertyName)
+ {
+ UpdateFont();
+ }
+ else if (e.PropertyName == Editor.FontSizeProperty.PropertyName)
+ {
+ UpdateFont();
+ }
+ else if (e.PropertyName == Editor.TextProperty.PropertyName)
+ {
+ UpdateText();
+ }
+
+ base.OnElementPropertyChanged(sender, e);
+ }
+
+ void OnLostFocus(object sender, RoutedEventArgs e)
+ {
+ Element?.SendCompleted();
+ }
+
+ void OnNativeTextChanged(object sender, Windows.UI.Xaml.Controls.TextChangedEventArgs args)
+ {
+ Element?.SetValueCore(Editor.TextProperty, Control.Text);
+ }
+
+ void UpdateFont()
+ {
+ if (Control == null)
+ return;
+
+ Editor editor = Element;
+
+ if (editor == null)
+ return;
+
+ bool editorIsDefault = editor.FontFamily == null && editor.FontSize == Device.GetNamedSize(NamedSize.Default, typeof(Editor), true) && editor.FontAttributes == FontAttributes.None;
+
+ if (editorIsDefault && !_fontApplied)
+ return;
+
+ if (editorIsDefault)
+ {
+ // ReSharper disable AccessToStaticMemberViaDerivedType
+ // Resharper wants to simplify 'TextBox' to 'Control', but then it'll conflict with the property 'Control'
+ Control.ClearValue(TextBox.FontStyleProperty);
+ Control.ClearValue(TextBox.FontSizeProperty);
+ Control.ClearValue(TextBox.FontFamilyProperty);
+ Control.ClearValue(TextBox.FontWeightProperty);
+ Control.ClearValue(TextBox.FontStretchProperty);
+ // ReSharper restore AccessToStaticMemberViaDerivedType
+ }
+ else
+ {
+ Control.ApplyFont(editor);
+ }
+
+ _fontApplied = true;
+ }
+
+ void UpdateInputScope()
+ {
+ var custom = Element.Keyboard as CustomKeyboard;
+ if (custom != null)
+ {
+ Control.IsTextPredictionEnabled = (custom.Flags & KeyboardFlags.Suggestions) != 0;
+ Control.IsSpellCheckEnabled = (custom.Flags & KeyboardFlags.Spellcheck) != 0;
+ }
+ else
+ {
+ Control.ClearValue(TextBox.IsTextPredictionEnabledProperty);
+ Control.ClearValue(TextBox.IsSpellCheckEnabledProperty);
+ }
+
+ Control.InputScope = Element.Keyboard.ToInputScope();
+ }
+
+ void UpdateText()
+ {
+ string newText = Element.Text ?? "";
+
+ if (Control.Text == newText)
+ {
+ return;
+ }
+
+ Control.Text = newText;
+ Control.SelectionStart = Control.Text.Length;
+ }
+
+ void UpdateTextColor()
+ {
+ Color textColor = Element.TextColor;
+
+ if (textColor.IsDefault || !Element.IsEnabled)
+ {
+ // ReSharper disable once AccessToStaticMemberViaDerivedType
+ Control.ClearValue(TextBox.ForegroundProperty);
+ }
+ else
+ {
+ Control.Foreground = textColor.ToBrush();
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WinRT/EntryCellTextBox.cs b/Xamarin.Forms.Platform.WinRT/EntryCellTextBox.cs
new file mode 100644
index 00000000..e280e18a
--- /dev/null
+++ b/Xamarin.Forms.Platform.WinRT/EntryCellTextBox.cs
@@ -0,0 +1,30 @@
+using Windows.System;
+using Windows.UI.Xaml.Controls;
+using Windows.UI.Xaml.Input;
+
+#if WINDOWS_UWP
+
+namespace Xamarin.Forms.Platform.UWP
+#else
+
+namespace Xamarin.Forms.Platform.WinRT
+#endif
+{
+ public class EntryCellTextBox : TextBox
+ {
+ protected override void OnKeyUp(KeyRoutedEventArgs e)
+ {
+ if (e.Key == VirtualKey.Enter)
+ {
+ var cell = DataContext as EntryCell;
+ if (cell != null)
+ {
+ cell.SendCompleted();
+ e.Handled = true;
+ }
+ }
+
+ base.OnKeyUp(e);
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WinRT/EntryRenderer.cs b/Xamarin.Forms.Platform.WinRT/EntryRenderer.cs
new file mode 100644
index 00000000..f02abe3c
--- /dev/null
+++ b/Xamarin.Forms.Platform.WinRT/EntryRenderer.cs
@@ -0,0 +1,212 @@
+using System.ComponentModel;
+using Windows.System;
+using Windows.UI.Xaml.Input;
+using Windows.UI.Xaml.Media;
+
+#if WINDOWS_UWP
+
+namespace Xamarin.Forms.Platform.UWP
+#else
+
+namespace Xamarin.Forms.Platform.WinRT
+#endif
+{
+ public class EntryRenderer : ViewRenderer<Entry, FormsTextBox>
+ {
+ Brush _backgroundColorFocusedDefaultBrush;
+
+ bool _fontApplied;
+ Brush _placeholderDefaultBrush;
+
+ protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
+ {
+ base.OnElementChanged(e);
+
+ if (e.NewElement != null)
+ {
+ if (Control == null)
+ {
+ var textBox = new FormsTextBox { Style = Windows.UI.Xaml.Application.Current.Resources["FormsTextBoxStyle"] as Windows.UI.Xaml.Style };
+
+ textBox.TextChanged += OnNativeTextChanged;
+ textBox.KeyUp += TextBoxOnKeyUp;
+ SetNativeControl(textBox);
+ }
+
+ UpdateIsPassword();
+ UpdateText();
+ UpdatePlaceholder();
+ UpdateTextColor();
+ UpdateFont();
+ UpdateInputScope();
+ UpdateAlignment();
+ UpdatePlaceholderColor();
+ }
+ }
+
+ protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ base.OnElementPropertyChanged(sender, e);
+
+ if (e.PropertyName == Entry.TextProperty.PropertyName)
+ UpdateText();
+ else if (e.PropertyName == Entry.IsPasswordProperty.PropertyName)
+ UpdateIsPassword();
+ else if (e.PropertyName == Entry.PlaceholderProperty.PropertyName)
+ UpdatePlaceholder();
+ else if (e.PropertyName == Entry.TextColorProperty.PropertyName)
+ UpdateTextColor();
+ else if (e.PropertyName == InputView.KeyboardProperty.PropertyName)
+ UpdateInputScope();
+ else if (e.PropertyName == Entry.FontAttributesProperty.PropertyName)
+ UpdateFont();
+ else if (e.PropertyName == Entry.FontFamilyProperty.PropertyName)
+ UpdateFont();
+ else if (e.PropertyName == Entry.FontSizeProperty.PropertyName)
+ UpdateFont();
+ else if (e.PropertyName == Entry.HorizontalTextAlignmentProperty.PropertyName)
+ UpdateAlignment();
+ else if (e.PropertyName == Entry.PlaceholderColorProperty.PropertyName)
+ UpdatePlaceholderColor();
+ }
+
+ protected override void UpdateBackgroundColor()
+ {
+ base.UpdateBackgroundColor();
+
+ if (Control == null)
+ {
+ return;
+ }
+
+ // By default some platforms have alternate default background colors when focused
+ Color backgroundColor = Element.BackgroundColor;
+ if (backgroundColor.IsDefault)
+ {
+ if (_backgroundColorFocusedDefaultBrush == null)
+ {
+ return;
+ }
+
+ Control.BackgroundFocusBrush = _backgroundColorFocusedDefaultBrush;
+ return;
+ }
+
+ if (_backgroundColorFocusedDefaultBrush == null)
+ {
+ _backgroundColorFocusedDefaultBrush = Control.BackgroundFocusBrush;
+ }
+
+ Control.BackgroundFocusBrush = backgroundColor.ToBrush();
+ }
+
+ void OnNativeTextChanged(object sender, Windows.UI.Xaml.Controls.TextChangedEventArgs args)
+ {
+ Element.SetValueCore(Entry.TextProperty, Control.Text);
+ }
+
+ void TextBoxOnKeyUp(object sender, KeyRoutedEventArgs args)
+ {
+ if (args.Key != VirtualKey.Enter)
+ return;
+
+ Element.SendCompleted();
+ }
+
+ void UpdateAlignment()
+ {
+ Control.TextAlignment = Element.HorizontalTextAlignment.ToNativeTextAlignment();
+ }
+
+ void UpdateFont()
+ {
+ if (Control == null)
+ return;
+
+ Entry entry = Element;
+
+ if (entry == null)
+ return;
+
+ bool entryIsDefault = entry.FontFamily == null && entry.FontSize == Device.GetNamedSize(NamedSize.Default, typeof(Entry), true) && entry.FontAttributes == FontAttributes.None;
+
+ if (entryIsDefault && !_fontApplied)
+ return;
+
+ if (entryIsDefault)
+ {
+ // ReSharper disable AccessToStaticMemberViaDerivedType
+ // Resharper wants to simplify 'FormsTextBox' to 'Control', but then it'll conflict with the property 'Control'
+ Control.ClearValue(FormsTextBox.FontStyleProperty);
+ Control.ClearValue(FormsTextBox.FontSizeProperty);
+ Control.ClearValue(FormsTextBox.FontFamilyProperty);
+ Control.ClearValue(FormsTextBox.FontWeightProperty);
+ Control.ClearValue(FormsTextBox.FontStretchProperty);
+ // ReSharper restore AccessToStaticMemberViaDerivedType
+ }
+ else
+ {
+ Control.ApplyFont(entry);
+ }
+
+ _fontApplied = true;
+ }
+
+ void UpdateInputScope()
+ {
+ var custom = Element.Keyboard as CustomKeyboard;
+ if (custom != null)
+ {
+ Control.IsTextPredictionEnabled = (custom.Flags & KeyboardFlags.Suggestions) != 0;
+ Control.IsSpellCheckEnabled = (custom.Flags & KeyboardFlags.Spellcheck) != 0;
+ }
+
+ Control.InputScope = Element.Keyboard.ToInputScope();
+ }
+
+ void UpdateIsPassword()
+ {
+ Control.IsPassword = Element.IsPassword;
+ }
+
+ void UpdatePlaceholder()
+ {
+ Control.PlaceholderText = Element.Placeholder ?? "";
+ }
+
+ void UpdatePlaceholderColor()
+ {
+ Color placeholderColor = Element.PlaceholderColor;
+
+ if (placeholderColor.IsDefault)
+ {
+ if (_placeholderDefaultBrush == null)
+ {
+ return;
+ }
+
+ // Use the cached default brush
+ Control.PlaceholderForegroundBrush = _placeholderDefaultBrush;
+ return;
+ }
+
+ if (_placeholderDefaultBrush == null)
+ {
+ // Cache the default brush in case we need to set the color back to default
+ _placeholderDefaultBrush = Control.PlaceholderForegroundBrush;
+ }
+
+ Control.PlaceholderForegroundBrush = placeholderColor.ToBrush();
+ }
+
+ void UpdateText()
+ {
+ Control.Text = Element.Text ?? "";
+ }
+
+ void UpdateTextColor()
+ {
+ Control.Foreground = Element.TextColor.ToBrush();
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WinRT/ExportRendererAttribute.cs b/Xamarin.Forms.Platform.WinRT/ExportRendererAttribute.cs
new file mode 100644
index 00000000..650ec0de
--- /dev/null
+++ b/Xamarin.Forms.Platform.WinRT/ExportRendererAttribute.cs
@@ -0,0 +1,34 @@
+using System;
+
+#if WINDOWS_UWP
+
+namespace Xamarin.Forms.Platform.UWP
+#else
+
+namespace Xamarin.Forms.Platform.WinRT
+#endif
+{
+ [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
+ public sealed class ExportRendererAttribute : HandlerAttribute
+ {
+ public ExportRendererAttribute(Type handler, Type target) : base(handler, target)
+ {
+ }
+ }
+
+ [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
+ public sealed class ExportCellAttribute : HandlerAttribute
+ {
+ public ExportCellAttribute(Type handler, Type target) : base(handler, target)
+ {
+ }
+ }
+
+ [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
+ public sealed class ExportImageSourceHandlerAttribute : HandlerAttribute
+ {
+ public ExportImageSourceHandlerAttribute(Type handler, Type target) : base(handler, target)
+ {
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WinRT/Extensions.cs b/Xamarin.Forms.Platform.WinRT/Extensions.cs
new file mode 100644
index 00000000..3d4431f1
--- /dev/null
+++ b/Xamarin.Forms.Platform.WinRT/Extensions.cs
@@ -0,0 +1,46 @@
+using System;
+using System.Runtime.CompilerServices;
+using Windows.Foundation;
+using Windows.UI;
+using Windows.UI.Xaml;
+
+#if WINDOWS_UWP
+
+namespace Xamarin.Forms.Platform.UWP
+#else
+
+namespace Xamarin.Forms.Platform.WinRT
+#endif
+{
+ internal static class Extensions
+ {
+ public static ConfiguredTaskAwaitable<T> DontSync<T>(this IAsyncOperation<T> self)
+ {
+ return self.AsTask().ConfigureAwait(false);
+ }
+
+ public static ConfiguredTaskAwaitable DontSync(this IAsyncAction self)
+ {
+ return self.AsTask().ConfigureAwait(false);
+ }
+
+ public static Windows.UI.Color GetIdealForegroundForBackgroundColor(this Windows.UI.Color backgroundColor)
+ {
+ var nThreshold = 105;
+ int bgLuminance = Convert.ToInt32(backgroundColor.R * 0.2 + backgroundColor.G * 0.7 + backgroundColor.B * 0.1);
+
+ Windows.UI.Color foregroundColor = 255 - bgLuminance < nThreshold ? Colors.Black : Colors.White;
+ return foregroundColor;
+ }
+
+ public static void SetBinding(this FrameworkElement self, DependencyProperty property, string path)
+ {
+ self.SetBinding(property, new Windows.UI.Xaml.Data.Binding { Path = new PropertyPath(path) });
+ }
+
+ public static void SetBinding(this FrameworkElement self, DependencyProperty property, string path, Windows.UI.Xaml.Data.IValueConverter converter)
+ {
+ self.SetBinding(property, new Windows.UI.Xaml.Data.Binding { Path = new PropertyPath(path), Converter = converter });
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WinRT/FileImageSourceHandler.cs b/Xamarin.Forms.Platform.WinRT/FileImageSourceHandler.cs
new file mode 100644
index 00000000..c9c6f16b
--- /dev/null
+++ b/Xamarin.Forms.Platform.WinRT/FileImageSourceHandler.cs
@@ -0,0 +1,29 @@
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+using Windows.UI.Xaml.Media.Imaging;
+
+#if WINDOWS_UWP
+
+namespace Xamarin.Forms.Platform.UWP
+#else
+
+namespace Xamarin.Forms.Platform.WinRT
+#endif
+{
+ public sealed class FileImageSourceHandler : IImageSourceHandler
+ {
+ public Task<Windows.UI.Xaml.Media.ImageSource> LoadImageAsync(ImageSource imagesoure, CancellationToken cancellationToken = new CancellationToken())
+ {
+ Windows.UI.Xaml.Media.ImageSource image = null;
+ var filesource = imagesoure as FileImageSource;
+ if (filesource != null)
+ {
+ string file = filesource.File;
+ image = new BitmapImage(new Uri("ms-appx:///" + file));
+ }
+
+ return Task.FromResult(image);
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WinRT/FileImageSourcePathConverter.cs b/Xamarin.Forms.Platform.WinRT/FileImageSourcePathConverter.cs
new file mode 100644
index 00000000..64468f5e
--- /dev/null
+++ b/Xamarin.Forms.Platform.WinRT/FileImageSourcePathConverter.cs
@@ -0,0 +1,26 @@
+using System;
+using Windows.UI.Xaml.Controls;
+
+#if WINDOWS_UWP
+
+namespace Xamarin.Forms.Platform.UWP
+#else
+
+namespace Xamarin.Forms.Platform.WinRT
+#endif
+{
+ internal class FileImageSourcePathConverter : Windows.UI.Xaml.Data.IValueConverter
+ {
+ public object Convert(object value, Type targetType, object parameter, string language)
+ {
+ var source = (FileImageSource)value;
+ string uri = "ms-appx:///" + (source != null ? source.File : string.Empty);
+ return new BitmapIcon { UriSource = new Uri(uri) };
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter, string language)
+ {
+ throw new NotImplementedException();
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WinRT/FontExtensions.cs b/Xamarin.Forms.Platform.WinRT/FontExtensions.cs
new file mode 100644
index 00000000..efab6118
--- /dev/null
+++ b/Xamarin.Forms.Platform.WinRT/FontExtensions.cs
@@ -0,0 +1,75 @@
+using System;
+using Windows.UI.Text;
+using Windows.UI.Xaml.Controls;
+using Windows.UI.Xaml.Documents;
+using Windows.UI.Xaml.Media;
+using WApplication = Windows.UI.Xaml.Application;
+
+#if WINDOWS_UWP
+
+namespace Xamarin.Forms.Platform.UWP
+#else
+
+namespace Xamarin.Forms.Platform.WinRT
+#endif
+{
+ public static class FontExtensions
+ {
+ public static void ApplyFont(this Control self, Font font)
+ {
+ self.FontSize = font.UseNamedSize ? font.NamedSize.GetFontSize() : font.FontSize;
+ self.FontFamily = !string.IsNullOrEmpty(font.FontFamily) ? new FontFamily(font.FontFamily) : (FontFamily)WApplication.Current.Resources["ContentControlThemeFontFamily"];
+ self.FontStyle = font.FontAttributes.HasFlag(FontAttributes.Italic) ? FontStyle.Italic : FontStyle.Normal;
+ self.FontWeight = font.FontAttributes.HasFlag(FontAttributes.Bold) ? FontWeights.Bold : FontWeights.Normal;
+ }
+
+ public static void ApplyFont(this TextBlock self, Font font)
+ {
+ self.FontSize = font.UseNamedSize ? font.NamedSize.GetFontSize() : font.FontSize;
+ self.FontFamily = !string.IsNullOrEmpty(font.FontFamily) ? new FontFamily(font.FontFamily) : (FontFamily)WApplication.Current.Resources["ContentControlThemeFontFamily"];
+ self.FontStyle = font.FontAttributes.HasFlag(FontAttributes.Italic) ? FontStyle.Italic : FontStyle.Normal;
+ self.FontWeight = font.FontAttributes.HasFlag(FontAttributes.Bold) ? FontWeights.Bold : FontWeights.Normal;
+ }
+
+ public static void ApplyFont(this TextElement self, Font font)
+ {
+ self.FontSize = font.UseNamedSize ? font.NamedSize.GetFontSize() : font.FontSize;
+ self.FontFamily = !string.IsNullOrEmpty(font.FontFamily) ? new FontFamily(font.FontFamily) : (FontFamily)WApplication.Current.Resources["ContentControlThemeFontFamily"];
+ self.FontStyle = font.FontAttributes.HasFlag(FontAttributes.Italic) ? FontStyle.Italic : FontStyle.Normal;
+ self.FontWeight = font.FontAttributes.HasFlag(FontAttributes.Bold) ? FontWeights.Bold : FontWeights.Normal;
+ }
+
+ internal static void ApplyFont(this Control self, IFontElement element)
+ {
+ self.FontSize = element.FontSize;
+ self.FontFamily = !string.IsNullOrEmpty(element.FontFamily) ? new FontFamily(element.FontFamily) : (FontFamily)WApplication.Current.Resources["ContentControlThemeFontFamily"];
+ self.FontStyle = element.FontAttributes.HasFlag(FontAttributes.Italic) ? FontStyle.Italic : FontStyle.Normal;
+ self.FontWeight = element.FontAttributes.HasFlag(FontAttributes.Bold) ? FontWeights.Bold : FontWeights.Normal;
+ }
+
+ internal static double GetFontSize(this NamedSize size)
+ {
+ // These are values pulled from the mapped sizes on Windows Phone, WinRT has no equivalent sizes, only intents.
+ switch (size)
+ {
+ case NamedSize.Default:
+ return (double)WApplication.Current.Resources["ControlContentThemeFontSize"];
+ case NamedSize.Micro:
+ return 18.667 - 3;
+ case NamedSize.Small:
+ return 18.667;
+ case NamedSize.Medium:
+ return 22.667;
+ case NamedSize.Large:
+ return 32;
+ default:
+ throw new ArgumentOutOfRangeException("size");
+ }
+ }
+
+ internal static bool IsDefault(this IFontElement self)
+ {
+ return self.FontFamily == null && self.FontSize == Device.GetNamedSize(NamedSize.Default, typeof(Label), true) && self.FontAttributes == FontAttributes.None;
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WinRT/FormsButton.cs b/Xamarin.Forms.Platform.WinRT/FormsButton.cs
new file mode 100644
index 00000000..73c2f922
--- /dev/null
+++ b/Xamarin.Forms.Platform.WinRT/FormsButton.cs
@@ -0,0 +1,46 @@
+using Windows.UI.Xaml;
+using Windows.UI.Xaml.Controls;
+
+#if WINDOWS_UWP
+
+namespace Xamarin.Forms.Platform.UWP
+#else
+
+namespace Xamarin.Forms.Platform.WinRT
+#endif
+{
+ public class FormsButton : Windows.UI.Xaml.Controls.Button
+ {
+ public static readonly DependencyProperty BorderRadiusProperty = DependencyProperty.Register("BorderRadius", typeof(int), typeof(FormsButton),
+ new PropertyMetadata(default(int), OnBorderRadiusChanged));
+
+ Border _border;
+
+ public int BorderRadius
+ {
+ get { return (int)GetValue(BorderRadiusProperty); }
+ set { SetValue(BorderRadiusProperty, value); }
+ }
+
+ protected override void OnApplyTemplate()
+ {
+ base.OnApplyTemplate();
+
+ _border = GetTemplateChild("Border") as Border;
+ UpdateBorderRadius();
+ }
+
+ static void OnBorderRadiusChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ ((FormsButton)d).UpdateBorderRadius();
+ }
+
+ void UpdateBorderRadius()
+ {
+ if (_border == null)
+ return;
+
+ _border.CornerRadius = new CornerRadius(BorderRadius);
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WinRT/FormsComboBox.cs b/Xamarin.Forms.Platform.WinRT/FormsComboBox.cs
new file mode 100644
index 00000000..f693c942
--- /dev/null
+++ b/Xamarin.Forms.Platform.WinRT/FormsComboBox.cs
@@ -0,0 +1,68 @@
+using System;
+using Windows.UI.Xaml;
+using Windows.UI.Xaml.Controls;
+using Windows.UI.Xaml.Media.Animation;
+
+#if WINDOWS_UWP
+
+namespace Xamarin.Forms.Platform.UWP
+#else
+
+namespace Xamarin.Forms.Platform.WinRT
+#endif
+{
+ public class FormsComboBox : ComboBox
+ {
+ internal bool IsClosingAnimated { get; private set; }
+
+ internal bool IsFullScreen => Device.Idiom == TargetIdiom.Phone && Items != null && Items.Count > 5;
+
+ internal bool IsOpeningAnimated { get; private set; }
+
+ protected override void OnApplyTemplate()
+ {
+ base.OnApplyTemplate();
+
+ if (Device.Idiom == TargetIdiom.Phone)
+ {
+ // If we're running on the phone, we have to give the PickerRenderer hooks
+ // into the opening and closing animations so it can handle them smoothly
+ // and measure at the appropriate times
+
+ var openedState = GetTemplateChild("Opened") as VisualState;
+ if (openedState != null)
+ {
+ openedState.Storyboard.Completed += (sender, o) => OnOpenAnimationCompleted();
+ IsOpeningAnimated = true;
+ }
+
+ var closedState = GetTemplateChild("Closed") as VisualState;
+
+ // On the phone, this is a dummy animation we've added to the closed state in the VSM
+ // Since it finishes immediately, we can use its Completed event to signal that the
+ // closing animation has started
+ var closedSignalAnimation = closedState?.Storyboard.Children[0] as DoubleAnimation;
+
+ if (closedSignalAnimation != null)
+ {
+ closedSignalAnimation.Completed += (sender, o) => OnClosedAnimationStarted();
+ IsClosingAnimated = true;
+ }
+ }
+ }
+
+ protected virtual void OnClosedAnimationStarted()
+ {
+ ClosedAnimationStarted?.Invoke(this, EventArgs.Empty);
+ }
+
+ protected virtual void OnOpenAnimationCompleted()
+ {
+ OpenAnimationCompleted?.Invoke(this, EventArgs.Empty);
+ }
+
+ internal event EventHandler<EventArgs> ClosedAnimationStarted;
+
+ internal event EventHandler<EventArgs> OpenAnimationCompleted;
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WinRT/FormsDatePicker.cs b/Xamarin.Forms.Platform.WinRT/FormsDatePicker.cs
new file mode 100644
index 00000000..44127423
--- /dev/null
+++ b/Xamarin.Forms.Platform.WinRT/FormsDatePicker.cs
@@ -0,0 +1,76 @@
+using System;
+using Windows.UI.Core;
+using Windows.UI.Xaml;
+using Windows.UI.Xaml.Controls;
+
+#if WINDOWS_UWP
+
+namespace Xamarin.Forms.Platform.UWP
+#else
+
+namespace Xamarin.Forms.Platform.WinRT
+#endif
+{
+ public class FormsDatePicker : Windows.UI.Xaml.Controls.DatePicker
+ {
+ public FormsDatePicker()
+ {
+ if (Device.Idiom == TargetIdiom.Desktop || Device.Idiom == TargetIdiom.Tablet)
+ {
+ Loaded += (sender, args) => { Window.Current.Activated += WindowOnActivated; };
+ Unloaded += (sender, args) => { Window.Current.Activated -= WindowOnActivated; };
+ }
+ }
+
+ public event EventHandler<EventArgs> ForceInvalidate;
+
+ protected override void OnApplyTemplate()
+ {
+ base.OnApplyTemplate();
+
+ if (Device.Idiom == TargetIdiom.Desktop || Device.Idiom == TargetIdiom.Tablet)
+ {
+ // Look for the combo boxes which make up a DatePicker on Windows 8.1
+ // So we can hook into their closed events and invalidate them if necessary
+
+ var dayPicker = GetTemplateChild("DayPicker") as ComboBox;
+ if (dayPicker != null)
+ {
+ dayPicker.DropDownClosed += PickerOnDropDownClosed;
+ }
+
+ var monthPicker = GetTemplateChild("MonthPicker") as ComboBox;
+ if (monthPicker != null)
+ {
+ monthPicker.DropDownClosed += PickerOnDropDownClosed;
+ }
+
+ var yearPicker = GetTemplateChild("YearPicker") as ComboBox;
+ if (yearPicker != null)
+ {
+ yearPicker.DropDownClosed += PickerOnDropDownClosed;
+ }
+ }
+ }
+
+ void PickerOnDropDownClosed(object sender, object o)
+ {
+ // If the DatePicker is in a TableView or ListView and the user
+ // opens one of the dropdowns but does not actually change the value,
+ // when the dropdown closes, the selected value will go blank
+ // To fix this, we have to invalidate the control
+ // This only applies to Windows 8.1
+ ForceInvalidate?.Invoke(this, EventArgs.Empty);
+ }
+
+ void WindowOnActivated(object sender, WindowActivatedEventArgs windowActivatedEventArgs)
+ {
+ // If the DatePicker is in a TableView or ListView, when the application loses and then regains focus
+ // the TextBlock/ComboBox controls (UWP and 8.1, respectively) which display its selected value
+ // will go blank.
+ // To fix this, we have to signal the renderer to invalidate if
+ // Window.Activated occurs.
+ ForceInvalidate?.Invoke(this, EventArgs.Empty);
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WinRT/FormsTextBox.cs b/Xamarin.Forms.Platform.WinRT/FormsTextBox.cs
new file mode 100644
index 00000000..03725cd8
--- /dev/null
+++ b/Xamarin.Forms.Platform.WinRT/FormsTextBox.cs
@@ -0,0 +1,332 @@
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+using Windows.UI.Core;
+using Windows.UI.Xaml;
+using Windows.UI.Xaml.Controls;
+using Windows.UI.Xaml.Input;
+using Windows.UI.Xaml.Media;
+
+#if WINDOWS_UWP
+
+namespace Xamarin.Forms.Platform.UWP
+#else
+
+namespace Xamarin.Forms.Platform.WinRT
+#endif
+{
+ /// <summary>
+ /// An intermediate class for injecting bindings for things the default
+ /// textbox doesn't allow us to bind/modify
+ /// </summary>
+ public class FormsTextBox : TextBox
+ {
+ const char ObfuscationCharacter = '●';
+
+ public static readonly DependencyProperty PlaceholderForegroundBrushProperty = DependencyProperty.Register("PlaceholderForegroundBrush", typeof(Brush), typeof(FormsTextBox),
+ new PropertyMetadata(default(Brush)));
+
+ public static readonly DependencyProperty PlaceholderForegroundFocusBrushProperty = DependencyProperty.Register("PlaceholderForegroundFocusBrush", typeof(Brush), typeof(FormsTextBox),
+ new PropertyMetadata(default(Brush)));
+
+ public static readonly DependencyProperty ForegroundFocusBrushProperty = DependencyProperty.Register("ForegroundFocusBrush", typeof(Brush), typeof(FormsTextBox), new PropertyMetadata(default(Brush)));
+
+ public static readonly DependencyProperty BackgroundFocusBrushProperty = DependencyProperty.Register("BackgroundFocusBrush", typeof(Brush), typeof(FormsTextBox), new PropertyMetadata(default(Brush)));
+
+ public static readonly DependencyProperty IsPasswordProperty = DependencyProperty.Register("IsPassword", typeof(bool), typeof(FormsTextBox), new PropertyMetadata(default(bool), OnIsPasswordChanged));
+
+ public new static readonly DependencyProperty TextProperty = DependencyProperty.Register("Text", typeof(string), typeof(FormsTextBox), new PropertyMetadata("", TextPropertyChanged));
+
+ static InputScope s_passwordInputScope;
+ Border _borderElement;
+ InputScope _cachedInputScope;
+ bool _cachedPredictionsSetting;
+ bool _cachedSpellCheckSetting;
+ CancellationTokenSource _cts;
+ bool _internalChangeFlag;
+
+ public FormsTextBox()
+ {
+ TextChanged += OnTextChanged;
+ SelectionChanged += OnSelectionChanged;
+ }
+
+ public Brush BackgroundFocusBrush
+ {
+ get { return (Brush)GetValue(BackgroundFocusBrushProperty); }
+ set { SetValue(BackgroundFocusBrushProperty, value); }
+ }
+
+ public Brush ForegroundFocusBrush
+ {
+ get { return (Brush)GetValue(ForegroundFocusBrushProperty); }
+ set { SetValue(ForegroundFocusBrushProperty, value); }
+ }
+
+ public bool IsPassword
+ {
+ get { return (bool)GetValue(IsPasswordProperty); }
+ set { SetValue(IsPasswordProperty, value); }
+ }
+
+ public Brush PlaceholderForegroundBrush
+ {
+ get { return (Brush)GetValue(PlaceholderForegroundBrushProperty); }
+ set { SetValue(PlaceholderForegroundBrushProperty, value); }
+ }
+
+ public Brush PlaceholderForegroundFocusBrush
+ {
+ get { return (Brush)GetValue(PlaceholderForegroundFocusBrushProperty); }
+ set { SetValue(PlaceholderForegroundFocusBrushProperty, value); }
+ }
+
+ public new string Text
+ {
+ get { return (string)GetValue(TextProperty); }
+ set { SetValue(TextProperty, value); }
+ }
+
+ static InputScope PasswordInputScope
+ {
+ get
+ {
+ if (s_passwordInputScope != null)
+ {
+ return s_passwordInputScope;
+ }
+
+ s_passwordInputScope = new InputScope();
+ var name = new InputScopeName { NameValue = InputScopeNameValue.Default };
+ s_passwordInputScope.Names.Add(name);
+
+ return s_passwordInputScope;
+ }
+ }
+
+ protected override void OnApplyTemplate()
+ {
+ base.OnApplyTemplate();
+
+ if (Device.Idiom == TargetIdiom.Phone)
+ {
+ // If we're on the phone, we need to grab this from the template
+ // so we can manually handle it's background when focused
+ _borderElement = (Border)GetTemplateChild("BorderElement");
+ }
+ }
+
+ protected override void OnGotFocus(RoutedEventArgs e)
+ {
+ base.OnGotFocus(e);
+
+ // If we're on the phone, the Visual State Manager crashes if we try to
+ // handle alternate background colors in the focus state; we have to do
+ // it manually here
+ if (Device.Idiom == TargetIdiom.Phone && _borderElement != null)
+ {
+ _borderElement.Background = BackgroundFocusBrush;
+ }
+ }
+
+ void DelayObfuscation()
+ {
+ int lengthDifference = base.Text.Length - Text.Length;
+
+ string updatedRealText = DetermineTextFromPassword(Text, base.Text);
+
+ if (Text == updatedRealText)
+ {
+ // Nothing to do
+ return;
+ }
+
+ Text = updatedRealText;
+
+ // Cancel any pending delayed obfuscation
+ _cts?.Cancel();
+ _cts = null;
+
+ string newText;
+
+ if (lengthDifference != 1)
+ {
+ // Either More than one character got added in this text change (e.g., a paste operation)
+ // Or characters were removed. Either way, we don't need to do the delayed obfuscation dance
+ newText = Obfuscate();
+ }
+ else
+ {
+ // Only one character was added; we need to leave it visible for a brief time period
+ // Obfuscate all but the last character for now
+ newText = Obfuscate(true);
+
+ // Leave the last character visible until a new character is added
+ // or sufficient time has passed
+ if (_cts == null)
+ {
+ _cts = new CancellationTokenSource();
+ }
+
+ Task.Run(async () =>
+ {
+ await Task.Delay(TimeSpan.FromSeconds(0.5), _cts.Token);
+ _cts.Token.ThrowIfCancellationRequested();
+ await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
+ {
+ base.Text = Obfuscate();
+ SelectionStart = base.Text.Length;
+ });
+ }, _cts.Token);
+ }
+
+ if (base.Text == newText)
+ {
+ return;
+ }
+
+ base.Text = newText;
+ SelectionStart = base.Text.Length;
+ }
+
+ static string DetermineTextFromPassword(string realText, string passwordText)
+ {
+ if (realText.Length == passwordText.Length)
+ {
+ return realText;
+ }
+
+ if (passwordText.Length == 0)
+ {
+ return "";
+ }
+
+ if (passwordText.Length < realText.Length)
+ {
+ return realText.Substring(0, passwordText.Length);
+ }
+
+ int lengthDifference = passwordText.Length - realText.Length;
+
+ return realText + passwordText.Substring(passwordText.Length - lengthDifference, lengthDifference);
+ }
+
+ string Obfuscate(bool leaveLastVisible = false)
+ {
+ if (leaveLastVisible && Text.Length == 1)
+ {
+ return Text;
+ }
+
+ if (leaveLastVisible && Text.Length > 1)
+ {
+ return new string(ObfuscationCharacter, Text.Length - 1) + Text.Substring(Text.Length - 1, 1);
+ }
+
+ return new string(ObfuscationCharacter, Text.Length);
+ }
+
+ static void OnIsPasswordChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
+ {
+ var textBox = (FormsTextBox)dependencyObject;
+ textBox.UpdateInputScope();
+ textBox.SyncBaseText();
+ }
+
+ void OnSelectionChanged(object sender, RoutedEventArgs routedEventArgs)
+ {
+ if (!IsPassword)
+ {
+ return;
+ }
+
+ // Prevent the user from selecting any text in the password box by forcing all selection
+ // to zero-length at the end of the Text
+ // This simulates the "do not allow clipboard copy" behavior the PasswordBox control has
+ if (SelectionLength > 0 || SelectionStart < Text.Length)
+ {
+ SelectionLength = 0;
+ SelectionStart = Text.Length;
+ }
+ }
+
+ void OnTextChanged(object sender, Windows.UI.Xaml.Controls.TextChangedEventArgs textChangedEventArgs)
+ {
+ if (IsPassword)
+ {
+ // If we're not on a phone, we can just obfuscate any input
+ if (Device.Idiom != TargetIdiom.Phone)
+ {
+ string updatedRealText = DetermineTextFromPassword(Text, base.Text);
+
+ if (Text == updatedRealText)
+ {
+ // Nothing to do
+ return;
+ }
+
+ Text = updatedRealText;
+
+ string updatedText = Obfuscate();
+
+ if (base.Text != updatedText)
+ {
+ base.Text = updatedText;
+ SelectionStart = base.Text.Length;
+ }
+
+ return;
+ }
+
+ // If we are on the phone, we might need to delay obfuscating the last character
+ DelayObfuscation();
+ }
+ else if (base.Text != Text)
+ {
+ // Not in password mode, so we just need to make the "real" text match
+ // what's in the textbox; the internalChange flag keeps the TextProperty
+ // synchronization from happening
+ _internalChangeFlag = true;
+ Text = base.Text;
+ _internalChangeFlag = false;
+ }
+ }
+
+ void SyncBaseText()
+ {
+ if (_internalChangeFlag)
+ {
+ return;
+ }
+
+ base.Text = IsPassword ? Obfuscate() : Text;
+
+ SelectionStart = base.Text.Length;
+ }
+
+ static void TextPropertyChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
+ {
+ var textBox = (FormsTextBox)dependencyObject;
+ textBox.SyncBaseText();
+ }
+
+ void UpdateInputScope()
+ {
+ if (IsPassword)
+ {
+ _cachedInputScope = InputScope;
+ _cachedSpellCheckSetting = IsSpellCheckEnabled;
+ _cachedPredictionsSetting = IsTextPredictionEnabled;
+ InputScope = PasswordInputScope; // Change to default input scope so we don't have suggestions, etc.
+ IsTextPredictionEnabled = false; // Force the other text modification options off
+ IsSpellCheckEnabled = false;
+ }
+ else
+ {
+ InputScope = _cachedInputScope;
+ IsSpellCheckEnabled = _cachedSpellCheckSetting;
+ IsTextPredictionEnabled = _cachedPredictionsSetting;
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WinRT/FormsTimePicker.cs b/Xamarin.Forms.Platform.WinRT/FormsTimePicker.cs
new file mode 100644
index 00000000..976c56f4
--- /dev/null
+++ b/Xamarin.Forms.Platform.WinRT/FormsTimePicker.cs
@@ -0,0 +1,76 @@
+using System;
+using Windows.UI.Core;
+using Windows.UI.Xaml;
+using Windows.UI.Xaml.Controls;
+
+#if WINDOWS_UWP
+
+namespace Xamarin.Forms.Platform.UWP
+#else
+
+namespace Xamarin.Forms.Platform.WinRT
+#endif
+{
+ public class FormsTimePicker : Windows.UI.Xaml.Controls.TimePicker
+ {
+ public FormsTimePicker()
+ {
+ if (Device.Idiom == TargetIdiom.Desktop || Device.Idiom == TargetIdiom.Tablet)
+ {
+ Loaded += (sender, args) => { Window.Current.Activated += WindowOnActivated; };
+ Unloaded += (sender, args) => { Window.Current.Activated -= WindowOnActivated; };
+ }
+ }
+
+ public event EventHandler<EventArgs> ForceInvalidate;
+
+ protected override void OnApplyTemplate()
+ {
+ base.OnApplyTemplate();
+
+ if (Device.Idiom == TargetIdiom.Desktop || Device.Idiom == TargetIdiom.Tablet)
+ {
+ // Look for the combo boxes which make up a TimePicker on Windows 8.1
+ // So we can hook into their closed events and invalidate them if necessary
+
+ var hourPicker = GetTemplateChild("HourPicker") as ComboBox;
+ if (hourPicker != null)
+ {
+ hourPicker.DropDownClosed += PickerOnDropDownClosed;
+ }
+
+ var minutePicker = GetTemplateChild("MinutePicker") as ComboBox;
+ if (minutePicker != null)
+ {
+ minutePicker.DropDownClosed += PickerOnDropDownClosed;
+ }
+
+ var periodPicker = GetTemplateChild("PeriodPicker") as ComboBox;
+ if (periodPicker != null)
+ {
+ periodPicker.DropDownClosed += PickerOnDropDownClosed;
+ }
+ }
+ }
+
+ void PickerOnDropDownClosed(object sender, object o)
+ {
+ // If the TimePicker is in a TableView or ListView and the user
+ // opens one of the dropdowns but does not actually change the value,
+ // when the dropdown closes, the selected value will go blank
+ // To fix this, we have to invalidate the control
+ // This only applies to Windows 8.1
+ ForceInvalidate?.Invoke(this, EventArgs.Empty);
+ }
+
+ void WindowOnActivated(object sender, WindowActivatedEventArgs windowActivatedEventArgs)
+ {
+ // If the TimePicker is in a TableView or ListView, when the application loses focus
+ // the TextBlock/ComboBox controls (UWP and 8.1, respectively) which display its selected value
+ // will go blank.
+ // To fix this, we have to signal the renderer to invalidate if
+ // Window.Activated occurs.
+ ForceInvalidate?.Invoke(this, EventArgs.Empty);
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WinRT/FrameRenderer.cs b/Xamarin.Forms.Platform.WinRT/FrameRenderer.cs
new file mode 100644
index 00000000..7dcac435
--- /dev/null
+++ b/Xamarin.Forms.Platform.WinRT/FrameRenderer.cs
@@ -0,0 +1,71 @@
+using System.ComponentModel;
+using Windows.UI.Xaml;
+using Windows.UI.Xaml.Controls;
+
+#if WINDOWS_UWP
+
+namespace Xamarin.Forms.Platform.UWP
+#else
+
+namespace Xamarin.Forms.Platform.WinRT
+#endif
+{
+ public class FrameRenderer : ViewRenderer<Frame, Border>
+ {
+ public FrameRenderer()
+ {
+ AutoPackage = false;
+ }
+
+ protected override void OnElementChanged(ElementChangedEventArgs<Frame> e)
+ {
+ base.OnElementChanged(e);
+
+ if (e.NewElement != null)
+ {
+ if (Control == null)
+ SetNativeControl(new Border());
+
+ PackChild();
+ UpdateBorder();
+ }
+ }
+
+ protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ base.OnElementPropertyChanged(sender, e);
+
+ if (e.PropertyName == "Content")
+ {
+ PackChild();
+ }
+ else if (e.PropertyName == Frame.OutlineColorProperty.PropertyName || e.PropertyName == Frame.HasShadowProperty.PropertyName)
+ {
+ UpdateBorder();
+ }
+ }
+
+ void PackChild()
+ {
+ if (Element.Content == null)
+ return;
+
+ IVisualElementRenderer renderer = Element.Content.GetOrCreateRenderer();
+ Control.Child = renderer.ContainerElement;
+ }
+
+ void UpdateBorder()
+ {
+ Control.CornerRadius = new CornerRadius(5);
+ if (Element.OutlineColor != Color.Default)
+ {
+ Control.BorderBrush = Element.OutlineColor.ToBrush();
+ Control.BorderThickness = new Windows.UI.Xaml.Thickness(1);
+ }
+ else
+ {
+ Control.BorderBrush = new Color(0, 0, 0, 0).ToBrush();
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WinRT/FrameworkElementExtensions.cs b/Xamarin.Forms.Platform.WinRT/FrameworkElementExtensions.cs
new file mode 100644
index 00000000..64b2daab
--- /dev/null
+++ b/Xamarin.Forms.Platform.WinRT/FrameworkElementExtensions.cs
@@ -0,0 +1,114 @@
+using System;
+using System.Linq;
+using System.Reflection;
+using Windows.UI.Xaml;
+using Windows.UI.Xaml.Controls;
+using Windows.UI.Xaml.Media;
+using WBinding = Windows.UI.Xaml.Data.Binding;
+using WBindingExpression = Windows.UI.Xaml.Data.BindingExpression;
+
+#if WINDOWS_UWP
+
+namespace Xamarin.Forms.Platform.UWP
+#else
+
+namespace Xamarin.Forms.Platform.WinRT
+#endif
+{
+ internal static class FrameworkElementExtensions
+ {
+ static readonly Lazy<ConcurrentDictionary<Type, DependencyProperty>> ForegroundProperties =
+ new Lazy<ConcurrentDictionary<Type, DependencyProperty>>(() => new ConcurrentDictionary<Type, DependencyProperty>());
+
+ public static Brush GetForeground(this FrameworkElement element)
+ {
+ if (element == null)
+ throw new ArgumentNullException("element");
+
+ return (Brush)element.GetValue(GetForegroundProperty(element));
+ }
+
+ public static WBinding GetForegroundBinding(this FrameworkElement element)
+ {
+ WBindingExpression expr = element.GetBindingExpression(GetForegroundProperty(element));
+ if (expr == null)
+ return null;
+
+ return expr.ParentBinding;
+ }
+
+ public static object GetForegroundCache(this FrameworkElement element)
+ {
+ WBinding binding = GetForegroundBinding(element);
+ if (binding != null)
+ return binding;
+
+ return GetForeground(element);
+ }
+
+ public static void RestoreForegroundCache(this FrameworkElement element, object cache)
+ {
+ var binding = cache as WBinding;
+ if (binding != null)
+ SetForeground(element, binding);
+ else
+ SetForeground(element, (Brush)cache);
+ }
+
+ public static void SetForeground(this FrameworkElement element, Brush foregroundBrush)
+ {
+ if (element == null)
+ throw new ArgumentNullException("element");
+
+ element.SetValue(GetForegroundProperty(element), foregroundBrush);
+ }
+
+ public static void SetForeground(this FrameworkElement element, WBinding binding)
+ {
+ if (element == null)
+ throw new ArgumentNullException("element");
+
+ element.SetBinding(GetForegroundProperty(element), binding);
+ }
+
+ internal static T GetFirstDescendant<T>(this DependencyObject element) where T : FrameworkElement
+ {
+ int count = VisualTreeHelper.GetChildrenCount(element);
+ for (var i = 0; i < count; i++)
+ {
+ DependencyObject child = VisualTreeHelper.GetChild(element, i);
+
+ T target = child as T ?? GetFirstDescendant<T>(child);
+ if (target != null)
+ return target;
+ }
+
+ return null;
+ }
+
+ static DependencyProperty GetForegroundProperty(FrameworkElement element)
+ {
+ if (element is Control)
+ return Control.ForegroundProperty;
+ if (element is TextBlock)
+ return TextBlock.ForegroundProperty;
+
+ Type type = element.GetType();
+
+ DependencyProperty foregroundProperty;
+ if (!ForegroundProperties.Value.TryGetValue(type, out foregroundProperty))
+ {
+ FieldInfo field = type.GetFields().FirstOrDefault(f => f.Name == "ForegroundProperty");
+ if (field == null)
+ throw new ArgumentException("type is not a Foregroundable type");
+
+ var property = (DependencyProperty)field.GetValue(null);
+ ForegroundProperties.Value.TryAdd(type, property);
+
+ return property;
+ }
+
+ return foregroundProperty;
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WinRT/HeightConverter.cs b/Xamarin.Forms.Platform.WinRT/HeightConverter.cs
new file mode 100644
index 00000000..8ae73795
--- /dev/null
+++ b/Xamarin.Forms.Platform.WinRT/HeightConverter.cs
@@ -0,0 +1,32 @@
+using System;
+
+#if WINDOWS_UWP
+
+namespace Xamarin.Forms.Platform.UWP
+#else
+
+namespace Xamarin.Forms.Platform.WinRT
+#endif
+{
+ public sealed class HeightConverter : Windows.UI.Xaml.Data.IValueConverter
+ {
+ public object Convert(object value, Type targetType, object parameter, string language)
+ {
+ double def;
+
+ var ps = parameter as string;
+ if (string.IsNullOrWhiteSpace(ps) || !double.TryParse(ps, out def))
+ {
+ def = double.NaN;
+ }
+
+ var val = (double)value;
+ return val > 0 ? val : def;
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter, string language)
+ {
+ throw new NotImplementedException();
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WinRT/HorizontalTextAlignmentConverter.cs b/Xamarin.Forms.Platform.WinRT/HorizontalTextAlignmentConverter.cs
new file mode 100644
index 00000000..f4f26d1a
--- /dev/null
+++ b/Xamarin.Forms.Platform.WinRT/HorizontalTextAlignmentConverter.cs
@@ -0,0 +1,24 @@
+using System;
+
+#if WINDOWS_UWP
+
+namespace Xamarin.Forms.Platform.UWP
+#else
+
+namespace Xamarin.Forms.Platform.WinRT
+#endif
+{
+ public class HorizontalTextAlignmentConverter : Windows.UI.Xaml.Data.IValueConverter
+ {
+ public object Convert(object value, Type targetType, object parameter, string language)
+ {
+ var textAlign = (TextAlignment)value;
+ return textAlign.ToNativeTextAlignment();
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter, string language)
+ {
+ throw new NotSupportedException();
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WinRT/ICellRenderer.cs b/Xamarin.Forms.Platform.WinRT/ICellRenderer.cs
new file mode 100644
index 00000000..e34d2db2
--- /dev/null
+++ b/Xamarin.Forms.Platform.WinRT/ICellRenderer.cs
@@ -0,0 +1,14 @@
+
+#if WINDOWS_UWP
+
+namespace Xamarin.Forms.Platform.UWP
+#else
+
+namespace Xamarin.Forms.Platform.WinRT
+#endif
+{
+ public interface ICellRenderer : IRegisterable
+ {
+ Windows.UI.Xaml.DataTemplate GetTemplate(Cell cell);
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WinRT/IImageSourceHandler.cs b/Xamarin.Forms.Platform.WinRT/IImageSourceHandler.cs
new file mode 100644
index 00000000..91eff534
--- /dev/null
+++ b/Xamarin.Forms.Platform.WinRT/IImageSourceHandler.cs
@@ -0,0 +1,16 @@
+using System.Threading;
+using System.Threading.Tasks;
+
+#if WINDOWS_UWP
+
+namespace Xamarin.Forms.Platform.UWP
+#else
+
+namespace Xamarin.Forms.Platform.WinRT
+#endif
+{
+ public interface IImageSourceHandler : IRegisterable
+ {
+ Task<Windows.UI.Xaml.Media.ImageSource> LoadImageAsync(ImageSource imagesoure, CancellationToken cancellationToken = default(CancellationToken));
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WinRT/ITitleProvider.cs b/Xamarin.Forms.Platform.WinRT/ITitleProvider.cs
new file mode 100644
index 00000000..cd17b09e
--- /dev/null
+++ b/Xamarin.Forms.Platform.WinRT/ITitleProvider.cs
@@ -0,0 +1,21 @@
+using Windows.UI.Xaml.Media;
+
+#if WINDOWS_UWP
+
+namespace Xamarin.Forms.Platform.UWP
+#else
+
+namespace Xamarin.Forms.Platform.WinRT
+#endif
+{
+ internal interface ITitleProvider
+ {
+ Brush BarBackgroundBrush { set; }
+
+ Brush BarForegroundBrush { set; }
+
+ bool ShowTitle { get; set; }
+
+ string Title { get; set; }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WinRT/IToolbarProvider.cs b/Xamarin.Forms.Platform.WinRT/IToolbarProvider.cs
new file mode 100644
index 00000000..0db54b02
--- /dev/null
+++ b/Xamarin.Forms.Platform.WinRT/IToolbarProvider.cs
@@ -0,0 +1,16 @@
+using System.Threading.Tasks;
+using Windows.UI.Xaml.Controls;
+
+#if WINDOWS_UWP
+
+namespace Xamarin.Forms.Platform.UWP
+#else
+
+namespace Xamarin.Forms.Platform.WinRT
+#endif
+{
+ internal interface IToolbarProvider
+ {
+ Task<CommandBar> GetCommandBarAsync();
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WinRT/IVisualElementRenderer.cs b/Xamarin.Forms.Platform.WinRT/IVisualElementRenderer.cs
new file mode 100644
index 00000000..4db9c1a6
--- /dev/null
+++ b/Xamarin.Forms.Platform.WinRT/IVisualElementRenderer.cs
@@ -0,0 +1,23 @@
+using System;
+using Windows.UI.Xaml;
+
+#if WINDOWS_UWP
+
+namespace Xamarin.Forms.Platform.UWP
+#else
+
+namespace Xamarin.Forms.Platform.WinRT
+#endif
+{
+ public interface IVisualElementRenderer : IRegisterable, IDisposable
+ {
+ FrameworkElement ContainerElement { get; }
+
+ VisualElement Element { get; }
+
+ event EventHandler<VisualElementChangedEventArgs> ElementChanged;
+
+ SizeRequest GetDesiredSize(double widthConstraint, double heightConstraint);
+ void SetElement(VisualElement element);
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WinRT/IWrapperAware.cs b/Xamarin.Forms.Platform.WinRT/IWrapperAware.cs
new file mode 100644
index 00000000..aff90133
--- /dev/null
+++ b/Xamarin.Forms.Platform.WinRT/IWrapperAware.cs
@@ -0,0 +1,14 @@
+
+#if WINDOWS_UWP
+
+namespace Xamarin.Forms.Platform.UWP
+#else
+
+namespace Xamarin.Forms.Platform.WinRT
+#endif
+{
+ internal interface IWrapperAware
+ {
+ void NotifyWrapped();
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WinRT/ImageConverter.cs b/Xamarin.Forms.Platform.WinRT/ImageConverter.cs
new file mode 100644
index 00000000..e111be4c
--- /dev/null
+++ b/Xamarin.Forms.Platform.WinRT/ImageConverter.cs
@@ -0,0 +1,33 @@
+using System;
+using System.Threading.Tasks;
+
+#if WINDOWS_UWP
+
+namespace Xamarin.Forms.Platform.UWP
+#else
+
+namespace Xamarin.Forms.Platform.WinRT
+#endif
+{
+ public class ImageConverter : Windows.UI.Xaml.Data.IValueConverter
+ {
+ public object Convert(object value, Type targetType, object parameter, string language)
+ {
+ var source = (ImageSource)value;
+ IImageSourceHandler handler;
+
+ if (source != null && (handler = Registrar.Registered.GetHandler<IImageSourceHandler>(source.GetType())) != null)
+ {
+ Task<Windows.UI.Xaml.Media.ImageSource> task = handler.LoadImageAsync(source);
+ return new AsyncValue<Windows.UI.Xaml.Media.ImageSource>(task, null);
+ }
+
+ return null;
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter, string language)
+ {
+ throw new NotSupportedException();
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WinRT/ImageLoaderSourceHandler.cs b/Xamarin.Forms.Platform.WinRT/ImageLoaderSourceHandler.cs
new file mode 100644
index 00000000..c9c711e0
--- /dev/null
+++ b/Xamarin.Forms.Platform.WinRT/ImageLoaderSourceHandler.cs
@@ -0,0 +1,52 @@
+using System;
+using System.Diagnostics;
+using System.IO;
+using System.Threading;
+using System.Threading.Tasks;
+using Windows.Storage.Streams;
+using Windows.UI.Xaml.Media.Imaging;
+
+#if WINDOWS_UWP
+
+namespace Xamarin.Forms.Platform.UWP
+#else
+
+namespace Xamarin.Forms.Platform.WinRT
+#endif
+{
+ public sealed class ImageLoaderSourceHandler : IImageSourceHandler
+ {
+ public async Task<Windows.UI.Xaml.Media.ImageSource> LoadImageAsync(ImageSource imagesoure, CancellationToken cancellationToken = new CancellationToken())
+ {
+ var imageLoader = imagesoure as UriImageSource;
+ if (imageLoader?.Uri == null)
+ return null;
+
+ Stream streamImage = await imageLoader.GetStreamAsync(cancellationToken);
+ if (streamImage == null || !streamImage.CanRead)
+ {
+ return null;
+ }
+
+ using(IRandomAccessStream stream = streamImage.AsRandomAccessStream())
+ {
+ try
+ {
+ var image = new BitmapImage();
+ await image.SetSourceAsync(stream);
+ return image;
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine(ex);
+
+ // Because this literally throws System.Exception
+ // According to https://msdn.microsoft.com/library/windows/apps/jj191522
+ // this can happen if the image data is bad or the app is close to its
+ // memory limit
+ return null;
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WinRT/ImageRenderer.cs b/Xamarin.Forms.Platform.WinRT/ImageRenderer.cs
new file mode 100644
index 00000000..47c674c7
--- /dev/null
+++ b/Xamarin.Forms.Platform.WinRT/ImageRenderer.cs
@@ -0,0 +1,132 @@
+using System;
+using System.ComponentModel;
+using Windows.UI.Xaml;
+using Windows.UI.Xaml.Media;
+using Windows.UI.Xaml.Media.Imaging;
+
+#if WINDOWS_UWP
+
+namespace Xamarin.Forms.Platform.UWP
+#else
+
+namespace Xamarin.Forms.Platform.WinRT
+#endif
+{
+ public class ImageRenderer : ViewRenderer<Image, Windows.UI.Xaml.Controls.Image>
+ {
+ bool _measured;
+
+ public override SizeRequest GetDesiredSize(double widthConstraint, double heightConstraint)
+ {
+ if (Control.Source == null)
+ return new SizeRequest();
+
+ _measured = true;
+
+ var result = new Size { Width = ((BitmapImage)Control.Source).PixelWidth, Height = ((BitmapImage)Control.Source).PixelHeight };
+
+ return new SizeRequest(result);
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ if (Control != null)
+ {
+ Control.ImageOpened -= OnImageOpened;
+ }
+
+ base.Dispose(disposing);
+ }
+
+ protected override void OnElementChanged(ElementChangedEventArgs<Image> e)
+ {
+ base.OnElementChanged(e);
+
+ if (e.NewElement != null)
+ {
+ if (Control == null)
+ {
+ var image = new Windows.UI.Xaml.Controls.Image();
+ image.ImageOpened += OnImageOpened;
+ SetNativeControl(image);
+ }
+
+ UpdateSource();
+ UpdateAspect();
+ }
+ }
+
+ protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ base.OnElementPropertyChanged(sender, e);
+
+ if (e.PropertyName == Image.SourceProperty.PropertyName)
+ UpdateSource();
+ else if (e.PropertyName == Image.AspectProperty.PropertyName)
+ UpdateAspect();
+ }
+
+ static Stretch GetStretch(Aspect aspect)
+ {
+ switch (aspect)
+ {
+ case Aspect.Fill:
+ return Stretch.Fill;
+ case Aspect.AspectFill:
+ return Stretch.UniformToFill;
+ default:
+ case Aspect.AspectFit:
+ return Stretch.Uniform;
+ }
+ }
+
+ void OnImageOpened(object sender, RoutedEventArgs routedEventArgs)
+ {
+ if (_measured)
+ {
+ RefreshImage();
+ }
+ }
+
+ void RefreshImage()
+ {
+ Element?.InvalidateMeasure(InvalidationTrigger.RendererReady);
+ }
+
+ void UpdateAspect()
+ {
+ Control.Stretch = GetStretch(Element.Aspect);
+ }
+
+ async void UpdateSource()
+ {
+ Element.SetValueCore(Image.IsLoadingPropertyKey, true);
+
+ ImageSource source = Element.Source;
+ IImageSourceHandler handler;
+ if (source != null && (handler = Registrar.Registered.GetHandler<IImageSourceHandler>(source.GetType())) != null)
+ {
+ Windows.UI.Xaml.Media.ImageSource imagesource;
+ try
+ {
+ imagesource = await handler.LoadImageAsync(source);
+ }
+ catch (OperationCanceledException)
+ {
+ imagesource = null;
+ }
+
+ // In the time it takes to await the imagesource, some zippy little app
+ // might have disposed of this Image already.
+ if (Control != null)
+ Control.Source = imagesource;
+
+ RefreshImage();
+ }
+ else
+ Control.Source = null;
+
+ Element?.SetValueCore(Image.IsLoadingPropertyKey, false);
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WinRT/KeyboardConverter.cs b/Xamarin.Forms.Platform.WinRT/KeyboardConverter.cs
new file mode 100644
index 00000000..0cbd6a55
--- /dev/null
+++ b/Xamarin.Forms.Platform.WinRT/KeyboardConverter.cs
@@ -0,0 +1,27 @@
+using System;
+
+#if WINDOWS_UWP
+
+namespace Xamarin.Forms.Platform.UWP
+#else
+
+namespace Xamarin.Forms.Platform.WinRT
+#endif
+{
+ public class KeyboardConverter : Windows.UI.Xaml.Data.IValueConverter
+ {
+ public object Convert(object value, Type targetType, object parameter, string language)
+ {
+ var keyboard = value as Keyboard;
+ if (keyboard == null)
+ return null;
+
+ return keyboard.ToInputScope();
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter, string language)
+ {
+ throw new NotImplementedException();
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WinRT/KeyboardExtensions.cs b/Xamarin.Forms.Platform.WinRT/KeyboardExtensions.cs
new file mode 100644
index 00000000..331e5269
--- /dev/null
+++ b/Xamarin.Forms.Platform.WinRT/KeyboardExtensions.cs
@@ -0,0 +1,57 @@
+using System;
+using Windows.UI.Xaml.Input;
+
+#if WINDOWS_UWP
+
+namespace Xamarin.Forms.Platform.UWP
+#else
+
+namespace Xamarin.Forms.Platform.WinRT
+#endif
+{
+ public static class KeyboardExtensions
+ {
+ public static InputScope ToInputScope(this Keyboard self)
+ {
+ if (self == null)
+ throw new ArgumentNullException("self");
+
+ var result = new InputScope();
+ var name = new InputScopeName();
+ if (self == Keyboard.Default)
+ {
+ name.NameValue = InputScopeNameValue.Default;
+ }
+ else if (self == Keyboard.Chat)
+ {
+ name.NameValue = InputScopeNameValue.Chat;
+ }
+ else if (self == Keyboard.Email)
+ {
+ name.NameValue = InputScopeNameValue.EmailSmtpAddress;
+ }
+ else if (self == Keyboard.Numeric)
+ {
+ name.NameValue = InputScopeNameValue.Number;
+ }
+ else if (self == Keyboard.Telephone)
+ {
+ name.NameValue = InputScopeNameValue.TelephoneNumber;
+ }
+ else if (self == Keyboard.Text)
+ {
+ name.NameValue = InputScopeNameValue.Default;
+ }
+ else if (self == Keyboard.Url)
+ {
+ name.NameValue = InputScopeNameValue.Url;
+ }
+ else
+ {
+ name.NameValue = InputScopeNameValue.Default;
+ }
+ result.Names.Add(name);
+ return result;
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WinRT/LabelRenderer.cs b/Xamarin.Forms.Platform.WinRT/LabelRenderer.cs
new file mode 100644
index 00000000..41a0b477
--- /dev/null
+++ b/Xamarin.Forms.Platform.WinRT/LabelRenderer.cs
@@ -0,0 +1,202 @@
+using System;
+using System.ComponentModel;
+using Windows.Foundation;
+using Windows.UI.Xaml;
+using Windows.UI.Xaml.Controls;
+using Windows.UI.Xaml.Documents;
+
+#if WINDOWS_UWP
+
+namespace Xamarin.Forms.Platform.UWP
+#else
+
+namespace Xamarin.Forms.Platform.WinRT
+#endif
+{
+ public static class FormattedStringExtensions
+ {
+ public static Run ToRun(this Span span)
+ {
+ var run = new Run { Text = span.Text };
+
+ if (span.ForegroundColor != Color.Default)
+ run.Foreground = span.ForegroundColor.ToBrush();
+
+ if (!span.IsDefault())
+ run.ApplyFont(span.Font);
+
+ return run;
+ }
+ }
+
+ public class LabelRenderer : ViewRenderer<Label, TextBlock>
+ {
+ bool _fontApplied;
+
+ protected override Windows.Foundation.Size ArrangeOverride(Windows.Foundation.Size finalSize)
+ {
+ if (Element == null)
+ return finalSize;
+ double childHeight = Math.Max(0, Math.Min(Element.Height, Control.DesiredSize.Height));
+ var rect = new Rect();
+
+ switch (Element.VerticalTextAlignment)
+ {
+ case TextAlignment.Start:
+ break;
+ default:
+ case TextAlignment.Center:
+ rect.Y = (int)((finalSize.Height - childHeight) / 2);
+ break;
+ case TextAlignment.End:
+ rect.Y = finalSize.Height - childHeight;
+ break;
+ }
+ rect.Height = childHeight;
+ rect.Width = finalSize.Width;
+ Control.Arrange(rect);
+ return finalSize;
+ }
+
+ protected override void OnElementChanged(ElementChangedEventArgs<Label> e)
+ {
+ base.OnElementChanged(e);
+
+ if (e.NewElement != null)
+ {
+ if (Control == null)
+ {
+ SetNativeControl(new TextBlock());
+ }
+
+ UpdateText(Control);
+ UpdateColor(Control);
+ UpdateAlign(Control);
+ UpdateFont(Control);
+ UpdateLineBreakMode(Control);
+ }
+ }
+
+ protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ if (e.PropertyName == Label.TextProperty.PropertyName)
+ UpdateText(Control);
+ else if (e.PropertyName == Label.TextColorProperty.PropertyName)
+ UpdateColor(Control);
+ else if (e.PropertyName == Label.HorizontalTextAlignmentProperty.PropertyName || e.PropertyName == Label.VerticalTextAlignmentProperty.PropertyName)
+ UpdateAlign(Control);
+ else if (e.PropertyName == Label.FontProperty.PropertyName)
+ UpdateFont(Control);
+ else if (e.PropertyName == Label.LineBreakModeProperty.PropertyName)
+ UpdateLineBreakMode(Control);
+
+ base.OnElementPropertyChanged(sender, e);
+ }
+
+ void UpdateAlign(TextBlock textBlock)
+ {
+ if (textBlock == null)
+ return;
+
+ Label label = Element;
+ if (label == null)
+ return;
+
+ textBlock.TextAlignment = label.HorizontalTextAlignment.ToNativeTextAlignment();
+ textBlock.VerticalAlignment = label.VerticalTextAlignment.ToNativeVerticalAlignment();
+ }
+
+ void UpdateColor(TextBlock textBlock)
+ {
+ if (textBlock == null)
+ return;
+
+ Label label = Element;
+ if (label != null && label.TextColor != Color.Default)
+ {
+ textBlock.Foreground = label.TextColor.ToBrush();
+ }
+ else
+ {
+ textBlock.ClearValue(TextBlock.ForegroundProperty);
+ }
+ }
+
+ void UpdateFont(TextBlock textBlock)
+ {
+ if (textBlock == null)
+ return;
+
+ Label label = Element;
+ if (label == null || (label.IsDefault() && !_fontApplied))
+ return;
+
+ Font fontToApply = label.IsDefault() ? Font.SystemFontOfSize(NamedSize.Medium) : label.Font;
+
+ textBlock.ApplyFont(fontToApply);
+ _fontApplied = true;
+ }
+
+ void UpdateLineBreakMode(TextBlock textBlock)
+ {
+ if (textBlock == null)
+ return;
+
+ switch (Element.LineBreakMode)
+ {
+ case LineBreakMode.NoWrap:
+ textBlock.TextTrimming = TextTrimming.Clip;
+ textBlock.TextWrapping = TextWrapping.NoWrap;
+ break;
+ case LineBreakMode.WordWrap:
+ textBlock.TextTrimming = TextTrimming.None;
+ textBlock.TextWrapping = TextWrapping.Wrap;
+ break;
+ case LineBreakMode.CharacterWrap:
+ textBlock.TextTrimming = TextTrimming.WordEllipsis;
+ textBlock.TextWrapping = TextWrapping.Wrap;
+ break;
+ case LineBreakMode.HeadTruncation:
+ textBlock.TextTrimming = TextTrimming.WordEllipsis;
+ textBlock.TextWrapping = TextWrapping.NoWrap;
+ break;
+ case LineBreakMode.TailTruncation:
+ textBlock.TextTrimming = TextTrimming.WordEllipsis;
+ textBlock.TextWrapping = TextWrapping.NoWrap;
+ break;
+ case LineBreakMode.MiddleTruncation:
+ textBlock.TextTrimming = TextTrimming.WordEllipsis;
+ textBlock.TextWrapping = TextWrapping.NoWrap;
+ break;
+ default:
+ throw new ArgumentOutOfRangeException();
+ }
+ }
+
+ void UpdateText(TextBlock textBlock)
+ {
+ if (textBlock == null)
+ return;
+
+ Label label = Element;
+ if (label != null)
+ {
+ FormattedString formatted = label.FormattedText;
+
+ if (formatted == null)
+ {
+ textBlock.Text = label.Text ?? string.Empty;
+ }
+ else
+ {
+ textBlock.Inlines.Clear();
+
+ for (var i = 0; i < formatted.Spans.Count; i++)
+ {
+ textBlock.Inlines.Add(formatted.Spans[i].ToRun());
+ }
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WinRT/LayoutExtensions.cs b/Xamarin.Forms.Platform.WinRT/LayoutExtensions.cs
new file mode 100644
index 00000000..6991b322
--- /dev/null
+++ b/Xamarin.Forms.Platform.WinRT/LayoutExtensions.cs
@@ -0,0 +1,33 @@
+using System.Collections.Generic;
+using Windows.UI.Xaml;
+using WSize = Windows.Foundation.Size;
+
+#if WINDOWS_UWP
+
+namespace Xamarin.Forms.Platform.UWP
+#else
+
+namespace Xamarin.Forms.Platform.WinRT
+#endif
+{
+ public delegate SizeRequest? GetDesiredSizeDelegate(NativeViewWrapperRenderer renderer, double widthConstraint, double heightConstraint);
+
+ public delegate WSize? ArrangeOverrideDelegate(NativeViewWrapperRenderer renderer, WSize finalSize);
+
+ public delegate WSize? MeasureOverrideDelegate(NativeViewWrapperRenderer renderer, WSize availableSize);
+
+ public static class LayoutExtensions
+ {
+ public static void Add(this IList<View> children, FrameworkElement view, GetDesiredSizeDelegate getDesiredSizeDelegate = null, ArrangeOverrideDelegate arrangeOverrideDelegate = null,
+ MeasureOverrideDelegate measureOverrideDelegate = null)
+ {
+ children.Add(view.ToView(getDesiredSizeDelegate, arrangeOverrideDelegate, measureOverrideDelegate));
+ }
+
+ public static View ToView(this FrameworkElement view, GetDesiredSizeDelegate getDesiredSizeDelegate = null, ArrangeOverrideDelegate arrangeOverrideDelegate = null,
+ MeasureOverrideDelegate measureOverrideDelegate = null)
+ {
+ return new NativeViewWrapper(view, getDesiredSizeDelegate, arrangeOverrideDelegate, measureOverrideDelegate);
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WinRT/LayoutRenderer.cs b/Xamarin.Forms.Platform.WinRT/LayoutRenderer.cs
new file mode 100644
index 00000000..86748dc8
--- /dev/null
+++ b/Xamarin.Forms.Platform.WinRT/LayoutRenderer.cs
@@ -0,0 +1,55 @@
+using System.ComponentModel;
+using Windows.Foundation;
+using Windows.UI.Xaml;
+using Windows.UI.Xaml.Media;
+
+#if WINDOWS_UWP
+
+namespace Xamarin.Forms.Platform.UWP
+#else
+
+namespace Xamarin.Forms.Platform.WinRT
+#endif
+{
+ public class LayoutRenderer : ViewRenderer<Layout, FrameworkElement>
+ {
+ protected override void OnElementChanged(ElementChangedEventArgs<Layout> e)
+ {
+ base.OnElementChanged(e);
+
+ if (e.OldElement != null)
+ {
+ SizeChanged -= OnSizeChanged;
+ }
+
+ if (e.NewElement != null)
+ {
+ SizeChanged += OnSizeChanged;
+
+ UpdateClipToBounds();
+ }
+ }
+
+ protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ base.OnElementPropertyChanged(sender, e);
+
+ if (e.PropertyName == Layout.IsClippedToBoundsProperty.PropertyName)
+ UpdateClipToBounds();
+ }
+
+ void OnSizeChanged(object sender, SizeChangedEventArgs e)
+ {
+ UpdateClipToBounds();
+ }
+
+ void UpdateClipToBounds()
+ {
+ Clip = null;
+ if (Element.IsClippedToBounds)
+ {
+ Clip = new RectangleGeometry { Rect = new Rect(0, 0, ActualWidth, ActualHeight) };
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WinRT/ListGroupHeaderPresenter.cs b/Xamarin.Forms.Platform.WinRT/ListGroupHeaderPresenter.cs
new file mode 100644
index 00000000..45818977
--- /dev/null
+++ b/Xamarin.Forms.Platform.WinRT/ListGroupHeaderPresenter.cs
@@ -0,0 +1,46 @@
+using Windows.UI.Xaml;
+using Windows.UI.Xaml.Controls;
+using Windows.UI.Xaml.Input;
+using Windows.UI.Xaml.Media;
+
+#if WINDOWS_UWP
+
+namespace Xamarin.Forms.Platform.UWP
+#else
+
+namespace Xamarin.Forms.Platform.WinRT
+#endif
+{
+ public class ListGroupHeaderPresenter : Windows.UI.Xaml.Controls.ContentPresenter
+ {
+ void OnTapped(object sender, TappedRoutedEventArgs tappedRoutedEventArgs)
+ {
+ var element = VisualTreeHelper.GetParent(this) as FrameworkElement;
+ while (element != null)
+ {
+ var list = element as Windows.UI.Xaml.Controls.ListView;
+ if (list != null)
+ element = list.SemanticZoomOwner;
+
+ if (element == null)
+ break;
+
+ var zoom = element as SemanticZoom;
+ if (zoom != null)
+ {
+ zoom.ToggleActiveView();
+
+ var grid = zoom.ZoomedOutView as GridView;
+ if (grid != null)
+ {
+ grid.MakeVisible(new SemanticZoomLocation { Item = DataContext });
+ }
+
+ return;
+ }
+
+ element = VisualTreeHelper.GetParent(element) as FrameworkElement;
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WinRT/ListViewGroupStyleSelector.cs b/Xamarin.Forms.Platform.WinRT/ListViewGroupStyleSelector.cs
new file mode 100644
index 00000000..066f5681
--- /dev/null
+++ b/Xamarin.Forms.Platform.WinRT/ListViewGroupStyleSelector.cs
@@ -0,0 +1,18 @@
+using Windows.UI.Xaml.Controls;
+
+#if WINDOWS_UWP
+
+namespace Xamarin.Forms.Platform.UWP
+#else
+
+namespace Xamarin.Forms.Platform.WinRT
+#endif
+{
+ public class ListViewGroupStyleSelector : GroupStyleSelector
+ {
+ protected override GroupStyle SelectGroupStyleCore(object group, uint level)
+ {
+ return (GroupStyle)Windows.UI.Xaml.Application.Current.Resources["ListViewGroup"];
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WinRT/ListViewRenderer.cs b/Xamarin.Forms.Platform.WinRT/ListViewRenderer.cs
new file mode 100644
index 00000000..5c231738
--- /dev/null
+++ b/Xamarin.Forms.Platform.WinRT/ListViewRenderer.cs
@@ -0,0 +1,634 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Linq;
+using System.Threading.Tasks;
+using Windows.Foundation;
+using Windows.UI.Xaml;
+using Windows.UI.Xaml.Controls;
+using Windows.UI.Xaml.Data;
+using Windows.UI.Xaml.Input;
+using Windows.UI.Xaml.Media;
+using WListView = Windows.UI.Xaml.Controls.ListView;
+using WBinding = Windows.UI.Xaml.Data.Binding;
+using WApp = Windows.UI.Xaml.Application;
+
+#if WINDOWS_UWP
+
+namespace Xamarin.Forms.Platform.UWP
+#else
+
+namespace Xamarin.Forms.Platform.WinRT
+#endif
+{
+ public class ListViewRenderer : ViewRenderer<ListView, FrameworkElement>
+ {
+#if !WINDOWS_UWP
+ public static readonly DependencyProperty HighlightWhenSelectedProperty = DependencyProperty.RegisterAttached("HighlightWhenSelected", typeof(bool), typeof(ListViewRenderer),
+ new PropertyMetadata(false));
+
+ public static bool GetHighlightWhenSelected(DependencyObject dependencyObject)
+ {
+ return (bool)dependencyObject.GetValue(HighlightWhenSelectedProperty);
+ }
+
+ public static void SetHighlightWhenSelected(DependencyObject dependencyObject, bool value)
+ {
+ dependencyObject.SetValue(HighlightWhenSelectedProperty, value);
+ }
+#endif
+
+ protected WListView List { get; private set; }
+
+ protected override void OnElementChanged(ElementChangedEventArgs<ListView> e)
+ {
+ base.OnElementChanged(e);
+
+ if (e.OldElement != null)
+ {
+ e.OldElement.ItemSelected -= OnElementItemSelected;
+ e.OldElement.ScrollToRequested -= OnElementScrollToRequested;
+ }
+
+ if (e.NewElement != null)
+ {
+ e.NewElement.ItemSelected += OnElementItemSelected;
+ e.NewElement.ScrollToRequested += OnElementScrollToRequested;
+
+ if (List == null)
+ {
+ List = new WListView
+ {
+ IsSynchronizedWithCurrentItem = false,
+ ItemTemplate = (Windows.UI.Xaml.DataTemplate)WApp.Current.Resources["CellTemplate"],
+ HeaderTemplate = (Windows.UI.Xaml.DataTemplate)WApp.Current.Resources["View"],
+ FooterTemplate = (Windows.UI.Xaml.DataTemplate)WApp.Current.Resources["View"],
+ ItemContainerStyle = (Windows.UI.Xaml.Style)WApp.Current.Resources["FormsListViewItem"],
+ GroupStyleSelector = (GroupStyleSelector)WApp.Current.Resources["ListViewGroupSelector"]
+ };
+
+ // In order to support tapping on elements within a list item, we handle
+ // ListView.Tapped (which can be handled by child elements in the list items
+ // and prevented from bubbling up) rather than ListView.ItemClick
+ List.Tapped += ListOnTapped;
+
+ if (ShouldCustomHighlight)
+ {
+ List.SelectionChanged += OnControlSelectionChanged;
+ }
+
+ List.SetBinding(ItemsControl.ItemsSourceProperty, "");
+ }
+
+ // WinRT throws an exception if you set ItemsSource directly to a CVS, so bind it.
+ List.DataContext = new CollectionViewSource { Source = Element.ItemsSource, IsSourceGrouped = Element.IsGroupingEnabled };
+
+ UpdateGrouping();
+ UpdateHeader();
+ UpdateFooter();
+ ClearSizeEstimate();
+ }
+ }
+
+ protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ base.OnElementPropertyChanged(sender, e);
+
+ if (e.PropertyName == ListView.IsGroupingEnabledProperty.PropertyName)
+ {
+ UpdateGrouping();
+ }
+ else if (e.PropertyName == ListView.HeaderProperty.PropertyName)
+ {
+ UpdateHeader();
+ }
+ else if (e.PropertyName == ListView.FooterProperty.PropertyName)
+ {
+ UpdateFooter();
+ }
+ else if (e.PropertyName == ListView.RowHeightProperty.PropertyName)
+ {
+ ClearSizeEstimate();
+ }
+ else if (e.PropertyName == ListView.HasUnevenRowsProperty.PropertyName)
+ {
+ ClearSizeEstimate();
+ }
+ else if (e.PropertyName == ListView.ItemTemplateProperty.PropertyName)
+ {
+ ClearSizeEstimate();
+ }
+ else if (e.PropertyName == ListView.ItemsSourceProperty.PropertyName)
+ {
+ ClearSizeEstimate();
+ ((CollectionViewSource)List.DataContext).Source = Element.ItemsSource;
+ }
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ if (List != null)
+ {
+ List.Tapped -= ListOnTapped;
+
+ if (ShouldCustomHighlight)
+ {
+ List.SelectionChanged -= OnControlSelectionChanged;
+ }
+
+ List.DataContext = null;
+ List = null;
+ }
+
+ if (_zoom != null)
+ {
+ _zoom.ViewChangeCompleted -= OnViewChangeCompleted;
+ _zoom = null;
+ }
+
+ base.Dispose(disposing);
+ }
+
+ static IEnumerable<T> FindDescendants<T>(DependencyObject dobj) where T : DependencyObject
+ {
+ int count = VisualTreeHelper.GetChildrenCount(dobj);
+ for (var i = 0; i < count; i++)
+ {
+ DependencyObject element = VisualTreeHelper.GetChild(dobj, i);
+ if (element is T)
+ yield return (T)element;
+
+ foreach (T descendant in FindDescendants<T>(element))
+ yield return descendant;
+ }
+ }
+
+ sealed class BrushedElement
+ {
+ bool _isBound;
+
+ public BrushedElement(FrameworkElement element, WBinding brushBinding = null, Brush brush = null)
+ {
+ Element = element;
+ BrushBinding = brushBinding;
+ Brush = brush;
+ }
+
+ public Brush Brush { get; }
+
+ public WBinding BrushBinding { get; }
+
+ public FrameworkElement Element { get; }
+
+ public bool IsBound
+ {
+ get { return BrushBinding != null; }
+ }
+ }
+
+ SemanticZoom _zoom;
+ ScrollViewer _scrollViewer;
+ ContentControl _headerControl;
+ readonly List<BrushedElement> _highlightedElements = new List<BrushedElement>();
+
+ bool ShouldCustomHighlight
+ {
+ get
+ {
+#if WINDOWS_UWP
+ return false;
+#else
+ return Device.Idiom == TargetIdiom.Phone;
+#endif
+ }
+ }
+
+ void ClearSizeEstimate()
+ {
+ Element.ClearValue(CellControl.MeasuredEstimateProperty);
+ }
+
+ void UpdateFooter()
+ {
+ List.Footer = ((IListViewController)Element).FooterElement;
+ }
+
+ void UpdateHeader()
+ {
+ List.Header = ((IListViewController)Element).HeaderElement;
+ }
+
+ void UpdateGrouping()
+ {
+ bool grouping = Element.IsGroupingEnabled;
+
+ ((CollectionViewSource)List.DataContext).IsSourceGrouped = grouping;
+
+ if (grouping && Element.TemplatedItems.ShortNames != null)
+ {
+ if (_zoom == null)
+ {
+ ScrollViewer.SetIsVerticalScrollChainingEnabled(List, false);
+
+ var grid = new GridView { ItemsSource = Element.TemplatedItems.ShortNames, Style = (Windows.UI.Xaml.Style)WApp.Current.Resources["JumpListGrid"] };
+
+ ScrollViewer.SetIsHorizontalScrollChainingEnabled(grid, false);
+
+ _zoom = new SemanticZoom { IsZoomOutButtonEnabled = false, ZoomedOutView = grid };
+
+ // Since we reuse our ScrollTo, we have to wait until the change completes or ChangeView has odd behavior.
+ _zoom.ViewChangeCompleted += OnViewChangeCompleted;
+
+ // Specific order to let SNC unparent the ListView for us
+ SetNativeControl(_zoom);
+ _zoom.ZoomedInView = List;
+ }
+ else
+ {
+ _zoom.CanChangeViews = true;
+ }
+ }
+ else
+ {
+ if (_zoom != null)
+ _zoom.CanChangeViews = false;
+ else if (List != Control)
+ SetNativeControl(List);
+ }
+ }
+
+ async void OnViewChangeCompleted(object sender, SemanticZoomViewChangedEventArgs e)
+ {
+ if (e.IsSourceZoomedInView)
+ return;
+
+ // HACK: Technically more than one short name could be the same, this will potentially find the wrong one in that case
+ var item = (string)e.SourceItem.Item;
+
+ int index = Element.TemplatedItems.ShortNames.IndexOf(item);
+ if (index == -1)
+ return;
+
+ TemplatedItemsList<ItemsView<Cell>, Cell> til = Element.TemplatedItems.GetGroup(index);
+ if (til.Count == 0)
+ return; // FIXME
+
+ // Delay until after the SemanticZoom change _actually_ finishes, fixes tons of odd issues on Phone w/ virtualization.
+ if (Device.Idiom == TargetIdiom.Phone)
+ await Task.Delay(1);
+
+ ScrollTo(til.ListProxy.ProxiedEnumerable, til.ListProxy[0], ScrollToPosition.Start, true, true);
+ }
+
+ async void ScrollTo(object group, object item, ScrollToPosition toPosition, bool shouldAnimate, bool includeGroup = false, bool previouslyFailed = false)
+ {
+ ScrollViewer viewer = GetScrollViewer();
+ if (viewer == null)
+ {
+ RoutedEventHandler loadedHandler = null;
+ loadedHandler = async (o, e) =>
+ {
+ List.Loaded -= loadedHandler;
+
+ // Here we try to avoid an exception, see explanation at bottom
+ await Dispatcher.RunIdleAsync(args => { ScrollTo(group, item, toPosition, shouldAnimate, includeGroup); });
+ };
+ List.Loaded += loadedHandler;
+ return;
+ }
+
+ Tuple<int, int> location = Element.TemplatedItems.GetGroupAndIndexOfItem(group, item);
+ if (location.Item1 == -1 || location.Item2 == -1)
+ return;
+
+ object[] t = Element.TemplatedItems.GetGroup(location.Item1).ItemsSource.Cast<object>().ToArray();
+ object c = t[location.Item2];
+
+ double viewportHeight = viewer.ViewportHeight;
+
+ var semanticLocation = new SemanticZoomLocation { Item = c };
+
+ switch (toPosition)
+ {
+ case ScrollToPosition.Start:
+ {
+ List.ScrollIntoView(c, ScrollIntoViewAlignment.Leading);
+ return;
+ }
+
+ case ScrollToPosition.MakeVisible:
+ {
+ List.ScrollIntoView(c, ScrollIntoViewAlignment.Default);
+ return;
+ }
+
+ case ScrollToPosition.End:
+ case ScrollToPosition.Center:
+ {
+ var content = (FrameworkElement)List.ItemTemplate.LoadContent();
+ content.DataContext = c;
+ content.Measure(new Windows.Foundation.Size(viewer.ActualWidth, double.PositiveInfinity));
+
+ double tHeight = content.DesiredSize.Height;
+
+ if (toPosition == ScrollToPosition.Center)
+ semanticLocation.Bounds = new Rect(0, viewportHeight / 2 - tHeight / 2, 0, 0);
+ else
+ semanticLocation.Bounds = new Rect(0, viewportHeight - tHeight, 0, 0);
+
+ break;
+ }
+ }
+
+ // Waiting for loaded doesn't seem to be enough anymore; the ScrollViewer does not appear until after Loaded.
+ // Even if the ScrollViewer is present, an invoke at low priority fails (E_FAIL) presumably because the items are
+ // still loading. An invoke at idle sometimes work, but isn't reliable enough, so we'll just have to commit
+ // treason and use a blanket catch for the E_FAIL and try again.
+ try
+ {
+ List.MakeVisible(semanticLocation);
+ }
+ catch (Exception)
+ {
+ if (previouslyFailed)
+ return;
+
+ Task.Delay(1).ContinueWith(ct => { ScrollTo(group, item, toPosition, shouldAnimate, includeGroup, true); }, TaskScheduler.FromCurrentSynchronizationContext()).WatchForError();
+ }
+ }
+
+ void OnElementScrollToRequested(object sender, ScrollToRequestedEventArgs e)
+ {
+ ScrollTo(e.Group, e.Item, e.Position, e.ShouldAnimate);
+ }
+
+ T GetFirstDescendant<T>(DependencyObject element) where T : FrameworkElement
+ {
+ int count = VisualTreeHelper.GetChildrenCount(element);
+ for (var i = 0; i < count; i++)
+ {
+ DependencyObject child = VisualTreeHelper.GetChild(element, i);
+
+ T target = child as T ?? GetFirstDescendant<T>(child);
+ if (target != null)
+ return target;
+ }
+
+ return null;
+ }
+
+ ContentControl GetHeaderControl()
+ {
+ if (_headerControl == null)
+ {
+ ScrollViewer viewer = GetScrollViewer();
+ if (viewer == null)
+ return null;
+
+ var presenter = GetFirstDescendant<ItemsPresenter>(viewer);
+ if (presenter == null)
+ return null;
+
+ _headerControl = GetFirstDescendant<ContentControl>(presenter);
+ }
+
+ return _headerControl;
+ }
+
+ ScrollViewer GetScrollViewer()
+ {
+ if (_scrollViewer == null)
+ {
+ _scrollViewer = List.GetFirstDescendant<ScrollViewer>();
+ }
+
+ return _scrollViewer;
+ }
+
+ void OnElementItemSelected(object sender, SelectedItemChangedEventArgs e)
+ {
+ if (Element == null)
+ return;
+
+ if (_deferSelection)
+ {
+ // If we get more than one of these, that's okay; we only want the latest one
+ _deferredSelectedItemChangedEvent = new Tuple<object, SelectedItemChangedEventArgs>(sender, e);
+ return;
+ }
+
+ if (e.SelectedItem == null)
+ {
+ List.SelectedIndex = -1;
+ return;
+ }
+
+ var index = 0;
+ if (Element.IsGroupingEnabled)
+ {
+ int selectedItemIndex = Element.TemplatedItems.GetGlobalIndexOfItem(e.SelectedItem);
+ var leftOver = 0;
+ int groupIndex = Element.TemplatedItems.GetGroupIndexFromGlobal(selectedItemIndex, out leftOver);
+
+ index = selectedItemIndex - (groupIndex + 1);
+ }
+ else
+ {
+ index = Element.TemplatedItems.GetGlobalIndexOfItem(e.SelectedItem);
+ }
+
+ List.SelectedIndex = index;
+ }
+
+ void ListOnTapped(object sender, TappedRoutedEventArgs args)
+ {
+ var orig = args.OriginalSource as DependencyObject;
+ int index = -1;
+
+ // Work our way up the tree until we find the actual list item
+ // the user tapped on
+ while (orig != null && orig != List)
+ {
+ var lv = orig as ListViewItem;
+
+ if (lv != null)
+ {
+ index = Element.TemplatedItems.GetGlobalIndexOfItem(lv.Content);
+ break;
+ }
+
+ orig = VisualTreeHelper.GetParent(orig);
+ }
+
+ if (index > -1)
+ {
+ OnListItemClicked(index);
+ }
+ }
+
+ void OnListItemClicked(int index)
+ {
+#if !WINDOWS_UWP
+ // If we're on the phone , we need to cache the selected item in case the handler
+ // we're about to call changes any item indexes;
+ // in some cases, those index changes will throw an exception we can't catch if
+ // the listview has an item selected
+ object selectedItem = null;
+ if (Device.Idiom == TargetIdiom.Phone)
+ {
+ selectedItem = List.SelectedItem;
+ List.SelectedIndex = -1;
+ _deferSelection = true;
+ }
+#endif
+
+ Element.NotifyRowTapped(index);
+
+#if !WINDOWS_UWP
+
+ if (Device.Idiom != TargetIdiom.Phone || List == null)
+ {
+ return;
+ }
+
+ _deferSelection = false;
+
+ if (_deferredSelectedItemChangedEvent != null)
+ {
+ // If there was a selection change attempt while RowTapped was being handled, replay it
+ OnElementItemSelected(_deferredSelectedItemChangedEvent.Item1, _deferredSelectedItemChangedEvent.Item2);
+ _deferredSelectedItemChangedEvent = null;
+ }
+ else if (List?.SelectedIndex == -1 && selectedItem != null)
+ {
+ // Otherwise, set the selection back to whatever it was before all this started
+ List.SelectedItem = selectedItem;
+ }
+#endif
+ }
+
+ void OnControlSelectionChanged(object sender, SelectionChangedEventArgs e)
+ {
+ RestorePreviousSelectedVisual();
+
+ if (e.AddedItems.Count == 0)
+ return;
+
+ object cell = e.AddedItems[0];
+ if (cell == null)
+ return;
+
+ if (ShouldCustomHighlight)
+ {
+ FrameworkElement element = FindElement(cell);
+ if (element != null)
+ {
+ SetSelectedVisual(element);
+ }
+ }
+ }
+
+ FrameworkElement FindElement(object cell)
+ {
+ foreach (CellControl selector in FindDescendants<CellControl>(List))
+ {
+ if (ReferenceEquals(cell, selector.DataContext))
+ return selector;
+ }
+
+ return null;
+ }
+
+#if WINDOWS_UWP
+ void RestorePreviousSelectedVisual()
+ {
+ }
+
+ void SetSelectedVisual(FrameworkElement element)
+ {
+ }
+#else
+ void RestorePreviousSelectedVisual()
+ {
+ foreach (BrushedElement highlight in _highlightedElements)
+ {
+ if (highlight.IsBound)
+ {
+ highlight.Element.SetForeground(highlight.BrushBinding);
+ }
+ else
+ {
+ highlight.Element.SetForeground(highlight.Brush);
+ }
+ }
+
+ _highlightedElements.Clear();
+ }
+
+ void SetSelectedVisual(FrameworkElement element)
+ {
+ // Find all labels in children and set their foreground color to accent color
+ IEnumerable<FrameworkElement> elementsToHighlight = FindPhoneHighlights(element);
+ var systemAccentBrush = (Brush)WApp.Current.Resources["SystemColorControlAccentBrush"];
+
+ foreach (FrameworkElement toHighlight in elementsToHighlight)
+ {
+ Brush brush = null;
+ WBinding binding = toHighlight.GetForegroundBinding();
+ if (binding == null)
+ brush = toHighlight.GetForeground();
+
+ var brushedElement = new BrushedElement(toHighlight, binding, brush);
+ _highlightedElements.Add(brushedElement);
+
+ toHighlight.SetForeground(systemAccentBrush);
+ }
+ }
+
+ IEnumerable<FrameworkElement> FindPhoneHighlights(FrameworkElement element)
+ {
+ FrameworkElement parent = element;
+ while (true)
+ {
+ element = parent;
+ if (element is CellControl)
+ break;
+
+ parent = VisualTreeHelper.GetParent(element) as FrameworkElement;
+ if (parent == null)
+ {
+ parent = element;
+ break;
+ }
+ }
+
+ return FindPhoneHighlightCore(parent);
+ }
+
+ IEnumerable<FrameworkElement> FindPhoneHighlightCore(DependencyObject element)
+ {
+ int children = VisualTreeHelper.GetChildrenCount(element);
+ for (var i = 0; i < children; i++)
+ {
+ DependencyObject child = VisualTreeHelper.GetChild(element, i);
+
+ var label = child as LabelRenderer;
+ var childElement = child as FrameworkElement;
+ if (childElement != null && (GetHighlightWhenSelected(childElement) || label != null))
+ {
+ if (label != null)
+ yield return label.Control;
+ else
+ yield return childElement;
+ }
+
+ foreach (FrameworkElement recursedElement in FindPhoneHighlightCore(childElement))
+ yield return recursedElement;
+ }
+ }
+#endif
+
+ bool _deferSelection;
+ Tuple<object, SelectedItemChangedEventArgs> _deferredSelectedItemChangedEvent;
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WinRT/MasterBackgroundConverter.cs b/Xamarin.Forms.Platform.WinRT/MasterBackgroundConverter.cs
new file mode 100644
index 00000000..aa82f56d
--- /dev/null
+++ b/Xamarin.Forms.Platform.WinRT/MasterBackgroundConverter.cs
@@ -0,0 +1,75 @@
+using System;
+using Windows.UI;
+using Windows.UI.Xaml;
+using Windows.UI.Xaml.Controls;
+using Windows.UI.Xaml.Media;
+
+#if WINDOWS_UWP
+
+namespace Xamarin.Forms.Platform.UWP
+#else
+
+namespace Xamarin.Forms.Platform.WinRT
+#endif
+{
+ public class MasterBackgroundConverter : Windows.UI.Xaml.Data.IValueConverter
+ {
+ // Obtained by comparing the Mail apps master section background to the detail background
+ const double Shift = 0.03;
+
+ public object Convert(object value, Type targetType, object parameter, string language)
+ {
+ SolidColorBrush brush = null;
+
+ var element = value as FrameworkElement;
+ if (element != null)
+ {
+ while (brush == null && element != null)
+ {
+ DependencyProperty property = GetBackgroundProperty(element);
+ if (property != null)
+ {
+ value = element.GetValue(property);
+ brush = value as SolidColorBrush;
+ if (brush != null && brush.Color == Colors.Transparent)
+ brush = null;
+ }
+
+ element = VisualTreeHelper.GetParent(element) as FrameworkElement;
+ }
+ }
+
+ brush = value as SolidColorBrush;
+ if (brush != null)
+ {
+ Windows.UI.Color wcolor = brush.Color;
+ Color color = Color.FromRgba(wcolor.R, wcolor.G, wcolor.B, wcolor.A);
+
+ double delta = Shift;
+ if (color.Luminosity > .6)
+ delta = -Shift;
+
+ color = color.AddLuminosity(delta);
+
+ return new SolidColorBrush(color.ToWindowsColor());
+ }
+
+ return null;
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter, string language)
+ {
+ throw new NotImplementedException();
+ }
+
+ static DependencyProperty GetBackgroundProperty(FrameworkElement element)
+ {
+ if (element is Control)
+ return Control.BackgroundProperty;
+ if (element is Panel)
+ return Panel.BackgroundProperty;
+
+ return null;
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WinRT/MasterDetailControl.cs b/Xamarin.Forms.Platform.WinRT/MasterDetailControl.cs
new file mode 100644
index 00000000..c325b42a
--- /dev/null
+++ b/Xamarin.Forms.Platform.WinRT/MasterDetailControl.cs
@@ -0,0 +1,168 @@
+using System;
+using Windows.UI.Xaml;
+using Windows.UI.Xaml.Controls;
+using Windows.UI.Xaml.Controls.Primitives;
+using Windows.UI.Xaml.Media;
+
+namespace Xamarin.Forms.Platform.WinRT
+{
+ public class MasterDetailControl : Control
+ {
+ public static readonly DependencyProperty MasterContentProperty = DependencyProperty.Register("MasterContent", typeof(object), typeof(MasterDetailControl),
+ new PropertyMetadata(null, (d, e) => ((MasterDetailControl)d).UpdateMaster()));
+
+ public static readonly DependencyProperty DetailContentProperty = DependencyProperty.Register("DetailContent", typeof(object), typeof(MasterDetailControl),
+ new PropertyMetadata(null, (d, e) => ((MasterDetailControl)d).UpdateDetail()));
+
+ public static readonly DependencyProperty IsMasterVisibleProperty = DependencyProperty.Register("IsMasterVisible", typeof(bool), typeof(MasterDetailControl),
+ new PropertyMetadata(false, (d, e) => ((MasterDetailControl)d).UpdateMaster()));
+
+ public static readonly DependencyProperty TitleProperty = DependencyProperty.Register("Title", typeof(string), typeof(MasterDetailControl),
+ new PropertyMetadata("", (d, e) => ((MasterDetailControl)d).UpdateTitle()));
+
+ public static readonly DependencyProperty DetailTitleVisibilityProperty = DependencyProperty.Register("DetailTitleVisibility", typeof(Visibility), typeof(MasterDetailControl),
+ new PropertyMetadata(Visibility.Collapsed, (d, e) => ((MasterDetailControl)d).UpdateToolbar()));
+
+ public static readonly DependencyProperty ToolbarForegroundProperty = DependencyProperty.Register("ToolbarForeground", typeof(Brush), typeof(MasterDetailControl),
+ new PropertyMetadata(default(Brush), (d, e) => ((MasterDetailControl)d).UpdateToolbar()));
+
+ public static readonly DependencyProperty ToolbarBackgroundProperty = DependencyProperty.Register("ToolbarBackground", typeof(Brush), typeof(MasterDetailControl),
+ new PropertyMetadata(default(Brush), (d, e) => ((MasterDetailControl)d).UpdateToolbar()));
+
+ Windows.UI.Xaml.Controls.Grid _grd;
+
+ Windows.UI.Xaml.Controls.ContentPresenter _masterPresenter;
+ Windows.UI.Xaml.Controls.ContentPresenter _detailPresenter;
+ Popup _popup;
+ TextBlock _txbTitle;
+
+ public object DetailContent
+ {
+ get { return GetValue(DetailContentProperty); }
+ set { SetValue(DetailContentProperty, value); }
+ }
+
+ public Visibility DetailTitleVisibility
+ {
+ get { return (Visibility)GetValue(DetailTitleVisibilityProperty); }
+ set { SetValue(DetailTitleVisibilityProperty, value); }
+ }
+
+ public bool IsMasterVisible
+ {
+ get { return (bool)GetValue(IsMasterVisibleProperty); }
+ set { SetValue(IsMasterVisibleProperty, value); }
+ }
+
+ public object MasterContent
+ {
+ get { return GetValue(MasterContentProperty); }
+ set { SetValue(MasterContentProperty, value); }
+ }
+
+ public string Title
+ {
+ get { return (string)GetValue(TitleProperty); }
+ set { SetValue(TitleProperty, value); }
+ }
+
+ public Brush ToolbarBackground
+ {
+ get { return (Brush)GetValue(ToolbarBackgroundProperty); }
+ set { SetValue(ToolbarBackgroundProperty, value); }
+ }
+
+ public Brush ToolbarForeground
+ {
+ get { return (Brush)GetValue(ToolbarForegroundProperty); }
+ set { SetValue(ToolbarForegroundProperty, value); }
+ }
+
+ public event EventHandler UserClosedPopover;
+
+ protected override void OnApplyTemplate()
+ {
+ if (_masterPresenter != null)
+ {
+ // Despite itself being unparented when the template changes, the presenters' children still think they're
+ // parented and so the new presenters throw when the content is assigned to them.
+ _masterPresenter.Content = null;
+ _masterPresenter = null;
+ }
+
+ if (_detailPresenter != null)
+ {
+ _detailPresenter.Content = null;
+ _detailPresenter = null;
+ }
+
+ if (_popup != null)
+ {
+ _popup.Closed -= OnPopupClosed;
+ _popup = null;
+ }
+
+ base.OnApplyTemplate();
+
+ _masterPresenter = GetTemplateChild("masterPresenter") as Windows.UI.Xaml.Controls.ContentPresenter;
+ _detailPresenter = GetTemplateChild("detailPresenter") as Windows.UI.Xaml.Controls.ContentPresenter;
+ _txbTitle = GetTemplateChild("txbTitle") as TextBlock;
+ _grd = GetTemplateChild("grdToolbar") as Windows.UI.Xaml.Controls.Grid;
+
+ _popup = GetTemplateChild("popup") as Popup;
+ if (_popup != null)
+ _popup.Closed += OnPopupClosed;
+
+ UpdateMaster();
+ UpdateDetail();
+ UpdateTitle();
+ UpdateToolbar();
+ }
+
+ void OnPopupClosed(object sender, object e)
+ {
+ EventHandler closed = UserClosedPopover;
+ if (closed != null)
+ closed(this, EventArgs.Empty);
+ }
+
+ void UpdateDetail()
+ {
+ if (_detailPresenter == null)
+ return;
+
+ _detailPresenter.Content = DetailContent;
+ }
+
+ void UpdateMaster()
+ {
+ if (_masterPresenter == null)
+ return;
+
+ bool visible = IsMasterVisible;
+ _masterPresenter.Visibility = visible ? Visibility.Visible : Visibility.Collapsed;
+ _masterPresenter.Content = MasterContent;
+
+ if (_popup != null)
+ _popup.IsOpen = visible;
+ }
+
+ void UpdateTitle()
+ {
+ if (_txbTitle == null)
+ return;
+
+ _txbTitle.Text = Title ?? "";
+ }
+
+ void UpdateToolbar()
+ {
+ if (_txbTitle == null)
+ return;
+
+ _grd.Visibility = DetailTitleVisibility;
+ _grd.Background = ToolbarBackground;
+ _txbTitle.Foreground = ToolbarForeground;
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WinRT/MasterDetailPageRenderer.cs b/Xamarin.Forms.Platform.WinRT/MasterDetailPageRenderer.cs
new file mode 100644
index 00000000..314cc794
--- /dev/null
+++ b/Xamarin.Forms.Platform.WinRT/MasterDetailPageRenderer.cs
@@ -0,0 +1,291 @@
+using System;
+using System.ComponentModel;
+using Windows.UI.Xaml;
+using Windows.UI.Xaml.Controls;
+using Windows.UI.Xaml.Media;
+
+namespace Xamarin.Forms.Platform.WinRT
+{
+ public class MasterDetailPageRenderer : IVisualElementRenderer, ITitleProvider
+ {
+ MasterDetailControl _container;
+
+ bool _disposed;
+ IVisualElementRenderer _masterRenderer;
+ IVisualElementRenderer _detailRenderer;
+ bool _showTitle;
+ VisualElementTracker<Page, PageControl> _tracker;
+
+ public MasterDetailPage Element { get; private set; }
+
+ protected VisualElementTracker<Page, PageControl> Tracker
+ {
+ get { return _tracker; }
+ set
+ {
+ if (_tracker == value)
+ return;
+
+ if (_tracker != null)
+ {
+ _tracker.Dispose();
+ }
+
+ _tracker = value;
+ }
+ }
+
+ bool IsPopoverFullScreen
+ {
+ get { return Device.Idiom == TargetIdiom.Phone; }
+ }
+
+ public void Dispose()
+ {
+ Dispose(true);
+ }
+
+ Brush ITitleProvider.BarBackgroundBrush
+ {
+ set { _container.ToolbarBackground = value; }
+ }
+
+ Brush ITitleProvider.BarForegroundBrush
+ {
+ set { _container.ToolbarForeground = value; }
+ }
+
+ bool ITitleProvider.ShowTitle
+ {
+ get { return _showTitle; }
+
+ set
+ {
+ if (_showTitle == value)
+ return;
+ _showTitle = value;
+ if (_showTitle)
+ _container.DetailTitleVisibility = Visibility.Visible;
+ else
+ _container.DetailTitleVisibility = Visibility.Collapsed;
+ }
+ }
+
+ string ITitleProvider.Title
+ {
+ get { return _container.Title; }
+
+ set { _container.Title = value; }
+ }
+
+ public FrameworkElement ContainerElement
+ {
+ get { return _container; }
+ }
+
+ VisualElement IVisualElementRenderer.Element
+ {
+ get { return Element; }
+ }
+
+ public event EventHandler<VisualElementChangedEventArgs> ElementChanged;
+
+ public SizeRequest GetDesiredSize(double widthConstraint, double heightConstraint)
+ {
+ return new SizeRequest(new Size(Device.Info.ScaledScreenSize.Width, Device.Info.ScaledScreenSize.Height));
+ }
+
+ public void SetElement(VisualElement element)
+ {
+ if (element != null && !(element is MasterDetailPage))
+ throw new ArgumentException("Element must be a Page", "element");
+
+ MasterDetailPage oldElement = Element;
+ Element = (MasterDetailPage)element;
+
+ if (oldElement != null)
+ {
+ oldElement.PropertyChanged -= OnElementPropertyChanged;
+ }
+
+ if (element != null)
+ {
+ if (_container == null)
+ {
+ _container = new MasterDetailControl();
+ _container.UserClosedPopover += OnUserClosedPopover;
+ _container.SizeChanged += OnNativeSizeChanged;
+
+ Tracker = new BackgroundTracker<PageControl>(Control.BackgroundProperty) { Element = (Page)element, Container = _container };
+
+ _container.Loaded += OnLoaded;
+ _container.Unloaded += OnUnloaded;
+ }
+
+ element.PropertyChanged += OnElementPropertyChanged;
+ UpdateBehavior();
+ SetMaster(Element.Master);
+ SetDetail(Element.Detail);
+ UpdateIsPresented();
+ }
+
+ OnElementChanged(new VisualElementChangedEventArgs(oldElement, element));
+ }
+
+ protected virtual void Dispose(bool disposing)
+ {
+ if (!disposing || _disposed)
+ return;
+
+ _disposed = true;
+
+ SetElement(null);
+ }
+
+ protected void OnElementChanged(VisualElementChangedEventArgs e)
+ {
+ EventHandler<VisualElementChangedEventArgs> changed = ElementChanged;
+ if (changed != null)
+ changed(this, e);
+ }
+
+ protected virtual void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ if (e.PropertyName == "Master")
+ SetMaster(Element.Master);
+ else if (e.PropertyName == "Detail")
+ SetDetail(Element.Detail);
+ else if (e.PropertyName == MasterDetailPage.IsPresentedProperty.PropertyName)
+ UpdateIsPresented();
+ else if (e.PropertyName == MasterDetailPage.MasterBehaviorProperty.PropertyName)
+ UpdateBehavior();
+ else if (e.PropertyName == Page.TitleProperty.PropertyName)
+ UpdateTitle();
+ }
+
+ bool GetIsMasterAPopover()
+ {
+ // TODO: Support tablet being shrunk to a very small size
+ return !Element.ShouldShowSplitMode;
+ }
+
+ void OnLoaded(object sender, RoutedEventArgs args)
+ {
+ if (Element == null)
+ return;
+
+ Element.SendAppearing();
+ }
+
+ void OnNativeSizeChanged(object sender, SizeChangedEventArgs e)
+ {
+ UpdateBounds(e.NewSize);
+ }
+
+ void OnUnloaded(object sender, RoutedEventArgs args)
+ {
+ if (Element == null)
+ return;
+
+ Element.SendDisappearing();
+ }
+
+ void OnUserClosedPopover(object sender, EventArgs e)
+ {
+ if (Element != null)
+ ((IElementController)Element).SetValueFromRenderer(MasterDetailPage.IsPresentedProperty, false);
+ }
+
+ void SetDetail(Page detailPage)
+ {
+ ((ITitleProvider)this).ShowTitle = detailPage is NavigationPage;
+
+ if (_detailRenderer != null)
+ {
+ FrameworkElement native = _detailRenderer.ContainerElement;
+ _container.DetailContent = null;
+ _detailRenderer = null;
+ }
+
+ if (detailPage == null)
+ return;
+
+ _detailRenderer = detailPage.GetOrCreateRenderer();
+ _container.DetailContent = _detailRenderer.ContainerElement;
+ UpdateTitle();
+ }
+
+ void SetMaster(Page masterPage)
+ {
+ if (_masterRenderer != null)
+ {
+ FrameworkElement native = _masterRenderer.ContainerElement;
+ _container.MasterContent = null;
+ _masterRenderer = null;
+ }
+
+ if (masterPage == null)
+ return;
+
+ _masterRenderer = masterPage.GetOrCreateRenderer();
+ _container.MasterContent = _masterRenderer.ContainerElement;
+ }
+
+ void UpdateBehavior()
+ {
+ string key = GetIsMasterAPopover() ? "MasterDetailPopup" : "MasterDetailSplit";
+ _container.Template = (Windows.UI.Xaml.Controls.ControlTemplate)Windows.UI.Xaml.Application.Current.Resources[key];
+ }
+
+ void UpdateBounds(bool isPresented)
+ {
+ UpdateBounds(new Windows.Foundation.Size(_container.ActualWidth, _container.ActualHeight), isPresented);
+ }
+
+ void UpdateBounds(Windows.Foundation.Size constraint)
+ {
+ UpdateBounds(constraint, Element.IsPresented);
+ }
+
+ void UpdateBounds(Windows.Foundation.Size constraint, bool isPresented)
+ {
+ if (constraint.Width <= 0 || constraint.Height <= 0)
+ return;
+
+ bool isPopover = GetIsMasterAPopover();
+ double masterWidth = 0;
+ if (isPresented || isPopover)
+ {
+ if (isPopover && IsPopoverFullScreen)
+ masterWidth = constraint.Width;
+ else
+ masterWidth = constraint.Width * .3;
+ }
+
+ double detailWidth = constraint.Width;
+ if (!isPopover)
+ detailWidth -= masterWidth;
+
+ Element.MasterBounds = new Rectangle(0, 0, masterWidth, constraint.Height);
+ Element.DetailBounds = new Rectangle(0, 0, detailWidth, constraint.Height);
+ }
+
+ void UpdateIsPresented()
+ {
+ UpdateBehavior();
+
+ bool isPresented = !GetIsMasterAPopover() || Element.IsPresented;
+ _container.IsMasterVisible = isPresented;
+
+ UpdateBounds(isPresented);
+ }
+
+ void UpdateTitle()
+ {
+ if (Element?.Detail == null)
+ return;
+
+ ((ITitleProvider)this).Title = (Element.Detail as NavigationPage)?.CurrentPage?.Title ?? Element.Title ?? Element?.Title;
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WinRT/MenuItemCommand.cs b/Xamarin.Forms.Platform.WinRT/MenuItemCommand.cs
new file mode 100644
index 00000000..31574b44
--- /dev/null
+++ b/Xamarin.Forms.Platform.WinRT/MenuItemCommand.cs
@@ -0,0 +1,48 @@
+using System;
+using System.ComponentModel;
+using System.Windows.Input;
+
+#if WINDOWS_UWP
+
+namespace Xamarin.Forms.Platform.UWP
+#else
+
+namespace Xamarin.Forms.Platform.WinRT
+#endif
+{
+ internal class MenuItemCommand : ICommand
+ {
+ readonly MenuItem _menuItem;
+
+ public MenuItemCommand(MenuItem item)
+ {
+ _menuItem = item;
+ _menuItem.PropertyChanged += OnElementPropertyChanged;
+ }
+
+ public virtual bool CanExecute(object parameter)
+ {
+ return _menuItem.IsEnabled;
+ }
+
+ public event EventHandler CanExecuteChanged;
+
+ public void Execute(object parameter)
+ {
+ _menuItem.Activate();
+ }
+
+ void OnCanExecuteChanged()
+ {
+ EventHandler changed = CanExecuteChanged;
+ if (changed != null)
+ changed(this, EventArgs.Empty);
+ }
+
+ void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ if (e.PropertyName == VisualElement.IsEnabledProperty.PropertyName)
+ OnCanExecuteChanged();
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WinRT/NativeViewWrapper.cs b/Xamarin.Forms.Platform.WinRT/NativeViewWrapper.cs
new file mode 100644
index 00000000..824f0eed
--- /dev/null
+++ b/Xamarin.Forms.Platform.WinRT/NativeViewWrapper.cs
@@ -0,0 +1,30 @@
+using Windows.UI.Xaml;
+
+#if WINDOWS_UWP
+
+namespace Xamarin.Forms.Platform.UWP
+#else
+
+namespace Xamarin.Forms.Platform.WinRT
+#endif
+{
+ public class NativeViewWrapper : View
+ {
+ public NativeViewWrapper(FrameworkElement nativeElement, GetDesiredSizeDelegate getDesiredSizeDelegate = null, ArrangeOverrideDelegate arrangeOverrideDelegate = null,
+ MeasureOverrideDelegate measureOverrideDelegate = null)
+ {
+ GetDesiredSizeDelegate = getDesiredSizeDelegate;
+ ArrangeOverrideDelegate = arrangeOverrideDelegate;
+ MeasureOverrideDelegate = measureOverrideDelegate;
+ NativeElement = nativeElement;
+ }
+
+ public ArrangeOverrideDelegate ArrangeOverrideDelegate { get; set; }
+
+ public GetDesiredSizeDelegate GetDesiredSizeDelegate { get; }
+
+ public MeasureOverrideDelegate MeasureOverrideDelegate { get; set; }
+
+ public FrameworkElement NativeElement { get; }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WinRT/NativeViewWrapperRenderer.cs b/Xamarin.Forms.Platform.WinRT/NativeViewWrapperRenderer.cs
new file mode 100644
index 00000000..0e669774
--- /dev/null
+++ b/Xamarin.Forms.Platform.WinRT/NativeViewWrapperRenderer.cs
@@ -0,0 +1,74 @@
+using Windows.UI.Xaml;
+
+#if WINDOWS_UWP
+
+namespace Xamarin.Forms.Platform.UWP
+#else
+
+namespace Xamarin.Forms.Platform.WinRT
+#endif
+{
+ public class NativeViewWrapperRenderer : ViewRenderer<NativeViewWrapper, FrameworkElement>
+ {
+ public override SizeRequest GetDesiredSize(double widthConstraint, double heightConstraint)
+ {
+ if (Element?.GetDesiredSizeDelegate == null)
+ {
+ return base.GetDesiredSize(widthConstraint, heightConstraint);
+ }
+
+ // The user has specified a different implementation of GetDesiredSize
+ SizeRequest? result = Element.GetDesiredSizeDelegate(this, widthConstraint, heightConstraint);
+
+ // If the delegate returns a SizeRequest, we use it;
+ // if it returns null, fall back to the default implementation
+ return result ?? base.GetDesiredSize(widthConstraint, heightConstraint);
+ }
+
+ protected override Windows.Foundation.Size ArrangeOverride(Windows.Foundation.Size finalSize)
+ {
+ if (Element?.ArrangeOverrideDelegate == null)
+ {
+ return base.ArrangeOverride(finalSize);
+ }
+
+ // The user has specified a different implementation of ArrangeOverride
+ Windows.Foundation.Size? result = Element.ArrangeOverrideDelegate(this, finalSize);
+
+ // If the delegate returns a Size, we use it;
+ // if it returns null, fall back to the default implementation
+ return result ?? base.ArrangeOverride(finalSize);
+ }
+
+ protected Windows.Foundation.Size MeasureOverride()
+ {
+ return MeasureOverride(new Windows.Foundation.Size());
+ }
+
+ protected override Windows.Foundation.Size MeasureOverride(Windows.Foundation.Size availableSize)
+ {
+ if (Element?.MeasureOverrideDelegate == null)
+ {
+ return base.MeasureOverride(availableSize);
+ }
+
+ // The user has specified a different implementation of MeasureOverride
+ Windows.Foundation.Size? result = Element.MeasureOverrideDelegate(this, availableSize);
+
+ // If the delegate returns a Size, we use it;
+ // if it returns null, fall back to the default implementation
+ return result ?? base.MeasureOverride(availableSize);
+ }
+
+ protected override void OnElementChanged(ElementChangedEventArgs<NativeViewWrapper> e)
+ {
+ base.OnElementChanged(e);
+
+ if (e.OldElement == null)
+ {
+ SetNativeControl(Element.NativeElement);
+ Control.LayoutUpdated += (sender, args) => { Element?.InvalidateMeasure(InvalidationTrigger.MeasureChanged); };
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WinRT/NavigationPageRenderer.cs b/Xamarin.Forms.Platform.WinRT/NavigationPageRenderer.cs
new file mode 100644
index 00000000..1e48d7ea
--- /dev/null
+++ b/Xamarin.Forms.Platform.WinRT/NavigationPageRenderer.cs
@@ -0,0 +1,554 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.Specialized;
+using System.ComponentModel;
+using System.Linq;
+using System.Threading.Tasks;
+using Windows.Devices.Input;
+using Windows.UI.Input;
+using Windows.UI.Xaml;
+using Windows.UI.Xaml.Automation;
+using Windows.UI.Xaml.Controls;
+using Windows.UI.Xaml.Input;
+using Windows.UI.Xaml.Media;
+using Windows.UI.Xaml.Media.Animation;
+#if WINDOWS_UWP
+using Windows.UI.Xaml.Data;
+using Windows.UI.Core;
+
+#endif
+
+#if WINDOWS_UWP
+
+namespace Xamarin.Forms.Platform.UWP
+#else
+
+namespace Xamarin.Forms.Platform.WinRT
+#endif
+{
+ public class NavigationPageRenderer : IVisualElementRenderer, ITitleProvider, IToolbarProvider
+#if WINDOWS_UWP
+ , IToolBarForegroundBinder
+#endif
+ {
+ PageControl _container;
+ Page _currentPage;
+ Page _previousPage;
+
+ bool _disposed;
+#if WINDOWS_UWP
+ SystemNavigationManager _navManager;
+#endif
+ MasterDetailPage _parentMasterDetailPage;
+ TabbedPage _parentTabbedPage;
+ bool _showTitle = true;
+ VisualElementTracker<Page, PageControl> _tracker;
+ ContentThemeTransition _transition;
+
+ public NavigationPage Element { get; private set; }
+
+ protected VisualElementTracker<Page, PageControl> Tracker
+ {
+ get { return _tracker; }
+ set
+ {
+ if (_tracker == value)
+ return;
+
+ if (_tracker != null)
+ _tracker.Dispose();
+
+ _tracker = value;
+ }
+ }
+
+ public void Dispose()
+ {
+ Dispose(true);
+ }
+
+ Brush ITitleProvider.BarBackgroundBrush
+ {
+ set
+ {
+ _container.NavigationBarBackground = value;
+ UpdateTitleOnParents();
+ }
+ }
+
+ Brush ITitleProvider.BarForegroundBrush
+ {
+ set
+ {
+ _container.TitleBrush = value;
+ UpdateTitleOnParents();
+ }
+ }
+
+ bool ITitleProvider.ShowTitle
+ {
+ get { return _showTitle; }
+ set
+ {
+ if (_showTitle == value)
+ return;
+
+ _showTitle = value;
+ UpdateNavigationBarVisible();
+ UpdateTitleOnParents();
+ }
+ }
+
+ public string Title
+ {
+ get { return _currentPage?.Title; }
+
+ set { }
+ }
+
+ Task<CommandBar> IToolbarProvider.GetCommandBarAsync()
+ {
+ return ((IToolbarProvider)_container)?.GetCommandBarAsync();
+ }
+
+ public FrameworkElement ContainerElement
+ {
+ get { return _container; }
+ }
+
+ VisualElement IVisualElementRenderer.Element
+ {
+ get { return Element; }
+ }
+
+ public event EventHandler<VisualElementChangedEventArgs> ElementChanged;
+
+ public SizeRequest GetDesiredSize(double widthConstraint, double heightConstraint)
+ {
+ var constraint = new Windows.Foundation.Size(widthConstraint, heightConstraint);
+ IVisualElementRenderer childRenderer = Platform.GetRenderer(Element.CurrentPage);
+ FrameworkElement child = childRenderer.ContainerElement;
+
+ double oldWidth = child.Width;
+ double oldHeight = child.Height;
+
+ child.Height = double.NaN;
+ child.Width = double.NaN;
+
+ child.Measure(constraint);
+ var result = new Size(Math.Ceiling(child.DesiredSize.Width), Math.Ceiling(child.DesiredSize.Height));
+
+ child.Width = oldWidth;
+ child.Height = oldHeight;
+
+ return new SizeRequest(result);
+ }
+
+ public void SetElement(VisualElement element)
+ {
+ if (element != null && !(element is NavigationPage))
+ throw new ArgumentException("Element must be a Page", "element");
+
+ NavigationPage oldElement = Element;
+ Element = (NavigationPage)element;
+
+ if (oldElement != null)
+ {
+ oldElement.PushRequested -= OnPushRequested;
+ oldElement.PopRequested -= OnPopRequested;
+ oldElement.InternalChildren.CollectionChanged -= OnChildrenChanged;
+ oldElement.PropertyChanged -= OnElementPropertyChanged;
+ }
+
+ if (element != null)
+ {
+ if (_container == null)
+ {
+ _container = new PageControl();
+ _container.PointerPressed += OnPointerPressed;
+ _container.SizeChanged += OnNativeSizeChanged;
+ _container.BackClicked += OnBackClicked;
+
+ Tracker = new BackgroundTracker<PageControl>(Control.BackgroundProperty) { Element = (Page)element, Container = _container };
+
+ SetPage(Element.CurrentPage, false, false);
+
+ _container.Loaded += OnLoaded;
+ _container.Unloaded += OnUnloaded;
+ }
+
+ _container.DataContext = Element.CurrentPage;
+
+ UpdatePadding();
+ LookupRelevantParents();
+ UpdateTitleColor();
+ UpdateNavigationBarBackground();
+ Element.PropertyChanged += OnElementPropertyChanged;
+ Element.PushRequested += OnPushRequested;
+ Element.PopRequested += OnPopRequested;
+ Element.InternalChildren.CollectionChanged += OnChildrenChanged;
+
+ if (!string.IsNullOrEmpty(Element.AutomationId))
+ _container.SetValue(AutomationProperties.AutomationIdProperty, Element.AutomationId);
+
+ PushExistingNavigationStack();
+ }
+
+ OnElementChanged(new VisualElementChangedEventArgs(oldElement, element));
+ }
+
+ protected void Dispose(bool disposing)
+ {
+ if (!disposing || _disposed)
+ return;
+ Element?.SendDisappearing();
+ _disposed = true;
+
+ _container.PointerPressed -= OnPointerPressed;
+ _container.SizeChanged -= OnNativeSizeChanged;
+ _container.BackClicked -= OnBackClicked;
+
+ SetElement(null);
+ SetPage(null, false, true);
+ _previousPage = null;
+
+ if (_parentTabbedPage != null)
+ _parentTabbedPage.PropertyChanged -= MultiPagePropertyChanged;
+
+ if (_parentMasterDetailPage != null)
+ _parentMasterDetailPage.PropertyChanged -= MultiPagePropertyChanged;
+ }
+
+ protected void OnElementChanged(VisualElementChangedEventArgs e)
+ {
+ EventHandler<VisualElementChangedEventArgs> changed = ElementChanged;
+ if (changed != null)
+ changed(this, e);
+ }
+
+ Brush GetBarBackgroundBrush()
+ {
+#if WINDOWS_UWP
+ object defaultColor = Windows.UI.Xaml.Application.Current.Resources["SystemControlBackgroundChromeMediumLowBrush"];
+#else
+ object defaultColor = Windows.UI.Xaml.Application.Current.Resources["ApplicationPageBackgroundThemeBrush"];
+#endif
+ if (Element.BarBackgroundColor.IsDefault && defaultColor != null)
+ return (Brush)defaultColor;
+ return Element.BarBackgroundColor.ToBrush();
+ }
+
+ Brush GetBarForegroundBrush()
+ {
+ object defaultColor = Windows.UI.Xaml.Application.Current.Resources["ApplicationForegroundThemeBrush"];
+ if (Element.BarTextColor.IsDefault)
+ return (Brush)defaultColor;
+ return Element.BarTextColor.ToBrush();
+ }
+
+ Task<CommandBar> GetCommandBarAsync()
+ {
+ var platform = (Platform)Element.Platform;
+ IToolbarProvider toolbarProvider = platform.GetToolbarProvider();
+ if (toolbarProvider == null)
+ return Task.FromResult<CommandBar>(null);
+
+ return toolbarProvider.GetCommandBarAsync();
+ }
+
+ bool GetIsNavBarPossible()
+ {
+ return _showTitle;
+ }
+
+ IToolbarProvider GetToolbarProvider()
+ {
+ var platform = (Platform)Element.Platform;
+ return platform.GetToolbarProvider();
+ }
+
+ void LookupRelevantParents()
+ {
+ IEnumerable<Page> parentPages = Element.GetParentPages();
+
+ if (_parentTabbedPage != null)
+ _parentTabbedPage.PropertyChanged -= MultiPagePropertyChanged;
+ if (_parentMasterDetailPage != null)
+ _parentMasterDetailPage.PropertyChanged -= MultiPagePropertyChanged;
+
+ foreach (Page parentPage in parentPages)
+ {
+ _parentTabbedPage = parentPage as TabbedPage;
+ _parentMasterDetailPage = parentPage as MasterDetailPage;
+ }
+
+ if (_parentTabbedPage != null)
+ _parentTabbedPage.PropertyChanged += MultiPagePropertyChanged;
+ if (_parentMasterDetailPage != null)
+ _parentMasterDetailPage.PropertyChanged += MultiPagePropertyChanged;
+#if WINDOWS_UWP
+ ((ITitleProvider)this).ShowTitle = _parentTabbedPage == null && _parentMasterDetailPage == null;
+#else
+ if (Device.Idiom == TargetIdiom.Phone && _parentTabbedPage != null)
+ ((ITitleProvider)this).ShowTitle = false;
+ else
+ ((ITitleProvider)this).ShowTitle = true;
+#endif
+ UpdateTitleOnParents();
+ }
+
+ void MultiPagePropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ if (e.PropertyName == "CurrentPage" || e.PropertyName == "Detail")
+ UpdateTitleOnParents();
+ }
+
+ async void OnBackClicked(object sender, RoutedEventArgs e)
+ {
+ await Element.PopAsync();
+ }
+
+ void OnChildrenChanged(object sender, NotifyCollectionChangedEventArgs e)
+ {
+ UpdateBackButton();
+ }
+
+ void OnCurrentPagePropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ if (e.PropertyName == NavigationPage.HasBackButtonProperty.PropertyName)
+ UpdateBackButton();
+ else if (e.PropertyName == NavigationPage.BackButtonTitleProperty.PropertyName)
+ UpdateBackButtonTitle();
+ else if (e.PropertyName == NavigationPage.HasNavigationBarProperty.PropertyName)
+ UpdateNavigationBarVisible();
+ }
+
+ void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ if (e.PropertyName == NavigationPage.BarTextColorProperty.PropertyName)
+ UpdateTitleColor();
+ else if (e.PropertyName == NavigationPage.BarBackgroundColorProperty.PropertyName)
+ UpdateNavigationBarBackground();
+ else if (e.PropertyName == Page.PaddingProperty.PropertyName)
+ UpdatePadding();
+ }
+
+ void OnLoaded(object sender, RoutedEventArgs args)
+ {
+ if (Element == null)
+ return;
+
+#if WINDOWS_UWP
+ _navManager = SystemNavigationManager.GetForCurrentView();
+#endif
+ Element.SendAppearing();
+ UpdateBackButton();
+ UpdateTitleOnParents();
+ }
+
+ void OnNativeSizeChanged(object sender, SizeChangedEventArgs e)
+ {
+ UpdateContainerArea();
+ }
+
+ void OnPointerPressed(object sender, PointerRoutedEventArgs e)
+ {
+ if (e.Handled)
+ return;
+
+ PointerPoint point = e.GetCurrentPoint(_container);
+ if (point == null)
+ return;
+
+ if (point.PointerDevice.PointerDeviceType != PointerDeviceType.Mouse)
+ return;
+
+ if (point.Properties.IsXButton1Pressed)
+ {
+ e.Handled = true;
+ OnBackClicked(_container, e);
+ }
+ }
+
+ void OnPopRequested(object sender, NavigationRequestedEventArgs e)
+ {
+ var newCurrent = (Page)Element.InternalChildren[Element.InternalChildren.Count - 2];
+ SetPage(newCurrent, e.Animated, true);
+ }
+
+ void OnPushRequested(object sender, NavigationRequestedEventArgs e)
+ {
+ SetPage(e.Page, e.Animated, false);
+ }
+
+ void OnUnloaded(object sender, RoutedEventArgs args)
+ {
+ if (Element == null)
+ return;
+
+ Element.SendDisappearing();
+ }
+
+ void PushExistingNavigationStack()
+ {
+ for (int i = Element.StackCopy.Count - 1; i >= 0; i--)
+ SetPage(Element.StackCopy.ElementAt(i), false, false);
+ }
+
+ void SetPage(Page page, bool isAnimated, bool isPopping)
+ {
+ if (_currentPage != null)
+ {
+ if (isPopping)
+ _currentPage.Cleanup();
+
+ _container.Content = null;
+
+ _currentPage.PropertyChanged -= OnCurrentPagePropertyChanged;
+ }
+
+ _previousPage = _currentPage;
+ _currentPage = page;
+
+ if (page == null)
+ return;
+
+ UpdateBackButton();
+ UpdateBackButtonTitle();
+
+ page.PropertyChanged += OnCurrentPagePropertyChanged;
+
+ IVisualElementRenderer renderer = page.GetOrCreateRenderer();
+
+ UpdateNavigationBarVisible();
+ UpdateTitleOnParents();
+
+ if (isAnimated && _transition == null)
+ {
+ _transition = new ContentThemeTransition();
+ _container.ContentTransitions = new TransitionCollection();
+ }
+
+ if (!isAnimated && _transition != null)
+ _container.ContentTransitions.Remove(_transition);
+ else if (isAnimated && _container.ContentTransitions.Count == 0)
+ _container.ContentTransitions.Add(_transition);
+
+ _container.Content = renderer.ContainerElement;
+ _container.DataContext = page;
+ }
+
+ void UpdateBackButton()
+ {
+ bool showBackButton = Element.InternalChildren.Count > 1 && NavigationPage.GetHasBackButton(_currentPage);
+ _container.ShowBackButton = showBackButton;
+
+#if WINDOWS_UWP
+ if (_navManager != null)
+ {
+ _navManager.AppViewBackButtonVisibility = showBackButton ? AppViewBackButtonVisibility.Visible : AppViewBackButtonVisibility.Collapsed;
+ }
+#endif
+ }
+
+ void UpdateBackButtonTitle()
+ {
+ string title = null;
+ if (_previousPage != null)
+ title = NavigationPage.GetBackButtonTitle(_previousPage);
+
+ _container.BackButtonTitle = title;
+ }
+
+ void UpdateContainerArea()
+ {
+ Element.ContainerArea = new Rectangle(0, 0, _container.ContentWidth, _container.ContentHeight);
+ }
+
+ void UpdateNavigationBarBackground()
+ {
+ (this as ITitleProvider).BarBackgroundBrush = GetBarBackgroundBrush();
+ }
+
+ void UpdateNavigationBarVisible()
+ {
+ UpdateTitleOnParents();
+
+ bool showing = _container.ShowNavigationBar;
+ bool newValue = GetIsNavBarPossible() && NavigationPage.GetHasNavigationBar(_currentPage);
+ if (showing == newValue)
+ return;
+
+ _container.ShowNavigationBar = newValue;
+
+ // Force ContentHeight/Width to update, doesn't work from inside PageControl for some reason
+ _container.UpdateLayout();
+ UpdateContainerArea();
+ }
+
+ void UpdatePadding()
+ {
+ _container.TitleInset = Element.Padding.Left;
+ }
+
+ void UpdateTitleColor()
+ {
+ (this as ITitleProvider).BarForegroundBrush = GetBarForegroundBrush();
+ }
+
+ async void UpdateTitleOnParents()
+ {
+ if (Element == null)
+ return;
+
+ ITitleProvider render = null;
+ if (_parentTabbedPage != null)
+ {
+ render = Platform.GetRenderer(_parentTabbedPage) as ITitleProvider;
+ if (render != null)
+ render.ShowTitle = (_parentTabbedPage.CurrentPage == Element) && NavigationPage.GetHasNavigationBar(_currentPage);
+ }
+
+ if (_parentMasterDetailPage != null)
+ {
+ render = Platform.GetRenderer(_parentMasterDetailPage) as ITitleProvider;
+ if (render != null)
+ render.ShowTitle = (_parentMasterDetailPage.Detail == Element) && NavigationPage.GetHasNavigationBar(_currentPage);
+ }
+
+ if (render != null && render.ShowTitle)
+ {
+ render.Title = _currentPage.Title;
+ render.BarBackgroundBrush = GetBarBackgroundBrush();
+ render.BarForegroundBrush = GetBarForegroundBrush();
+#if WINDOWS_UWP
+ await (Element.Platform as Platform).UpdateToolbarItems();
+#endif
+ }
+ else if (_showTitle)
+ {
+#if WINDOWS_UWP
+ await (Element.Platform as Platform).UpdateToolbarItems();
+#endif
+ }
+ }
+
+#if WINDOWS_UWP
+ public void BindForegroundColor(AppBar appBar)
+ {
+ SetAppBarForegroundBinding(appBar);
+ }
+
+ public void BindForegroundColor(AppBarButton button)
+ {
+ SetAppBarForegroundBinding(button);
+ }
+
+ void SetAppBarForegroundBinding(FrameworkElement element)
+ {
+ element.SetBinding(Control.ForegroundProperty,
+ new Windows.UI.Xaml.Data.Binding { Path = new PropertyPath("TitleBrush"), Source = _container, RelativeSource = new RelativeSource { Mode = RelativeSourceMode.TemplatedParent } });
+ }
+#endif
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WinRT/PageControl.xaml b/Xamarin.Forms.Platform.WinRT/PageControl.xaml
new file mode 100644
index 00000000..1f4872e2
--- /dev/null
+++ b/Xamarin.Forms.Platform.WinRT/PageControl.xaml
@@ -0,0 +1,10 @@
+<ContentControl
+ x:Class="Xamarin.Forms.Platform.WinRT.PageControl"
+ xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ mc:Ignorable="d" d:DesignWidth="1024"
+ HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch">
+
+</ContentControl> \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WinRT/PageControl.xaml.cs b/Xamarin.Forms.Platform.WinRT/PageControl.xaml.cs
new file mode 100644
index 00000000..0991db42
--- /dev/null
+++ b/Xamarin.Forms.Platform.WinRT/PageControl.xaml.cs
@@ -0,0 +1,166 @@
+using System.Threading.Tasks;
+using Windows.UI.Xaml;
+using Windows.UI.Xaml.Controls;
+using Windows.UI.Xaml.Media;
+
+#if WINDOWS_UWP
+
+namespace Xamarin.Forms.Platform.UWP
+#else
+
+namespace Xamarin.Forms.Platform.WinRT
+#endif
+{
+ public sealed partial class PageControl : IToolbarProvider
+ {
+ public static readonly DependencyProperty InvisibleBackButtonCollapsedProperty = DependencyProperty.Register("InvisibleBackButtonCollapsed", typeof(bool), typeof(PageControl),
+ new PropertyMetadata(true, OnInvisibleBackButtonCollapsedChanged));
+
+ public static readonly DependencyProperty ShowBackButtonProperty = DependencyProperty.Register("ShowBackButton", typeof(bool), typeof(PageControl),
+ new PropertyMetadata(false, OnShowBackButtonChanged));
+
+ public static readonly DependencyProperty ShowNavigationBarProperty = DependencyProperty.Register("ShowNavigationBar", typeof(bool), typeof(PageControl), new PropertyMetadata(true));
+
+ public static readonly DependencyProperty NavigationBarBackgroundProperty = DependencyProperty.Register("NavigationBarBackground", typeof(Brush), typeof(PageControl),
+ new PropertyMetadata(default(Brush)));
+
+ public static readonly DependencyProperty BackButtonTitleProperty = DependencyProperty.Register("BackButtonTitle", typeof(string), typeof(PageControl), new PropertyMetadata(false));
+
+ public static readonly DependencyProperty ContentMarginProperty = DependencyProperty.Register("ContentMargin", typeof(Windows.UI.Xaml.Thickness), typeof(PageControl),
+ new PropertyMetadata(default(Windows.UI.Xaml.Thickness)));
+
+ public static readonly DependencyProperty TitleInsetProperty = DependencyProperty.Register("TitleInset", typeof(double), typeof(PageControl), new PropertyMetadata(default(double)));
+
+ public static readonly DependencyProperty TitleBrushProperty = DependencyProperty.Register("TitleBrush", typeof(Brush), typeof(PageControl), new PropertyMetadata(null));
+
+ AppBarButton _backButton;
+ CommandBar _commandBar;
+
+ TaskCompletionSource<CommandBar> _commandBarTcs;
+ Windows.UI.Xaml.Controls.ContentPresenter _presenter;
+
+ public PageControl()
+ {
+ InitializeComponent();
+ }
+
+ public string BackButtonTitle
+ {
+ get { return (string)GetValue(BackButtonTitleProperty); }
+ set { SetValue(BackButtonTitleProperty, value); }
+ }
+
+ public double ContentHeight
+ {
+ get { return _presenter != null ? _presenter.ActualHeight : 0; }
+ }
+
+ public Windows.UI.Xaml.Thickness ContentMargin
+ {
+ get { return (Windows.UI.Xaml.Thickness)GetValue(ContentMarginProperty); }
+ set { SetValue(ContentMarginProperty, value); }
+ }
+
+ public double ContentWidth
+ {
+ get { return _presenter != null ? _presenter.ActualWidth : 0; }
+ }
+
+ public bool InvisibleBackButtonCollapsed
+ {
+ get { return (bool)GetValue(InvisibleBackButtonCollapsedProperty); }
+ set { SetValue(InvisibleBackButtonCollapsedProperty, value); }
+ }
+
+ public Brush NavigationBarBackground
+ {
+ get { return (Brush)GetValue(NavigationBarBackgroundProperty); }
+ set { SetValue(NavigationBarBackgroundProperty, value); }
+ }
+
+ public bool ShowBackButton
+ {
+ get { return (bool)GetValue(ShowBackButtonProperty); }
+ set { SetValue(ShowBackButtonProperty, value); }
+ }
+
+ public bool ShowNavigationBar
+ {
+ get { return (bool)GetValue(ShowNavigationBarProperty); }
+ set { SetValue(ShowNavigationBarProperty, value); }
+ }
+
+ public Brush TitleBrush
+ {
+ get { return (Brush)GetValue(TitleBrushProperty); }
+ set { SetValue(TitleBrushProperty, value); }
+ }
+
+ public double TitleInset
+ {
+ get { return (double)GetValue(TitleInsetProperty); }
+ set { SetValue(TitleInsetProperty, value); }
+ }
+
+ Task<CommandBar> IToolbarProvider.GetCommandBarAsync()
+ {
+ if (_commandBar != null)
+ return Task.FromResult(_commandBar);
+
+ _commandBarTcs = new TaskCompletionSource<CommandBar>();
+ ApplyTemplate();
+ return _commandBarTcs.Task;
+ }
+
+ public event RoutedEventHandler BackClicked;
+
+ protected override void OnApplyTemplate()
+ {
+ base.OnApplyTemplate();
+
+ _backButton = GetTemplateChild("backButton") as AppBarButton;
+ if (_backButton != null)
+ _backButton.Click += OnBackClicked;
+
+ _presenter = GetTemplateChild("presenter") as Windows.UI.Xaml.Controls.ContentPresenter;
+
+ _commandBar = GetTemplateChild("CommandBar") as CommandBar;
+
+ TaskCompletionSource<CommandBar> tcs = _commandBarTcs;
+ if (tcs != null)
+ {
+ tcs.SetResult(_commandBar);
+ }
+ }
+
+ void OnBackClicked(object sender, RoutedEventArgs e)
+ {
+ RoutedEventHandler clicked = BackClicked;
+ if (clicked != null)
+ clicked(this, e);
+ }
+
+ static void OnInvisibleBackButtonCollapsedChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
+ {
+ ((PageControl)dependencyObject).UpdateBackButton();
+ }
+
+ static void OnShowBackButtonChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
+ {
+ ((PageControl)dependencyObject).UpdateBackButton();
+ }
+
+ void UpdateBackButton()
+ {
+ if (_backButton == null)
+ return;
+
+ if (ShowBackButton)
+ _backButton.Visibility = Visibility.Visible;
+ else
+ _backButton.Visibility = InvisibleBackButtonCollapsed ? Visibility.Collapsed : Visibility.Visible;
+
+ _backButton.Opacity = ShowBackButton ? 1 : 0;
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WinRT/PageRenderer.cs b/Xamarin.Forms.Platform.WinRT/PageRenderer.cs
new file mode 100644
index 00000000..d33ff028
--- /dev/null
+++ b/Xamarin.Forms.Platform.WinRT/PageRenderer.cs
@@ -0,0 +1,80 @@
+using System.Collections.ObjectModel;
+using Windows.UI.Xaml;
+
+#if WINDOWS_UWP
+
+namespace Xamarin.Forms.Platform.UWP
+#else
+
+namespace Xamarin.Forms.Platform.WinRT
+#endif
+{
+ public class PageRenderer : VisualElementRenderer<Page, FrameworkElement>
+ {
+ bool _disposed;
+
+ bool _loaded;
+
+ protected override void Dispose(bool disposing)
+ {
+ if (!disposing || _disposed)
+ return;
+
+ _disposed = true;
+
+ if (Element != null)
+ {
+ ReadOnlyCollection<Element> children = Element.LogicalChildren;
+ for (var i = 0; i < children.Count; i++)
+ {
+ var visualChild = children[i] as VisualElement;
+ visualChild?.Cleanup();
+ }
+ Element?.SendDisappearing();
+ }
+
+ base.Dispose();
+ }
+
+ protected override void OnElementChanged(ElementChangedEventArgs<Page> e)
+ {
+ base.OnElementChanged(e);
+
+ if (e.OldElement != null)
+ {
+ e.OldElement.SendDisappearing();
+ }
+
+ if (e.NewElement != null)
+ {
+ if (e.OldElement == null)
+ {
+ Loaded += OnLoaded;
+ Unloaded += OnUnloaded;
+
+ Tracker = new BackgroundTracker<FrameworkElement>(BackgroundProperty);
+ }
+
+ if (_loaded)
+ e.NewElement.SendAppearing();
+ }
+ }
+
+ void OnLoaded(object sender, RoutedEventArgs args)
+ {
+ var carouselPage = Element?.Parent as CarouselPage;
+ if (carouselPage != null && carouselPage.Children[0] != Element)
+ {
+ return;
+ }
+ _loaded = true;
+ Element?.SendAppearing();
+ }
+
+ void OnUnloaded(object sender, RoutedEventArgs args)
+ {
+ _loaded = false;
+ Element?.SendDisappearing();
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WinRT/PageToRenderedElementConverter.cs b/Xamarin.Forms.Platform.WinRT/PageToRenderedElementConverter.cs
new file mode 100644
index 00000000..68e23d31
--- /dev/null
+++ b/Xamarin.Forms.Platform.WinRT/PageToRenderedElementConverter.cs
@@ -0,0 +1,31 @@
+using System;
+
+#if WINDOWS_UWP
+
+namespace Xamarin.Forms.Platform.UWP
+#else
+
+namespace Xamarin.Forms.Platform.WinRT
+#endif
+{
+ public class PageToRenderedElementConverter : Windows.UI.Xaml.Data.IValueConverter
+ {
+ public object Convert(object value, Type targetType, object parameter, string language)
+ {
+ var page = value as Page;
+ if (page == null)
+ return null;
+
+ IVisualElementRenderer renderer = page.GetOrCreateRenderer();
+ if (renderer == null)
+ return null;
+
+ return renderer.ContainerElement;
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter, string language)
+ {
+ throw new NotImplementedException();
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WinRT/PickerRenderer.cs b/Xamarin.Forms.Platform.WinRT/PickerRenderer.cs
new file mode 100644
index 00000000..a7fcbb41
--- /dev/null
+++ b/Xamarin.Forms.Platform.WinRT/PickerRenderer.cs
@@ -0,0 +1,147 @@
+using System;
+using System.ComponentModel;
+using System.Threading.Tasks;
+using Windows.UI.Core;
+using Windows.UI.Xaml.Controls;
+
+#if WINDOWS_UWP
+
+namespace Xamarin.Forms.Platform.UWP
+#else
+
+namespace Xamarin.Forms.Platform.WinRT
+#endif
+{
+ public class PickerRenderer : ViewRenderer<Picker, FormsComboBox>
+ {
+ bool _isAnimating;
+
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ if (Control != null)
+ {
+ _isAnimating = false;
+ Control.SelectionChanged -= OnControlSelectionChanged;
+ Control.DropDownOpened -= OnDropDownOpenStateChanged;
+ Control.DropDownClosed -= OnDropDownOpenStateChanged;
+ Control.OpenAnimationCompleted -= ControlOnOpenAnimationCompleted;
+ }
+ }
+
+ base.Dispose(disposing);
+ }
+
+ protected override void OnElementChanged(ElementChangedEventArgs<Picker> e)
+ {
+ if (e.NewElement != null)
+ {
+ if (Control == null)
+ {
+ SetNativeControl(new FormsComboBox());
+ Control.SelectionChanged += OnControlSelectionChanged;
+ Control.DropDownOpened += OnDropDownOpenStateChanged;
+ Control.DropDownClosed += OnDropDownOpenStateChanged;
+ Control.OpenAnimationCompleted += ControlOnOpenAnimationCompleted;
+ Control.ClosedAnimationStarted += ControlOnClosedAnimationStarted;
+ }
+
+ Control.ItemsSource = Element.Items;
+
+ UpdateTitle();
+ UpdateSelectedIndex();
+ }
+
+ base.OnElementChanged(e);
+ }
+
+ protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ base.OnElementPropertyChanged(sender, e);
+
+ if (e.PropertyName == Picker.SelectedIndexProperty.PropertyName)
+ UpdateSelectedIndex();
+ else if (e.PropertyName == Picker.TitleProperty.PropertyName)
+ UpdateTitle();
+ }
+
+ void ControlOnClosedAnimationStarted(object sender, EventArgs eventArgs)
+ {
+ if (!Control.IsFullScreen)
+ {
+ // Start refreshing while the control's closing animation runs;
+ // OnDropDownOpenStateChanged will take care of stopping the refresh
+ StartAnimationRefresh();
+ }
+ }
+
+ void ControlOnOpenAnimationCompleted(object sender, EventArgs eventArgs)
+ {
+ _isAnimating = false;
+ if (!Control.IsFullScreen)
+ {
+ // Force a final redraw after the closing animation has completed
+ Element?.InvalidateMeasure(InvalidationTrigger.MeasureChanged);
+ }
+ }
+
+ void OnControlSelectionChanged(object sender, SelectionChangedEventArgs e)
+ {
+ if (Element != null)
+ Element.SelectedIndex = Control.SelectedIndex;
+ }
+
+ void OnDropDownOpenStateChanged(object sender, object o)
+ {
+ if (Control.IsDropDownOpen)
+ {
+ if (Control.IsOpeningAnimated && !Control.IsFullScreen)
+ {
+ // Start running the animation refresh;
+ // ControlOnOpenAnimationCompleted will take care of stopping it
+ StartAnimationRefresh();
+ }
+ else
+ {
+ Element?.InvalidateMeasure(InvalidationTrigger.MeasureChanged);
+ }
+ }
+ else
+ {
+ // The ComboBox is now closed; if we were animating the closure, stop
+ _isAnimating = false;
+ // and force the final redraw
+ Element?.InvalidateMeasure(InvalidationTrigger.MeasureChanged);
+ }
+ }
+
+ /// <summary>
+ /// Forces redraw of the control during opening/closing animations to provide
+ /// a smoother sliding animation for the surrounding controls
+ /// Only applies on the phone and only when there are fewer than 6 items in the picker
+ /// </summary>
+ void StartAnimationRefresh()
+ {
+ _isAnimating = true;
+ Task.Factory.StartNew(async () =>
+ {
+ while (_isAnimating)
+ {
+ await Task.Delay(16);
+ await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => Element?.InvalidateMeasure(InvalidationTrigger.MeasureChanged));
+ }
+ });
+ }
+
+ void UpdateSelectedIndex()
+ {
+ Control.SelectedIndex = Element.SelectedIndex;
+ }
+
+ void UpdateTitle()
+ {
+ Control.Header = Element.Title;
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WinRT/Platform.cs b/Xamarin.Forms.Platform.WinRT/Platform.cs
new file mode 100644
index 00000000..df16ddc9
--- /dev/null
+++ b/Xamarin.Forms.Platform.WinRT/Platform.cs
@@ -0,0 +1,721 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+
+using Windows.UI;
+using Windows.UI.Popups;
+using Windows.UI.Xaml;
+using Windows.UI.Xaml.Controls;
+using Windows.UI.Xaml.Controls.Primitives;
+using Windows.UI.Xaml.Media;
+using Windows.UI.Xaml.Media.Animation;
+
+#if WINDOWS_UWP
+using Windows.Foundation.Metadata;
+using Windows.UI.ViewManagement;
+#endif
+
+#if WINDOWS_UWP
+
+namespace Xamarin.Forms.Platform.UWP
+#else
+
+namespace Xamarin.Forms.Platform.WinRT
+#endif
+{
+ public abstract class Platform : IPlatform, INavigation, IToolbarProvider
+ {
+ internal static readonly BindableProperty RendererProperty = BindableProperty.CreateAttached("Renderer", typeof(IVisualElementRenderer), typeof(Platform), default(IVisualElementRenderer));
+
+ public static IVisualElementRenderer GetRenderer(VisualElement element)
+ {
+ return (IVisualElementRenderer)element.GetValue(RendererProperty);
+ }
+
+ public static void SetRenderer(VisualElement element, IVisualElementRenderer value)
+ {
+ element.SetValue(RendererProperty, value);
+ element.IsPlatformEnabled = value != null;
+ }
+
+ public static IVisualElementRenderer CreateRenderer(VisualElement element)
+ {
+ if (element == null)
+ throw new ArgumentNullException("element");
+
+ IVisualElementRenderer renderer = Registrar.Registered.GetHandler<IVisualElementRenderer>(element.GetType()) ?? new DefaultRenderer();
+ renderer.SetElement(element);
+ return renderer;
+ }
+
+ internal Platform(Windows.UI.Xaml.Controls.Page page)
+ {
+ if (page == null)
+ throw new ArgumentNullException("page");
+
+ _page = page;
+
+ _container = new Canvas { Style = (Windows.UI.Xaml.Style)Windows.UI.Xaml.Application.Current.Resources["RootContainerStyle"] };
+
+ _page.Content = _container;
+
+ _container.SizeChanged += OnRendererSizeChanged;
+
+ MessagingCenter.Subscribe(this, Page.BusySetSignalName, (Page sender, bool enabled) =>
+ {
+ Windows.UI.Xaml.Controls.ProgressBar indicator = GetBusyIndicator();
+ indicator.Visibility = enabled ? Visibility.Visible : Visibility.Collapsed;
+ });
+
+ _toolbarTracker.CollectionChanged += OnToolbarItemsChanged;
+
+ MessagingCenter.Subscribe<Page, AlertArguments>(this, Page.AlertSignalName, OnPageAlert);
+ MessagingCenter.Subscribe<Page, ActionSheetArguments>(this, Page.ActionSheetSignalName, OnPageActionSheet);
+
+ UpdateBounds();
+
+#if WINDOWS_UWP
+ if (ApiInformation.IsTypePresent(typeof(StatusBar).ToString()))
+ {
+ StatusBar statusBar = StatusBar.GetForCurrentView();
+ statusBar.Showing += (sender, args) => UpdateBounds();
+ statusBar.Hiding += (sender, args) => UpdateBounds();
+ }
+#endif
+ }
+
+ internal void SetPage(Page newRoot)
+ {
+ if (newRoot == null)
+ throw new ArgumentNullException("newRoot");
+
+ _navModel.Clear();
+
+ _navModel.Push(newRoot, null);
+ newRoot.NavigationProxy.Inner = this;
+ SetCurrent(newRoot, false, true);
+ }
+
+ public IReadOnlyList<Page> NavigationStack
+ {
+ get { return _navModel.Tree.Last(); }
+ }
+
+ public IReadOnlyList<Page> ModalStack
+ {
+ get { return _navModel.Modals.ToList(); }
+ }
+
+ Task INavigation.PushAsync(Page root)
+ {
+ return ((INavigation)this).PushAsync(root, true);
+ }
+
+ Task<Page> INavigation.PopAsync()
+ {
+ return ((INavigation)this).PopAsync(true);
+ }
+
+ Task INavigation.PopToRootAsync()
+ {
+ return ((INavigation)this).PopToRootAsync(true);
+ }
+
+ Task INavigation.PushAsync(Page root, bool animated)
+ {
+ throw new InvalidOperationException("PushAsync is not supported globally on Windows, please use a NavigationPage.");
+ }
+
+ Task<Page> INavigation.PopAsync(bool animated)
+ {
+ throw new InvalidOperationException("PopAsync is not supported globally on Windows, please use a NavigationPage.");
+ }
+
+ Task INavigation.PopToRootAsync(bool animated)
+ {
+ throw new InvalidOperationException("PopToRootAsync is not supported globally on Windows, please use a NavigationPage.");
+ }
+
+ void INavigation.RemovePage(Page page)
+ {
+ throw new InvalidOperationException("RemovePage is not supported globally on Windows, please use a NavigationPage.");
+ }
+
+ void INavigation.InsertPageBefore(Page page, Page before)
+ {
+ throw new InvalidOperationException("InsertPageBefore is not supported globally on Windows, please use a NavigationPage.");
+ }
+
+ Task INavigation.PushModalAsync(Page page)
+ {
+ return ((INavigation)this).PushModalAsync(page, true);
+ }
+
+ Task<Page> INavigation.PopModalAsync()
+ {
+ return ((INavigation)this).PopModalAsync(true);
+ }
+
+ Task INavigation.PushModalAsync(Page page, bool animated)
+ {
+ if (page == null)
+ throw new ArgumentNullException("page");
+
+ var tcs = new TaskCompletionSource<bool>();
+ _navModel.PushModal(page);
+ SetCurrent(page, animated, completedCallback: () => tcs.SetResult(true));
+ page.NavigationProxy.Inner = this;
+ return tcs.Task;
+ }
+
+ Task<Page> INavigation.PopModalAsync(bool animated)
+ {
+ var tcs = new TaskCompletionSource<Page>();
+ Page result = _navModel.PopModal();
+ SetCurrent(_navModel.CurrentPage, animated, true, () => tcs.SetResult(result));
+ return tcs.Task;
+ }
+
+ SizeRequest IPlatform.GetNativeSize(VisualElement element, double widthConstraint, double heightConstraint)
+ {
+ // Hack around the fact that Canvas ignores the child constraints.
+ // It is entirely possible using Canvas as our base class is not wise.
+ // FIXME: This should not be an if statement. Probably need to define an interface here.
+ if (widthConstraint > 0 && heightConstraint > 0)
+ {
+ IVisualElementRenderer elementRenderer = GetRenderer(element);
+ if (elementRenderer != null)
+ return elementRenderer.GetDesiredSize(widthConstraint, heightConstraint);
+ }
+
+ return new SizeRequest();
+ }
+
+ internal virtual Rectangle WindowBounds
+ {
+ get { return _bounds; }
+ }
+
+ internal void UpdatePageSizes()
+ {
+ Rectangle bounds = WindowBounds;
+ if (bounds.IsEmpty)
+ return;
+ foreach (Page root in _navModel.Roots)
+ {
+ root.Layout(bounds);
+ IVisualElementRenderer renderer = GetRenderer(root);
+ if (renderer != null)
+ {
+ renderer.ContainerElement.Width = _container.ActualWidth;
+ renderer.ContainerElement.Height = _container.ActualHeight;
+ }
+ }
+ }
+
+ internal IToolbarProvider GetToolbarProvider()
+ {
+ IToolbarProvider provider = null;
+
+ Page element = _currentPage;
+ while (element != null)
+ {
+ provider = GetRenderer(element) as IToolbarProvider;
+ if (provider != null)
+ break;
+
+ var pageContainer = element as IPageContainer<Page>;
+ element = pageContainer?.CurrentPage;
+ }
+
+ if (provider != null && _toolbarProvider == null)
+ ClearCommandBar();
+
+ return provider;
+ }
+
+ internal async Task UpdateToolbarItems()
+ {
+ CommandBar commandBar = await GetCommandBarAsync();
+ if (commandBar != null)
+ {
+ commandBar.PrimaryCommands.Clear();
+ commandBar.SecondaryCommands.Clear();
+#if WINDOWS_UWP
+ if (_page.BottomAppBar != null || _page.TopAppBar != null)
+ {
+ _page.BottomAppBar = null;
+ _page.TopAppBar = null;
+ _page.InvalidateMeasure();
+ }
+#endif
+ }
+
+#if !WINDOWS_UWP
+ commandBar = AddOpenMasterButton(commandBar);
+#endif
+
+#if WINDOWS_UWP
+ var toolBarProvider = GetToolbarProvider() as IToolBarForegroundBinder;
+#endif
+
+ foreach (ToolbarItem item in _toolbarTracker.ToolbarItems.OrderBy(ti => ti.Priority))
+ {
+ if (commandBar == null)
+ commandBar = CreateCommandBar();
+
+#if WINDOWS_UWP
+ toolBarProvider?.BindForegroundColor(commandBar);
+#endif
+
+ var button = new AppBarButton();
+ button.SetBinding(AppBarButton.LabelProperty, "Text");
+ button.SetBinding(AppBarButton.IconProperty, "Icon", _fileImageSourcePathConverter);
+ button.Command = new MenuItemCommand(item);
+ button.DataContext = item;
+
+#if WINDOWS_UWP
+ toolBarProvider?.BindForegroundColor(button);
+#endif
+
+ ToolbarItemOrder order = item.Order == ToolbarItemOrder.Default ? ToolbarItemOrder.Primary : item.Order;
+ if (order == ToolbarItemOrder.Primary)
+ commandBar.PrimaryCommands.Add(button);
+ else
+ commandBar.SecondaryCommands.Add(button);
+ }
+
+ if (commandBar?.PrimaryCommands.Count + commandBar?.SecondaryCommands.Count == 0)
+ ClearCommandBar();
+ }
+
+#if !WINDOWS_UWP
+ CommandBar AddOpenMasterButton(CommandBar commandBar)
+ {
+ if (!_toolbarTracker.HaveMasterDetail)
+ {
+ return commandBar;
+ }
+
+ if (commandBar == null)
+ {
+ commandBar = CreateCommandBar();
+ }
+
+ Page target = _toolbarTracker.Target;
+ var mdp = target as MasterDetailPage;
+ while (mdp == null)
+ {
+ var container = target as IPageContainer<Page>;
+ if (container == null)
+ {
+ break;
+ }
+
+ target = container.CurrentPage;
+ mdp = container.CurrentPage as MasterDetailPage;
+ }
+
+ if (mdp == null || !mdp.ShouldShowToolbarButton())
+ {
+ return commandBar;
+ }
+
+ var openMaster = new AppBarButton { DataContext = mdp };
+ openMaster.SetBinding(AppBarButton.LabelProperty, "Master.Title");
+ openMaster.SetBinding(AppBarButton.IconProperty, "Master.Icon", _fileImageSourcePathConverter);
+ openMaster.Click += (s, a) => { mdp.IsPresented = !mdp.IsPresented; };
+
+ commandBar.PrimaryCommands.Add(openMaster);
+
+ return commandBar;
+ }
+#endif
+
+ Rectangle _bounds;
+ readonly Canvas _container;
+ readonly Windows.UI.Xaml.Controls.Page _page;
+ Windows.UI.Xaml.Controls.ProgressBar _busyIndicator;
+ Page _currentPage;
+ readonly NavigationModel _navModel = new NavigationModel();
+ readonly ToolbarTracker _toolbarTracker = new ToolbarTracker();
+ readonly FileImageSourcePathConverter _fileImageSourcePathConverter = new FileImageSourcePathConverter();
+
+ IToolbarProvider _toolbarProvider;
+
+ class ToolbarProvider : IToolbarProvider
+ {
+ readonly Task<CommandBar> _commandBar;
+
+ public ToolbarProvider(CommandBar commandBar)
+ {
+ _commandBar = Task.FromResult(commandBar);
+ }
+
+ public CommandBar CommandBar => _commandBar.Result;
+
+ public Task<CommandBar> GetCommandBarAsync()
+ {
+ return _commandBar;
+ }
+ }
+
+ Windows.UI.Xaml.Controls.ProgressBar GetBusyIndicator()
+ {
+ if (_busyIndicator == null)
+ {
+ _busyIndicator = new Windows.UI.Xaml.Controls.ProgressBar { IsIndeterminate = true, Visibility = Visibility.Collapsed, VerticalAlignment = VerticalAlignment.Top };
+
+ Canvas.SetZIndex(_busyIndicator, 1);
+ _container.Children.Add(_busyIndicator);
+ }
+
+ return _busyIndicator;
+ }
+
+ internal bool BackButtonPressed()
+ {
+ if (_currentActionSheet != null)
+ {
+ CancelActionSheet();
+ return true;
+ }
+
+ Page lastRoot = _navModel.Roots.Last();
+
+ bool handled = lastRoot.SendBackButtonPressed();
+
+ if (!handled && _navModel.Tree.Count > 1)
+ {
+ Page removed = _navModel.PopModal();
+ if (removed != null)
+ {
+ SetCurrent(_navModel.CurrentPage, true, true);
+ handled = true;
+ }
+ }
+
+ return handled;
+ }
+
+ void CancelActionSheet()
+ {
+ if (_currentActionSheet == null)
+ return;
+
+ _actionSheetOptions.SetResult(null);
+ _actionSheetOptions = null;
+ _currentActionSheet.IsOpen = false;
+ _currentActionSheet = null;
+ }
+
+ void UpdateBounds()
+ {
+ _bounds = new Rectangle(0, 0, _page.ActualWidth, _page.ActualHeight);
+#if WINDOWS_UWP
+ if (ApiInformation.IsTypePresent(typeof(StatusBar).ToString()))
+ {
+ StatusBar statusBar = StatusBar.GetForCurrentView();
+
+ bool landscape = Device.Info.CurrentOrientation.IsLandscape();
+ double offset = landscape ? statusBar.OccludedRect.Width : statusBar.OccludedRect.Height;
+
+ _bounds = new Rectangle(0, 0, _page.ActualWidth - (landscape ? offset : 0), _page.ActualHeight - (landscape ? 0 : offset));
+ }
+#endif
+ }
+
+ void OnRendererSizeChanged(object sender, SizeChangedEventArgs sizeChangedEventArgs)
+ {
+ UpdateBounds();
+ UpdatePageSizes();
+ }
+
+ async void SetCurrent(Page newPage, bool animated, bool popping = false, Action completedCallback = null)
+ {
+ if (newPage == _currentPage)
+ return;
+
+ newPage.Platform = this;
+
+ if (_currentPage != null)
+ {
+ Page previousPage = _currentPage;
+ IVisualElementRenderer previousRenderer = GetRenderer(previousPage);
+ _container.Children.Remove(previousRenderer.ContainerElement);
+
+ if (popping)
+ previousPage.Cleanup();
+ }
+
+ newPage.Layout(new Rectangle(0, 0, _page.ActualWidth, _page.ActualHeight));
+
+ IVisualElementRenderer pageRenderer = newPage.GetOrCreateRenderer();
+ _container.Children.Add(pageRenderer.ContainerElement);
+
+ pageRenderer.ContainerElement.Width = _container.ActualWidth;
+ pageRenderer.ContainerElement.Height = _container.ActualHeight;
+
+ if (completedCallback != null)
+ completedCallback();
+
+ _currentPage = newPage;
+
+ UpdateToolbarTracker();
+ UpdateToolbarTitle(newPage);
+ await UpdateToolbarItems();
+ }
+
+ void UpdateToolbarTitle(Page page)
+ {
+ if (_toolbarProvider == null)
+ return;
+
+ ((ToolbarProvider)_toolbarProvider).CommandBar.Content = page.Title;
+ }
+
+ Task<CommandBar> IToolbarProvider.GetCommandBarAsync()
+ {
+ return GetCommandBarAsync();
+ }
+
+ async Task<CommandBar> GetCommandBarAsync()
+ {
+#if !WINDOWS_UWP
+ return _page.BottomAppBar as CommandBar;
+#else
+ IToolbarProvider provider = GetToolbarProvider();
+ var titleProvider = provider as ITitleProvider;
+ if (provider == null || (titleProvider != null && !titleProvider.ShowTitle))
+ return null;
+
+ return await provider.GetCommandBarAsync();
+#endif
+ }
+
+ CommandBar CreateCommandBar()
+ {
+#if !WINDOWS_UWP
+ var commandBar = new CommandBar();
+ _page.BottomAppBar = commandBar;
+ return commandBar;
+#else
+ var bar = new FormsCommandBar();
+ if (Device.Idiom != TargetIdiom.Phone)
+ bar.Style = (Windows.UI.Xaml.Style)Windows.UI.Xaml.Application.Current.Resources["TitleToolbar"];
+
+ _toolbarProvider = new ToolbarProvider(bar);
+
+ if (Device.Idiom == TargetIdiom.Phone)
+ _page.BottomAppBar = bar;
+ else
+ _page.TopAppBar = bar;
+
+ return bar;
+#endif
+ }
+
+ void ClearCommandBar()
+ {
+#if !WINDOWS_UWP
+ _page.BottomAppBar = null;
+#else
+ if (_toolbarProvider != null)
+ {
+ _toolbarProvider = null;
+ if (Device.Idiom == TargetIdiom.Phone)
+ _page.BottomAppBar = null;
+ else
+ _page.TopAppBar = null;
+ }
+#endif
+ }
+
+ async void OnToolbarItemsChanged(object sender, EventArgs e)
+ {
+ await UpdateToolbarItems();
+ }
+
+ void UpdateToolbarTracker()
+ {
+ Page last = _navModel.Roots.Last();
+ if (last != null)
+ _toolbarTracker.Target = last;
+ }
+
+ ActionSheetArguments _actionSheetOptions;
+ Popup _currentActionSheet;
+
+#if WINDOWS_UWP
+ async void OnPageActionSheet(Page sender, ActionSheetArguments options)
+ {
+ List<string> buttons = options.Buttons.ToList();
+
+ var list = new Windows.UI.Xaml.Controls.ListView
+ {
+ Style = (Windows.UI.Xaml.Style)Windows.UI.Xaml.Application.Current.Resources["ActionSheetList"],
+ ItemsSource = buttons,
+ IsItemClickEnabled = true
+ };
+
+ var dialog = new ContentDialog
+ {
+ Template = (Windows.UI.Xaml.Controls.ControlTemplate)Windows.UI.Xaml.Application.Current.Resources["MyContentDialogControlTemplate"],
+ Content = list,
+ Style = (Windows.UI.Xaml.Style)Windows.UI.Xaml.Application.Current.Resources["ActionSheetStyle"]
+ };
+
+ if (options.Title != null)
+ dialog.Title = options.Title;
+
+ list.ItemClick += (s, e) =>
+ {
+ dialog.Hide();
+ options.SetResult((string)e.ClickedItem);
+ };
+
+ _actionSheetOptions = options;
+
+ if (options.Cancel != null)
+ dialog.SecondaryButtonText = options.Cancel;
+
+ if (options.Destruction != null)
+ dialog.PrimaryButtonText = options.Destruction;
+
+ ContentDialogResult result = await dialog.ShowAsync();
+ if (result == ContentDialogResult.Secondary)
+ options.SetResult(options.Cancel);
+ else if (result == ContentDialogResult.Primary)
+ options.SetResult(options.Destruction);
+ }
+#else
+ void OnPageActionSheet(Page sender, ActionSheetArguments options)
+ {
+ var finalArguments = new List<string>();
+ if (options.Destruction != null)
+ finalArguments.Add(options.Destruction);
+ if (options.Buttons != null)
+ finalArguments.AddRange(options.Buttons);
+ if (options.Cancel != null)
+ finalArguments.Add(options.Cancel);
+
+ var list = new Windows.UI.Xaml.Controls.ListView
+ {
+ Style = (Windows.UI.Xaml.Style)Windows.UI.Xaml.Application.Current.Resources["ActionSheetList"],
+ ItemsSource = finalArguments,
+ IsItemClickEnabled = true
+ };
+
+ list.ItemClick += (s, e) =>
+ {
+ _currentActionSheet.IsOpen = false;
+ _currentActionSheet = null;
+ options.SetResult((string)e.ClickedItem);
+ };
+
+ _actionSheetOptions = options;
+
+ Size size = Device.Info.ScaledScreenSize;
+
+ var stack = new StackPanel
+ {
+ MinWidth = 100,
+ Children =
+ {
+ new TextBlock
+ {
+ Text = options.Title ?? string.Empty,
+ Style = (Windows.UI.Xaml.Style)Windows.UI.Xaml.Application.Current.Resources["TitleTextBlockStyle"],
+ Margin = new Windows.UI.Xaml.Thickness(0, 0, 0, 10),
+ Visibility = options.Title != null ? Visibility.Visible : Visibility.Collapsed
+ },
+ list
+ }
+ };
+
+ var border = new Border
+ {
+ Child = stack,
+ BorderBrush = new SolidColorBrush(Colors.White),
+ BorderThickness = new Windows.UI.Xaml.Thickness(1),
+ Padding = new Windows.UI.Xaml.Thickness(15),
+ Background = (Brush)Windows.UI.Xaml.Application.Current.Resources["AppBarBackgroundThemeBrush"]
+ };
+
+ Windows.UI.Xaml.Controls.Grid.SetRow(border, 1);
+ Windows.UI.Xaml.Controls.Grid.SetColumn(border, 1);
+
+ var container = new Windows.UI.Xaml.Controls.Grid
+ {
+ RowDefinitions =
+ {
+ new Windows.UI.Xaml.Controls.RowDefinition { Height = new Windows.UI.Xaml.GridLength(1, Windows.UI.Xaml.GridUnitType.Star) },
+ new Windows.UI.Xaml.Controls.RowDefinition { Height = new Windows.UI.Xaml.GridLength(0, Windows.UI.Xaml.GridUnitType.Auto) },
+ new Windows.UI.Xaml.Controls.RowDefinition { Height = new Windows.UI.Xaml.GridLength(1, Windows.UI.Xaml.GridUnitType.Star) }
+ },
+ ColumnDefinitions =
+ {
+ new Windows.UI.Xaml.Controls.ColumnDefinition { Width = new Windows.UI.Xaml.GridLength(1, Windows.UI.Xaml.GridUnitType.Star) },
+ new Windows.UI.Xaml.Controls.ColumnDefinition { Width = new Windows.UI.Xaml.GridLength(0, Windows.UI.Xaml.GridUnitType.Auto) },
+ new Windows.UI.Xaml.Controls.ColumnDefinition { Width = new Windows.UI.Xaml.GridLength(1, Windows.UI.Xaml.GridUnitType.Star) }
+ },
+ Height = size.Height,
+ Width = size.Width,
+ Children = { border }
+ };
+
+ var bgPopup = new Popup { Child = new Canvas { Width = size.Width, Height = size.Height, Background = new SolidColorBrush(new Windows.UI.Color { A = 128, R = 0, G = 0, B = 0 }) } };
+
+ bgPopup.IsOpen = true;
+
+ _currentActionSheet = new Popup { ChildTransitions = new TransitionCollection { new PopupThemeTransition() }, IsLightDismissEnabled = true, Child = container };
+
+ _currentActionSheet.Closed += (s, e) =>
+ {
+ bgPopup.IsOpen = false;
+ CancelActionSheet();
+ };
+
+ if (Device.Idiom == TargetIdiom.Phone)
+ {
+ double height = WindowBounds.Height;
+ stack.Height = height;
+ stack.Width = size.Width;
+ border.BorderThickness = new Windows.UI.Xaml.Thickness(0);
+
+ _currentActionSheet.Height = height;
+ _currentActionSheet.VerticalOffset = size.Height - height;
+ }
+
+ _currentActionSheet.IsOpen = true;
+ }
+#endif
+
+ async void OnPageAlert(Page sender, AlertArguments options)
+ {
+ string content = options.Message ?? options.Title ?? string.Empty;
+
+ MessageDialog dialog;
+ if (options.Message == null || options.Title == null)
+ dialog = new MessageDialog(content);
+ else
+ dialog = new MessageDialog(options.Message, options.Title);
+
+ if (options.Accept != null)
+ {
+ dialog.Commands.Add(new UICommand(options.Accept));
+ dialog.DefaultCommandIndex = (uint)dialog.Commands.Count - 1;
+ }
+
+ if (options.Cancel != null)
+ {
+ dialog.Commands.Add(new UICommand(options.Cancel));
+ dialog.CancelCommandIndex = 0;
+ }
+
+ IUICommand command = await dialog.ShowAsync();
+ options.SetResult(command.Label == options.Accept);
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WinRT/PlatformEffect.cs b/Xamarin.Forms.Platform.WinRT/PlatformEffect.cs
new file mode 100644
index 00000000..cd2991eb
--- /dev/null
+++ b/Xamarin.Forms.Platform.WinRT/PlatformEffect.cs
@@ -0,0 +1,14 @@
+using Windows.UI.Xaml;
+
+#if WINDOWS_UWP
+
+namespace Xamarin.Forms.Platform.UWP
+#else
+
+namespace Xamarin.Forms.Platform.WinRT
+#endif
+{
+ public abstract class PlatformEffect : PlatformEffect<FrameworkElement, FrameworkElement>
+ {
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WinRT/ProgressBarRenderer.cs b/Xamarin.Forms.Platform.WinRT/ProgressBarRenderer.cs
new file mode 100644
index 00000000..87f1e35e
--- /dev/null
+++ b/Xamarin.Forms.Platform.WinRT/ProgressBarRenderer.cs
@@ -0,0 +1,59 @@
+using System.ComponentModel;
+using Windows.UI.Xaml.Controls.Primitives;
+
+#if WINDOWS_UWP
+
+namespace Xamarin.Forms.Platform.UWP
+#else
+
+namespace Xamarin.Forms.Platform.WinRT
+#endif
+{
+ public class ProgressBarRenderer : ViewRenderer<ProgressBar, Windows.UI.Xaml.Controls.ProgressBar>
+ {
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ if (Control != null)
+ {
+ Control.ValueChanged -= ProgressBarOnValueChanged;
+ }
+ }
+
+ base.Dispose(disposing);
+ }
+
+ protected override void OnElementChanged(ElementChangedEventArgs<ProgressBar> e)
+ {
+ base.OnElementChanged(e);
+
+ if (e.NewElement != null)
+ {
+ if (Control == null)
+ {
+ var progressBar = new Windows.UI.Xaml.Controls.ProgressBar { Minimum = 0, Maximum = 1 };
+
+ progressBar.ValueChanged += ProgressBarOnValueChanged;
+
+ SetNativeControl(progressBar);
+ }
+
+ Control.Value = e.NewElement.Progress;
+ }
+ }
+
+ protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ base.OnElementPropertyChanged(sender, e);
+
+ if (e.PropertyName == ProgressBar.ProgressProperty.PropertyName)
+ Control.Value = Element.Progress;
+ }
+
+ void ProgressBarOnValueChanged(object sender, RangeBaseValueChangedEventArgs rangeBaseValueChangedEventArgs)
+ {
+ Element?.InvalidateMeasure(InvalidationTrigger.MeasureChanged);
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WinRT/Properties/AssemblyInfo.cs b/Xamarin.Forms.Platform.WinRT/Properties/AssemblyInfo.cs
new file mode 100644
index 00000000..f24feecf
--- /dev/null
+++ b/Xamarin.Forms.Platform.WinRT/Properties/AssemblyInfo.cs
@@ -0,0 +1,57 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using Xamarin.Forms;
+using Xamarin.Forms.Platform.WinRT;
+
+[assembly: AssemblyTitle("Xamarin.Forms.Platform.WinRT")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: InternalsVisibleTo("Xamarin.Forms.Platform.WinRT.Tablet")]
+[assembly: InternalsVisibleTo("Xamarin.Forms.Platform.WinRT.Phone")]
+[assembly: Dependency(typeof(WindowsSerializer))]
+
+// Views
+
+[assembly: ExportRenderer(typeof(Layout), typeof(LayoutRenderer))]
+[assembly: ExportRenderer(typeof(BoxView), typeof(BoxViewRenderer))]
+[assembly: ExportRenderer(typeof(Image), typeof(ImageRenderer))]
+[assembly: ExportRenderer(typeof(Label), typeof(LabelRenderer))]
+[assembly: ExportRenderer(typeof(Button), typeof(ButtonRenderer))]
+[assembly: ExportRenderer(typeof(ListView), typeof(ListViewRenderer))]
+[assembly: ExportRenderer(typeof(CarouselView), typeof(CarouselViewRenderer))]
+[assembly: ExportRenderer(typeof(ScrollView), typeof(ScrollViewRenderer))]
+[assembly: ExportRenderer(typeof(ProgressBar), typeof(ProgressBarRenderer))]
+[assembly: ExportRenderer(typeof(Slider), typeof(SliderRenderer))]
+[assembly: ExportRenderer(typeof(Switch), typeof(SwitchRenderer))]
+[assembly: ExportRenderer(typeof(WebView), typeof(WebViewRenderer))]
+[assembly: ExportRenderer(typeof(Frame), typeof(FrameRenderer))]
+[assembly: ExportRenderer(typeof(ActivityIndicator), typeof(ActivityIndicatorRenderer))]
+[assembly: ExportRenderer(typeof(Editor), typeof(EditorRenderer))]
+[assembly: ExportRenderer(typeof(Picker), typeof(PickerRenderer))]
+[assembly: ExportRenderer(typeof(TimePicker), typeof(TimePickerRenderer))]
+[assembly: ExportRenderer(typeof(DatePicker), typeof(DatePickerRenderer))]
+[assembly: ExportRenderer(typeof(Stepper), typeof(StepperRenderer))]
+[assembly: ExportRenderer(typeof(Entry), typeof(EntryRenderer))]
+[assembly: ExportRenderer(typeof(TableView), typeof(TableViewRenderer))]
+[assembly: ExportRenderer(typeof(NativeViewWrapper), typeof(NativeViewWrapperRenderer))]
+
+//ImageSources
+
+[assembly: ExportImageSourceHandler(typeof(FileImageSource), typeof(FileImageSourceHandler))]
+[assembly: ExportImageSourceHandler(typeof(StreamImageSource), typeof(StreamImagesourceHandler))]
+[assembly: ExportImageSourceHandler(typeof(UriImageSource), typeof(ImageLoaderSourceHandler))]
+
+// Pages
+
+[assembly: ExportRenderer(typeof(Page), typeof(PageRenderer))]
+[assembly: ExportRenderer(typeof(NavigationPage), typeof(NavigationPageRenderer))]
+[assembly: ExportRenderer(typeof(MasterDetailPage), typeof(MasterDetailPageRenderer))]
+[assembly: ExportRenderer(typeof(CarouselPage), typeof(CarouselPageRenderer))]
+
+// Cells
+
+[assembly: ExportCell(typeof(Cell), typeof(TextCellRenderer))]
+[assembly: ExportCell(typeof(ImageCell), typeof(ImageCellRenderer))]
+[assembly: ExportCell(typeof(EntryCell), typeof(EntryCellRenderer))]
+[assembly: ExportCell(typeof(SwitchCell), typeof(SwitchCellRenderer))]
+[assembly: ExportCell(typeof(ViewCell), typeof(ViewCellRenderer))] \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WinRT/RendererFactory.cs b/Xamarin.Forms.Platform.WinRT/RendererFactory.cs
new file mode 100644
index 00000000..576950c3
--- /dev/null
+++ b/Xamarin.Forms.Platform.WinRT/RendererFactory.cs
@@ -0,0 +1,19 @@
+using System;
+
+#if WINDOWS_UWP
+
+namespace Xamarin.Forms.Platform.UWP
+#else
+
+namespace Xamarin.Forms.Platform.WinRT
+#endif
+{
+ public static class RendererFactory
+ {
+ [Obsolete("Use Platform.CreateRenderer")]
+ public static IVisualElementRenderer CreateRenderer(VisualElement element)
+ {
+ return Platform.CreateRenderer(element);
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WinRT/Resources.xaml b/Xamarin.Forms.Platform.WinRT/Resources.xaml
new file mode 100644
index 00000000..fe135a89
--- /dev/null
+++ b/Xamarin.Forms.Platform.WinRT/Resources.xaml
@@ -0,0 +1,87 @@
+<ResourceDictionary
+ xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ xmlns:forms="using:Xamarin.Forms.Platform.WinRT">
+
+ <forms:CaseConverter x:Key="LowerConverter" ConvertToUpper="False" />
+ <forms:CaseConverter x:Key="UpperConverter" ConvertToUpper="True" />
+ <forms:HeightConverter x:Key="HeightConverter" />
+ <forms:CollapseWhenEmptyConverter x:Key="CollapseWhenEmpty" />
+ <forms:BoolToVisibilityConverter x:Key="BoolVisibilityConverter" />
+ <forms:PageToRenderedElementConverter x:Key="PageToRenderer" />
+ <forms:ImageConverter x:Key="ImageConverter" />
+ <forms:ViewToRendererConverter x:Key="ViewToRenderer" />
+ <forms:ColorConverter x:Key="ColorConverter" />
+ <forms:HorizontalTextAlignmentConverter x:Key="HorizontalTextAlignmentConverter" />
+ <forms:KeyboardConverter x:Key="KeyboardConverter" />
+ <forms:MasterBackgroundConverter x:Key="MasterBackgroundConverter" />
+
+ <Style x:Key="RootContainerStyle" TargetType="Canvas">
+ <Setter Property="Background" Value="{ThemeResource ApplicationPageBackgroundThemeBrush}" />
+ </Style>
+
+ <Style x:Key="ActionSheetList" TargetType="ListView">
+ <Setter Property="SelectionMode" Value="None" />
+ <Setter Property="ItemContainerStyle">
+ <Setter.Value>
+ <Style TargetType="ListViewItem">
+ <Setter Property="Margin" Value="0" />
+ <Setter Property="HorizontalContentAlignment" Value="Stretch" />
+ </Style>
+ </Setter.Value>
+ </Setter>
+ <Setter Property="ItemTemplate">
+ <Setter.Value>
+ <DataTemplate>
+ <TextBlock Text="{Binding}" HorizontalAlignment="Stretch" VerticalAlignment="Center" Style="{ThemeResource SubheaderTextBlockStyle}" />
+ </DataTemplate>
+ </Setter.Value>
+ </Setter>
+ </Style>
+
+ <DataTemplate x:Key="ItemTemplate">
+ <forms:ItemControl HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch" />
+ </DataTemplate>
+
+ <DataTemplate x:Key="CellTemplate">
+ <forms:CellControl HorizontalContentAlignment="Stretch" />
+ </DataTemplate>
+
+ <DataTemplate x:Key="TableRoot">
+ <TextBlock Margin="0,20,0,0" Text="{Binding Title,Converter={StaticResource LowerConverter}}" Style="{ThemeResource HeaderTextBlockStyle}" Visibility="{Binding Text,RelativeSource={RelativeSource Mode=Self},Converter={StaticResource CollapseWhenEmpty}}" />
+ </DataTemplate>
+
+ <DataTemplate x:Key="TableSection">
+ <TextBlock Margin="0,20,0,0" Text="{Binding Title,Converter={StaticResource LowerConverter}}" Style="{ThemeResource SubheaderTextBlockStyle}" Visibility="{Binding Text,RelativeSource={RelativeSource Mode=Self},Converter={StaticResource CollapseWhenEmpty}}" />
+ </DataTemplate>
+
+ <forms:ListViewGroupStyleSelector x:Key="ListViewGroupSelector" />
+
+ <DataTemplate x:Key="ViewCell">
+ <ContentControl DataContext="{Binding Cell}">
+ <ContentPresenter Height="{Binding RenderHeight, Converter={StaticResource HeightConverter}}" Content="{Binding View, Converter={StaticResource ViewToRenderer}}" />
+ </ContentControl>
+ </DataTemplate>
+
+ <DataTemplate x:Key="ContainedPageTemplate">
+ <ContentPresenter Content="{Binding Converter={StaticResource PageToRenderer}}">
+ <ContentPresenter.Resources>
+ <Style TargetType="forms:PageControl">
+ <Setter Property="Background" Value="Transparent" />
+ </Style>
+ </ContentPresenter.Resources>
+ </ContentPresenter>
+ </DataTemplate>
+
+ <DataTemplate x:Key="View">
+ <ContentPresenter Content="{Binding Converter={StaticResource ViewToRenderer}}" />
+ </DataTemplate>
+
+ <Style TargetType="TextBox">
+ <Setter Property="Margin" Value="0" />
+ </Style>
+
+ <DataTemplate x:Key="PushPinTemplate">
+ <Path Data="M 50.7361,983.661 C 44.1895,983.661 38.8369,988.97 38.8369,995.517 39.8649,1003.3 45.246,1008.1 49.8547,1014.12 50.2838,1014.66 51.2336,1014.66 51.6619,1014.12 52.1384,1013.48 52.7575,1012.73 53.4248,1011.91 55.0322,1012.07 56.4727,1012.32 57.5676,1012.71 58.407,1013 59.06,1013.33 59.4192,1013.63 59.7784,1013.93 59.7716,1014.11 59.7716,1014.16 59.7716,1014.21 59.7716,1014.39 59.4192,1014.69 59.06,1014.99 58.407,1015.32 57.5676,1015.61 55.8888,1016.2 53.4519,1016.63 50.7361,1016.63 48.0204,1016.63 45.5399,1016.2 43.8611,1015.61 43.0218,1015.32 42.3695,1014.99 42.0103,1014.69 41.6504,1014.39 41.6135,1014.21 41.6135,1014.16 41.6135,1014.11 41.6511,1013.93 42.0103,1013.63 42.3695,1013.33 43.0218,1013 43.8611,1012.71 44.3158,1012.55 44.8455,1012.35 45.4039,1012.22 L 43.8611,1010.33 C 43.6124,1010.4 43.3441,1010.46 43.1119,1010.55 42.1005,1010.9 41.2318,1011.31 40.5555,1011.87 39.8799,1012.43 39.3216,1013.22 39.3216,1014.16 39.3216,1015.1 39.8799,1015.85 40.5555,1016.41 41.2318,1016.97 42.1005,1017.42 43.1119,1017.77 45.1356,1018.48 47.8025,1018.92 50.7362,1018.92 54.437,1018.81 57.9892,1018.36 60.8733,1016.41 62.5084,1014.79 62.0756,1013.4 60.8733,1011.87 60.1969,1011.31 59.3726,1010.9 58.3612,1010.55 57.4331,1010.22 56.3503,1009.94 55.1878,1009.75 56.1992,1008.51 57.2362,1007.18 58.2289,1005.79 60.5599,1002.51 62.5918,998.968 62.5918,995.517 62.5918,988.97 57.2836,983.661 50.7362,983.661 Z M 50.7361,989.655 C 47.571,989.655 44.9627,992.219 44.9627,995.385 44.9627,998.55 47.571,1001.16 50.7361,1001.16 53.902,1001.16 56.4659,998.55 56.4659,995.385 56.4659,992.219 53.902,989.655 50.7361,989.655 Z M 50.7361,991.947 C 52.6591,991.947 54.174,993.462 54.174,995.385 54.174,997.307 52.6591,998.866 50.7361,998.866 48.8139,998.866 47.2546,997.307 47.2546,995.385 47.2546,993.462 48.8139,991.947 50.7361,991.947 Z" Fill="#000000" />
+ </DataTemplate>
+</ResourceDictionary> \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WinRT/ScrollViewRenderer.cs b/Xamarin.Forms.Platform.WinRT/ScrollViewRenderer.cs
new file mode 100644
index 00000000..6938a76e
--- /dev/null
+++ b/Xamarin.Forms.Platform.WinRT/ScrollViewRenderer.cs
@@ -0,0 +1,194 @@
+using System;
+using System.ComponentModel;
+using Windows.Foundation;
+using Windows.UI.Xaml;
+using Windows.UI.Xaml.Controls;
+
+#if WINDOWS_UWP
+
+namespace Xamarin.Forms.Platform.UWP
+#else
+
+namespace Xamarin.Forms.Platform.WinRT
+#endif
+{
+ public class ScrollViewRenderer : ViewRenderer<ScrollView, ScrollViewer>
+ {
+ VisualElement _currentView;
+
+ public ScrollViewRenderer()
+ {
+ AutoPackage = false;
+ }
+
+ protected IScrollViewController Controller
+ {
+ get { return Element; }
+ }
+
+ public override SizeRequest GetDesiredSize(double widthConstraint, double heightConstraint)
+ {
+ SizeRequest result = base.GetDesiredSize(widthConstraint, heightConstraint);
+ result.Minimum = new Size(40, 40);
+ return result;
+ }
+
+ protected override Windows.Foundation.Size ArrangeOverride(Windows.Foundation.Size finalSize)
+ {
+ if (Element == null)
+ return finalSize;
+
+ Element.IsInNativeLayout = true;
+
+ Control?.Arrange(new Rect(0, 0, finalSize.Width, finalSize.Height));
+
+ Element.IsInNativeLayout = false;
+
+ return finalSize;
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ if (Control != null)
+ {
+ Control.ViewChanged -= OnViewChanged;
+ }
+
+ base.Dispose(disposing);
+ }
+
+ protected override Windows.Foundation.Size MeasureOverride(Windows.Foundation.Size availableSize)
+ {
+ if (Element == null)
+ return new Windows.Foundation.Size(0, 0);
+
+ double width = Math.Max(0, Element.Width);
+ double height = Math.Max(0, Element.Height);
+ var result = new Windows.Foundation.Size(width, height);
+
+ Control?.Measure(result);
+
+ return result;
+ }
+
+ protected override void OnElementChanged(ElementChangedEventArgs<ScrollView> e)
+ {
+ base.OnElementChanged(e);
+
+ if (e.OldElement != null)
+ {
+ ((IScrollViewController)e.OldElement).ScrollToRequested -= OnScrollToRequested;
+ }
+
+ if (e.NewElement != null)
+ {
+ if (Control == null)
+ {
+ SetNativeControl(new ScrollViewer { HorizontalScrollBarVisibility = ScrollBarVisibility.Auto, VerticalScrollBarVisibility = ScrollBarVisibility.Auto });
+
+ Control.ViewChanged += OnViewChanged;
+ }
+
+ Controller.ScrollToRequested += OnScrollToRequested;
+
+ UpdateOrientation();
+
+ LoadContent();
+ }
+ }
+
+ protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ base.OnElementPropertyChanged(sender, e);
+
+ if (e.PropertyName == "Content")
+ LoadContent();
+ else if (e.PropertyName == Layout.PaddingProperty.PropertyName)
+ UpdateMargins();
+ else if (e.PropertyName == ScrollView.OrientationProperty.PropertyName)
+ UpdateOrientation();
+ }
+
+ void LoadContent()
+ {
+ if (_currentView != null)
+ {
+ _currentView.Cleanup();
+ }
+
+ _currentView = Element.Content;
+
+ IVisualElementRenderer renderer = null;
+ if (_currentView != null)
+ {
+ renderer = _currentView.GetOrCreateRenderer();
+ }
+
+ Control.Content = renderer != null ? renderer.ContainerElement : null;
+
+ UpdateMargins();
+ }
+
+ void OnScrollToRequested(object sender, ScrollToRequestedEventArgs e)
+ {
+ double x = e.ScrollX, y = e.ScrollY;
+
+ ScrollToMode mode = e.Mode;
+ if (mode == ScrollToMode.Element)
+ {
+ Point pos = Controller.GetScrollPositionForElement((VisualElement)e.Element, e.Position);
+ x = pos.X;
+ y = pos.Y;
+ mode = ScrollToMode.Position;
+ }
+
+ if (mode == ScrollToMode.Position)
+ {
+ Control.ChangeView(x, y, null, !e.ShouldAnimate);
+ }
+ }
+
+ void OnViewChanged(object sender, ScrollViewerViewChangedEventArgs e)
+ {
+ Controller.SetScrolledPosition(Control.HorizontalOffset, Control.VerticalOffset);
+
+ if (!e.IsIntermediate)
+ Controller.SendScrollFinished();
+ }
+
+ void UpdateMargins()
+ {
+ var element = Control.Content as FrameworkElement;
+ if (element == null)
+ return;
+
+ switch (Element.Orientation)
+ {
+ case ScrollOrientation.Horizontal:
+ // need to add left/right margins
+ element.Margin = new Windows.UI.Xaml.Thickness(Element.Padding.Left, 0, Element.Padding.Right, 0);
+ break;
+ case ScrollOrientation.Vertical:
+ // need to add top/bottom margins
+ element.Margin = new Windows.UI.Xaml.Thickness(0, Element.Padding.Top, 0, Element.Padding.Bottom);
+ break;
+ case ScrollOrientation.Both:
+ // need to add all margins
+ element.Margin = new Windows.UI.Xaml.Thickness(Element.Padding.Left, Element.Padding.Top, Element.Padding.Right, Element.Padding.Bottom);
+ break;
+ }
+ }
+
+ void UpdateOrientation()
+ {
+ if (Element.Orientation == ScrollOrientation.Horizontal || Element.Orientation == ScrollOrientation.Both)
+ {
+ Control.HorizontalScrollBarVisibility = ScrollBarVisibility.Auto;
+ }
+ else
+ {
+ Control.HorizontalScrollBarVisibility = ScrollBarVisibility.Disabled;
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WinRT/SliderRenderer.cs b/Xamarin.Forms.Platform.WinRT/SliderRenderer.cs
new file mode 100644
index 00000000..ee7a8adc
--- /dev/null
+++ b/Xamarin.Forms.Platform.WinRT/SliderRenderer.cs
@@ -0,0 +1,59 @@
+using System;
+using System.ComponentModel;
+using Windows.UI.Xaml.Controls.Primitives;
+
+#if WINDOWS_UWP
+
+namespace Xamarin.Forms.Platform.UWP
+#else
+
+namespace Xamarin.Forms.Platform.WinRT
+#endif
+{
+ public class SliderRenderer : ViewRenderer<Slider, Windows.UI.Xaml.Controls.Slider>
+ {
+ protected override void OnElementChanged(ElementChangedEventArgs<Slider> e)
+ {
+ base.OnElementChanged(e);
+
+ if (e.NewElement != null)
+ {
+ if (Control == null)
+ {
+ var slider = new Windows.UI.Xaml.Controls.Slider();
+ SetNativeControl(slider);
+
+ slider.ValueChanged += OnNativeValueCHanged;
+ }
+
+ double stepping = Math.Min((e.NewElement.Maximum - e.NewElement.Minimum) / 10, 1);
+ Control.StepFrequency = stepping;
+ Control.SmallChange = stepping;
+
+ Control.Minimum = e.NewElement.Minimum;
+ Control.Maximum = e.NewElement.Maximum;
+ Control.Value = e.NewElement.Value;
+ }
+ }
+
+ protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ base.OnElementPropertyChanged(sender, e);
+
+ if (e.PropertyName == Slider.MinimumProperty.PropertyName)
+ Control.Minimum = Element.Minimum;
+ else if (e.PropertyName == Slider.MaximumProperty.PropertyName)
+ Control.Maximum = Element.Maximum;
+ else if (e.PropertyName == Slider.ValueProperty.PropertyName)
+ {
+ if (Control.Value != Element.Value)
+ Control.Value = Element.Value;
+ }
+ }
+
+ void OnNativeValueCHanged(object sender, RangeBaseValueChangedEventArgs e)
+ {
+ ((IElementController)Element).SetValueFromRenderer(Slider.ValueProperty, e.NewValue);
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WinRT/StepperControl.xaml b/Xamarin.Forms.Platform.WinRT/StepperControl.xaml
new file mode 100644
index 00000000..f4ab46c2
--- /dev/null
+++ b/Xamarin.Forms.Platform.WinRT/StepperControl.xaml
@@ -0,0 +1,90 @@
+<UserControl
+ x:Class="Xamarin.Forms.Platform.WinRT.StepperControl"
+ xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ xmlns:local="using:Xamarin.Forms.Platform.WinRT"
+ xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ mc:Ignorable="d"
+ d:DesignHeight="300"
+ d:DesignWidth="400">
+
+ <ContentControl>
+ <Grid>
+ <Grid.Resources>
+ <Style TargetType="Button">
+ <Setter Property="Margin" Value="0" />
+ <Setter Property="Template">
+ <Setter.Value>
+ <ControlTemplate TargetType="Button">
+ <Grid>
+ <VisualStateManager.VisualStateGroups>
+ <VisualStateGroup x:Name="CommonStates">
+ <VisualState x:Name="Normal"/>
+ <VisualState x:Name="PointerOver">
+ <Storyboard>
+ <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Background" Storyboard.TargetName="Border">
+ <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ButtonPointerOverBackgroundThemeBrush}"/>
+ </ObjectAnimationUsingKeyFrames>
+ <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Foreground" Storyboard.TargetName="ContentPresenter">
+ <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ButtonPointerOverForegroundThemeBrush}"/>
+ </ObjectAnimationUsingKeyFrames>
+ </Storyboard>
+ </VisualState>
+ <VisualState x:Name="Pressed">
+ <Storyboard>
+ <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Background" Storyboard.TargetName="Border">
+ <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ButtonPressedBackgroundThemeBrush}"/>
+ </ObjectAnimationUsingKeyFrames>
+ <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Foreground" Storyboard.TargetName="ContentPresenter">
+ <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ButtonPressedForegroundThemeBrush}"/>
+ </ObjectAnimationUsingKeyFrames>
+ </Storyboard>
+ </VisualState>
+ <VisualState x:Name="Disabled">
+ <Storyboard>
+ <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Background" Storyboard.TargetName="Border">
+ <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ButtonDisabledBackgroundThemeBrush}"/>
+ </ObjectAnimationUsingKeyFrames>
+ <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="BorderBrush" Storyboard.TargetName="Border">
+ <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ButtonDisabledBorderThemeBrush}"/>
+ </ObjectAnimationUsingKeyFrames>
+ <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Foreground" Storyboard.TargetName="ContentPresenter">
+ <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ButtonDisabledForegroundThemeBrush}"/>
+ </ObjectAnimationUsingKeyFrames>
+ </Storyboard>
+ </VisualState>
+ </VisualStateGroup>
+ <VisualStateGroup x:Name="FocusStates">
+ <VisualState x:Name="Focused">
+ <Storyboard>
+ <DoubleAnimation Duration="0" To="1" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="FocusVisualWhite"/>
+ <DoubleAnimation Duration="0" To="1" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="FocusVisualBlack"/>
+ </Storyboard>
+ </VisualState>
+ <VisualState x:Name="Unfocused"/>
+ <VisualState x:Name="PointerFocused"/>
+ </VisualStateGroup>
+ </VisualStateManager.VisualStateGroups>
+ <Border x:Name="Border" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Margin="0">
+ <ContentPresenter x:Name="ContentPresenter" AutomationProperties.AccessibilityView="Raw" ContentTemplate="{TemplateBinding ContentTemplate}" ContentTransitions="{TemplateBinding ContentTransitions}" Content="{TemplateBinding Content}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
+ </Border>
+ <Rectangle x:Name="FocusVisualWhite" IsHitTestVisible="False" Opacity="0" StrokeDashOffset="1.5" StrokeEndLineCap="Square" Stroke="{ThemeResource FocusVisualWhiteStrokeThemeBrush}" StrokeDashArray="1,1"/>
+ <Rectangle x:Name="FocusVisualBlack" IsHitTestVisible="False" Opacity="0" StrokeDashOffset="0.5" StrokeEndLineCap="Square" Stroke="{ThemeResource FocusVisualBlackStrokeThemeBrush}" StrokeDashArray="1,1"/>
+ </Grid>
+ </ControlTemplate>
+ </Setter.Value>
+ </Setter>
+ </Style>
+ </Grid.Resources>
+
+ <Grid.ColumnDefinitions>
+ <ColumnDefinition Width="*" />
+ <ColumnDefinition Width="*" />
+ </Grid.ColumnDefinitions>
+
+ <Button Name="Minus" Grid.Column="0" Content="-" Click="OnMinusClicked" BorderThickness="2,2,1,2" />
+ <Button Name="Plus" Grid.Column="1" Content="+" Click="OnPlusClicked" BorderThickness="1,2,2,2" />
+ </Grid>
+ </ContentControl>
+</UserControl> \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WinRT/StepperControl.xaml.cs b/Xamarin.Forms.Platform.WinRT/StepperControl.xaml.cs
new file mode 100644
index 00000000..49a4632f
--- /dev/null
+++ b/Xamarin.Forms.Platform.WinRT/StepperControl.xaml.cs
@@ -0,0 +1,94 @@
+using System;
+using Windows.UI.Xaml;
+using Windows.UI.Xaml.Controls;
+
+namespace Xamarin.Forms.Platform.WinRT
+{
+ public sealed partial class StepperControl : UserControl
+ {
+ public static readonly DependencyProperty ValueProperty = DependencyProperty.Register("Value", typeof(double), typeof(StepperControl), new PropertyMetadata(default(double), OnValueChanged));
+
+ public static readonly DependencyProperty MaximumProperty = DependencyProperty.Register("Maximum", typeof(double), typeof(StepperControl), new PropertyMetadata(default(double), OnMaxMinChanged));
+
+ public static readonly DependencyProperty MinimumProperty = DependencyProperty.Register("Minimum", typeof(double), typeof(StepperControl), new PropertyMetadata(default(double), OnMaxMinChanged));
+
+ public static readonly DependencyProperty IncrementProperty = DependencyProperty.Register("Increment", typeof(double), typeof(StepperControl),
+ new PropertyMetadata(default(double), OnIncrementChanged));
+
+ public StepperControl()
+ {
+ InitializeComponent();
+ }
+
+ public double Increment
+ {
+ get { return (double)GetValue(IncrementProperty); }
+ set { SetValue(IncrementProperty, value); }
+ }
+
+ public double Maximum
+ {
+ get { return (double)GetValue(MaximumProperty); }
+ set { SetValue(MaximumProperty, value); }
+ }
+
+ public double Minimum
+ {
+ get { return (double)GetValue(MinimumProperty); }
+ set { SetValue(MinimumProperty, value); }
+ }
+
+ public double Value
+ {
+ get { return (double)GetValue(ValueProperty); }
+ set { SetValue(ValueProperty, value); }
+ }
+
+ public event EventHandler ValueChanged;
+
+ static void OnIncrementChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ var stepper = (StepperControl)d;
+ stepper.UpdateEnabled(stepper.Value);
+ }
+
+ static void OnMaxMinChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ var stepper = (StepperControl)d;
+ stepper.UpdateEnabled(stepper.Value);
+ }
+
+ void OnMinusClicked(object sender, RoutedEventArgs e)
+ {
+ UpdateValue(-Increment);
+ }
+
+ void OnPlusClicked(object sender, RoutedEventArgs e)
+ {
+ UpdateValue(+Increment);
+ }
+
+ static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ var stepper = (StepperControl)d;
+ stepper.UpdateEnabled((double)e.NewValue);
+
+ EventHandler changed = stepper.ValueChanged;
+ if (changed != null)
+ changed(d, EventArgs.Empty);
+ }
+
+ void UpdateEnabled(double value)
+ {
+ double increment = Increment;
+ Plus.IsEnabled = value + increment <= Maximum;
+ Minus.IsEnabled = value - increment >= Minimum;
+ }
+
+ void UpdateValue(double delta)
+ {
+ double newValue = Value + delta;
+ Value = newValue;
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WinRT/StepperRenderer.cs b/Xamarin.Forms.Platform.WinRT/StepperRenderer.cs
new file mode 100644
index 00000000..7a4724cf
--- /dev/null
+++ b/Xamarin.Forms.Platform.WinRT/StepperRenderer.cs
@@ -0,0 +1,72 @@
+using System;
+using System.ComponentModel;
+
+#if WINDOWS_UWP
+
+namespace Xamarin.Forms.Platform.UWP
+#else
+
+namespace Xamarin.Forms.Platform.WinRT
+#endif
+{
+ public class StepperRenderer : ViewRenderer<Stepper, StepperControl>
+ {
+ protected override void OnElementChanged(ElementChangedEventArgs<Stepper> e)
+ {
+ base.OnElementChanged(e);
+
+ if (e.NewElement != null)
+ {
+ if (Control == null)
+ {
+ SetNativeControl(new StepperControl());
+ Control.ValueChanged += OnControlValue;
+ }
+
+ UpdateMaximum();
+ UpdateMinimum();
+ UpdateValue();
+ UpdateIncrement();
+ }
+ }
+
+ protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ base.OnElementPropertyChanged(sender, e);
+
+ if (e.PropertyName == Stepper.ValueProperty.PropertyName)
+ UpdateValue();
+ else if (e.PropertyName == Stepper.MaximumProperty.PropertyName)
+ UpdateMaximum();
+ else if (e.PropertyName == Stepper.MinimumProperty.PropertyName)
+ UpdateMinimum();
+ else if (e.PropertyName == Stepper.IncrementProperty.PropertyName)
+ UpdateIncrement();
+ }
+
+ void OnControlValue(object sender, EventArgs e)
+ {
+ Element.SetValueCore(Stepper.ValueProperty, Control.Value);
+ }
+
+ void UpdateIncrement()
+ {
+ Control.Increment = Element.Increment;
+ }
+
+ void UpdateMaximum()
+ {
+ Control.Maximum = Element.Maximum;
+ }
+
+ void UpdateMinimum()
+ {
+ Control.Minimum = Element.Minimum;
+ }
+
+ void UpdateValue()
+ {
+ Control.Value = Element.Value;
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WinRT/StreamImagesourceHandler.cs b/Xamarin.Forms.Platform.WinRT/StreamImagesourceHandler.cs
new file mode 100644
index 00000000..ecf8c0db
--- /dev/null
+++ b/Xamarin.Forms.Platform.WinRT/StreamImagesourceHandler.cs
@@ -0,0 +1,36 @@
+using System;
+using System.IO;
+using System.Threading;
+using System.Threading.Tasks;
+using Windows.UI.Xaml.Media.Imaging;
+
+#if WINDOWS_UWP
+
+namespace Xamarin.Forms.Platform.UWP
+#else
+
+namespace Xamarin.Forms.Platform.WinRT
+#endif
+{
+ public sealed class StreamImagesourceHandler : IImageSourceHandler
+ {
+ public async Task<Windows.UI.Xaml.Media.ImageSource> LoadImageAsync(ImageSource imagesource, CancellationToken cancellationToken = new CancellationToken())
+ {
+ BitmapImage bitmapimage = null;
+
+ var streamsource = imagesource as StreamImageSource;
+ if (streamsource != null && streamsource.Stream != null)
+ {
+ using(Stream stream = await streamsource.GetStreamAsync(cancellationToken))
+ {
+ if (stream == null)
+ return null;
+ bitmapimage = new BitmapImage();
+ await bitmapimage.SetSourceAsync(stream.AsRandomAccessStream());
+ }
+ }
+
+ return bitmapimage;
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WinRT/SwitchRenderer.cs b/Xamarin.Forms.Platform.WinRT/SwitchRenderer.cs
new file mode 100644
index 00000000..ebc08ad6
--- /dev/null
+++ b/Xamarin.Forms.Platform.WinRT/SwitchRenderer.cs
@@ -0,0 +1,50 @@
+using System.ComponentModel;
+using Windows.UI.Xaml;
+using Windows.UI.Xaml.Controls;
+
+#if WINDOWS_UWP
+
+namespace Xamarin.Forms.Platform.UWP
+#else
+
+namespace Xamarin.Forms.Platform.WinRT
+#endif
+{
+ public class SwitchRenderer : ViewRenderer<Switch, ToggleSwitch>
+ {
+ protected override void OnElementChanged(ElementChangedEventArgs<Switch> e)
+ {
+ base.OnElementChanged(e);
+
+ if (e.NewElement != null)
+ {
+ if (Control == null)
+ {
+ var control = new ToggleSwitch();
+ control.Toggled += OnNativeToggled;
+ control.ClearValue(ToggleSwitch.OnContentProperty);
+ control.ClearValue(ToggleSwitch.OffContentProperty);
+
+ SetNativeControl(control);
+ }
+
+ Control.IsOn = Element.IsToggled;
+ }
+ }
+
+ protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ base.OnElementPropertyChanged(sender, e);
+
+ if (e.PropertyName == Switch.IsToggledProperty.PropertyName)
+ {
+ Control.IsOn = Element.IsToggled;
+ }
+ }
+
+ void OnNativeToggled(object sender, RoutedEventArgs routedEventArgs)
+ {
+ ((IElementController)Element).SetValueFromRenderer(Switch.IsToggledProperty, Control.IsOn);
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WinRT/TableViewRenderer.cs b/Xamarin.Forms.Platform.WinRT/TableViewRenderer.cs
new file mode 100644
index 00000000..c5750c79
--- /dev/null
+++ b/Xamarin.Forms.Platform.WinRT/TableViewRenderer.cs
@@ -0,0 +1,85 @@
+using System;
+using Windows.UI.Xaml.Controls;
+using Windows.UI.Xaml.Data;
+
+#if WINDOWS_UWP
+
+namespace Xamarin.Forms.Platform.UWP
+#else
+
+namespace Xamarin.Forms.Platform.WinRT
+#endif
+{
+ public class TableViewRenderer : ViewRenderer<TableView, Windows.UI.Xaml.Controls.ListView>
+ {
+ bool _ignoreSelectionEvent;
+
+ public override SizeRequest GetDesiredSize(double widthConstraint, double heightConstraint)
+ {
+ SizeRequest result = base.GetDesiredSize(widthConstraint, heightConstraint);
+ result.Minimum = new Size(40, 40);
+ return result;
+ }
+
+ protected override void OnElementChanged(ElementChangedEventArgs<TableView> e)
+ {
+ if (e.OldElement != null)
+ {
+ e.OldElement.ModelChanged -= OnModelChanged;
+ }
+
+ if (e.NewElement != null)
+ {
+ if (Control == null)
+ {
+ SetNativeControl(new Windows.UI.Xaml.Controls.ListView
+ {
+ ItemContainerStyle = (Windows.UI.Xaml.Style)Windows.UI.Xaml.Application.Current.Resources["FormsListViewItem"],
+ ItemTemplate = (Windows.UI.Xaml.DataTemplate)Windows.UI.Xaml.Application.Current.Resources["CellTemplate"],
+ GroupStyle = { new GroupStyle { HidesIfEmpty = false, HeaderTemplate = (Windows.UI.Xaml.DataTemplate)Windows.UI.Xaml.Application.Current.Resources["TableSection"] } },
+ HeaderTemplate = (Windows.UI.Xaml.DataTemplate)Windows.UI.Xaml.Application.Current.Resources["TableRoot"],
+ SelectionMode = ListViewSelectionMode.Single
+ });
+
+ // You can't set ItemsSource directly to a CollectionViewSource, it crashes.
+ Control.SetBinding(ItemsControl.ItemsSourceProperty, "");
+ Control.SelectionChanged += OnSelectionChanged;
+ }
+
+ e.NewElement.ModelChanged += OnModelChanged;
+ OnModelChanged(e.NewElement, EventArgs.Empty);
+ }
+
+ base.OnElementChanged(e);
+ }
+
+ void OnModelChanged(object sender, EventArgs e)
+ {
+ Control.Header = Element.Root;
+
+ // This auto-selects the first item in the new DataContext, so we just null it and ignore the selection
+ // as this selection isn't driven by user input
+ _ignoreSelectionEvent = true;
+ Control.DataContext = new CollectionViewSource { Source = Element.Root, IsSourceGrouped = true };
+ _ignoreSelectionEvent = false;
+ }
+
+ void OnSelectionChanged(object sender, SelectionChangedEventArgs e)
+ {
+ if (!_ignoreSelectionEvent)
+ {
+ foreach (object item in e.AddedItems)
+ {
+ var cell = item as Cell;
+ if (cell != null)
+ {
+ Element.Model.RowSelected(cell);
+ break;
+ }
+ }
+ }
+
+ Control.SelectedItem = null;
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WinRT/TaskExtensions.cs b/Xamarin.Forms.Platform.WinRT/TaskExtensions.cs
new file mode 100644
index 00000000..332e8a64
--- /dev/null
+++ b/Xamarin.Forms.Platform.WinRT/TaskExtensions.cs
@@ -0,0 +1,40 @@
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+using Windows.Foundation;
+
+#if WINDOWS_UWP
+
+namespace Xamarin.Forms.Platform.UWP
+#else
+
+namespace Xamarin.Forms.Platform.WinRT
+#endif
+{
+ internal static class TaskExtensions
+ {
+ public static void WatchForError(this IAsyncAction self)
+ {
+ self.AsTask().WatchForError();
+ }
+
+ public static void WatchForError<T>(this IAsyncOperation<T> self)
+ {
+ self.AsTask().WatchForError();
+ }
+
+ public static void WatchForError(this Task self)
+ {
+ SynchronizationContext context = SynchronizationContext.Current;
+ if (context == null)
+ return;
+
+ self.ContinueWith(t =>
+ {
+ Exception exception = t.Exception.InnerExceptions.Count > 1 ? t.Exception : t.Exception.InnerException;
+
+ context.Post(e => { throw (Exception)e; }, exception);
+ }, CancellationToken.None, TaskContinuationOptions.OnlyOnFaulted, TaskScheduler.Default);
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WinRT/TextAlignmentToHorizontalAlignmentConverter.cs b/Xamarin.Forms.Platform.WinRT/TextAlignmentToHorizontalAlignmentConverter.cs
new file mode 100644
index 00000000..8f95af1e
--- /dev/null
+++ b/Xamarin.Forms.Platform.WinRT/TextAlignmentToHorizontalAlignmentConverter.cs
@@ -0,0 +1,48 @@
+using System;
+using Windows.UI.Xaml;
+
+#if WINDOWS_UWP
+
+namespace Xamarin.Forms.Platform.UWP
+#else
+
+namespace Xamarin.Forms.Platform.WinRT
+#endif
+{
+ public sealed class TextAlignmentToHorizontalAlignmentConverter : Windows.UI.Xaml.Data.IValueConverter
+ {
+ public object Convert(object value, Type targetType, object parameter, string language)
+ {
+ var alignment = (Windows.UI.Xaml.TextAlignment)value;
+
+ switch (alignment)
+ {
+ case Windows.UI.Xaml.TextAlignment.Center:
+ return HorizontalAlignment.Center;
+ case Windows.UI.Xaml.TextAlignment.Left:
+ return HorizontalAlignment.Left;
+ case Windows.UI.Xaml.TextAlignment.Right:
+ return HorizontalAlignment.Right;
+ default:
+ return HorizontalAlignment.Left;
+ }
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter, string language)
+ {
+ var alignment = (HorizontalAlignment)value;
+
+ switch (alignment)
+ {
+ case HorizontalAlignment.Left:
+ return Windows.UI.Xaml.TextAlignment.Left;
+ case HorizontalAlignment.Center:
+ return Windows.UI.Xaml.TextAlignment.Center;
+ case HorizontalAlignment.Right:
+ return Windows.UI.Xaml.TextAlignment.Right;
+ default:
+ return Windows.UI.Xaml.TextAlignment.Left;
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WinRT/TextCellRenderer.cs b/Xamarin.Forms.Platform.WinRT/TextCellRenderer.cs
new file mode 100644
index 00000000..7adf009d
--- /dev/null
+++ b/Xamarin.Forms.Platform.WinRT/TextCellRenderer.cs
@@ -0,0 +1,79 @@
+using System;
+using System.Windows.Input;
+using WDataTemplate = Windows.UI.Xaml.DataTemplate;
+using WApplication = Windows.UI.Xaml.Application;
+
+#if WINDOWS_UWP
+
+namespace Xamarin.Forms.Platform.UWP
+#else
+
+namespace Xamarin.Forms.Platform.WinRT
+#endif
+{
+ public class TextCellRenderer : ICellRenderer
+ {
+ public virtual WDataTemplate GetTemplate(Cell cell)
+ {
+ if (cell.RealParent is ListView)
+ {
+ if (TemplatedItemsList<ItemsView<Cell>, Cell>.GetIsGroupHeader(cell))
+ return (WDataTemplate)WApplication.Current.Resources["ListViewHeaderTextCell"];
+
+ //return (WDataTemplate) WApplication.Current.Resources["ListViewTextCell"];
+ }
+
+ return (WDataTemplate)WApplication.Current.Resources["TextCell"];
+ }
+ }
+
+ public class EntryCellRendererCompleted : ICommand
+ {
+ public bool CanExecute(object parameter)
+ {
+ return true;
+ }
+
+ public event EventHandler CanExecuteChanged;
+
+ public void Execute(object parameter)
+ {
+ var entryCell = (EntryCell)parameter;
+ entryCell.SendCompleted();
+ }
+ }
+
+ public class EntryCellRenderer : ICellRenderer
+ {
+ public virtual WDataTemplate GetTemplate(Cell cell)
+ {
+ return (WDataTemplate)WApplication.Current.Resources["EntryCell"];
+ }
+ }
+
+ public class ViewCellRenderer : ICellRenderer
+ {
+ public virtual WDataTemplate GetTemplate(Cell cell)
+ {
+ return (WDataTemplate)WApplication.Current.Resources["ViewCell"];
+ }
+ }
+
+ public class SwitchCellRenderer : ICellRenderer
+ {
+ public virtual WDataTemplate GetTemplate(Cell cell)
+ {
+ return (WDataTemplate)WApplication.Current.Resources["SwitchCell"];
+ }
+ }
+
+ public class ImageCellRenderer : ICellRenderer
+ {
+ public virtual WDataTemplate GetTemplate(Cell cell)
+ {
+ //if (cell.Parent is ListView)
+ // return (WDataTemplate)WApplication.Current.Resources["ListImageCell"];
+ return (WDataTemplate)WApplication.Current.Resources["ImageCell"];
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WinRT/TimePickerRenderer.cs b/Xamarin.Forms.Platform.WinRT/TimePickerRenderer.cs
new file mode 100644
index 00000000..3aa5fdca
--- /dev/null
+++ b/Xamarin.Forms.Platform.WinRT/TimePickerRenderer.cs
@@ -0,0 +1,75 @@
+using System;
+using System.ComponentModel;
+using Windows.UI.Xaml.Controls;
+
+#if WINDOWS_UWP
+
+namespace Xamarin.Forms.Platform.UWP
+#else
+
+namespace Xamarin.Forms.Platform.WinRT
+#endif
+{
+ public class TimePickerRenderer : ViewRenderer<TimePicker, FormsTimePicker>, IWrapperAware
+ {
+ public void NotifyWrapped()
+ {
+ if (Control != null)
+ {
+ Control.ForceInvalidate += PickerOnForceInvalidate;
+ }
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && Control != null)
+ {
+ Control.ForceInvalidate -= PickerOnForceInvalidate;
+ Control.TimeChanged -= OnControlTimeChanged;
+ }
+
+ base.Dispose(disposing);
+ }
+
+ protected override void OnElementChanged(ElementChangedEventArgs<TimePicker> e)
+ {
+ if (e.NewElement != null)
+ {
+ if (Control == null)
+ {
+ var picker = new FormsTimePicker();
+ picker.TimeChanged += OnControlTimeChanged;
+ SetNativeControl(picker);
+ }
+
+ UpdateTime();
+ }
+
+ base.OnElementChanged(e);
+ }
+
+ protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ base.OnElementPropertyChanged(sender, e);
+
+ if (e.PropertyName == TimePicker.TimeProperty.PropertyName)
+ UpdateTime();
+ }
+
+ void OnControlTimeChanged(object sender, TimePickerValueChangedEventArgs e)
+ {
+ Element.Time = e.NewTime;
+ Element?.InvalidateMeasure(InvalidationTrigger.SizeRequestChanged);
+ }
+
+ void PickerOnForceInvalidate(object sender, EventArgs eventArgs)
+ {
+ Element?.InvalidateMeasure(InvalidationTrigger.SizeRequestChanged);
+ }
+
+ void UpdateTime()
+ {
+ Control.Time = Element.Time;
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WinRT/ViewExtensions.cs b/Xamarin.Forms.Platform.WinRT/ViewExtensions.cs
new file mode 100644
index 00000000..9e382ab9
--- /dev/null
+++ b/Xamarin.Forms.Platform.WinRT/ViewExtensions.cs
@@ -0,0 +1,26 @@
+using System.Collections.Generic;
+
+#if WINDOWS_UWP
+
+namespace Xamarin.Forms.Platform.UWP
+#else
+
+namespace Xamarin.Forms.Platform.WinRT
+#endif
+{
+ internal static class ViewExtensions
+ {
+ public static IEnumerable<Page> GetParentPages(this Page target)
+ {
+ var result = new List<Page>();
+ var parent = target.Parent as Page;
+ while (!Application.IsApplicationOrNull(parent))
+ {
+ result.Add(parent);
+ parent = parent.Parent as Page;
+ }
+
+ return result;
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WinRT/ViewRenderer.cs b/Xamarin.Forms.Platform.WinRT/ViewRenderer.cs
new file mode 100644
index 00000000..148da1b3
--- /dev/null
+++ b/Xamarin.Forms.Platform.WinRT/ViewRenderer.cs
@@ -0,0 +1,37 @@
+using Windows.UI.Xaml;
+using Windows.UI.Xaml.Automation;
+
+#if WINDOWS_UWP
+
+namespace Xamarin.Forms.Platform.UWP
+#else
+
+namespace Xamarin.Forms.Platform.WinRT
+#endif
+{
+ public class ViewRenderer<TElement, TNativeElement> : VisualElementRenderer<TElement, TNativeElement> where TElement : View where TNativeElement : FrameworkElement
+ {
+ protected override void OnElementChanged(ElementChangedEventArgs<TElement> e)
+ {
+ base.OnElementChanged(e);
+
+ if (e.NewElement != null)
+ {
+ UpdateBackgroundColor();
+ }
+ }
+
+ protected override void SetAutomationId(string id)
+ {
+ if (Control == null)
+ {
+ base.SetAutomationId(id);
+ }
+ else
+ {
+ SetValue(AutomationProperties.AutomationIdProperty, $"{id}_Container");
+ Control.SetValue(AutomationProperties.AutomationIdProperty, id);
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WinRT/ViewToRendererConverter.cs b/Xamarin.Forms.Platform.WinRT/ViewToRendererConverter.cs
new file mode 100644
index 00000000..1c078d93
--- /dev/null
+++ b/Xamarin.Forms.Platform.WinRT/ViewToRendererConverter.cs
@@ -0,0 +1,113 @@
+using System;
+using Windows.Foundation;
+using Windows.UI.Xaml;
+using Windows.UI.Xaml.Controls;
+
+#if WINDOWS_UWP
+
+namespace Xamarin.Forms.Platform.UWP
+#else
+
+namespace Xamarin.Forms.Platform.WinRT
+#endif
+{
+ public class ViewToRendererConverter : Windows.UI.Xaml.Data.IValueConverter
+ {
+ public object Convert(object value, Type targetType, object parameter, string language)
+ {
+ var view = value as View;
+ if (view == null)
+ {
+ var page = value as Page;
+ if (page != null)
+ {
+ IVisualElementRenderer renderer = page.GetOrCreateRenderer();
+ return renderer.ContainerElement;
+ }
+ }
+
+ if (view == null)
+ return null;
+
+ return new WrapperControl(view);
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter, string language)
+ {
+ throw new NotSupportedException();
+ }
+
+ class WrapperControl : ContentControl
+ {
+ readonly View _view;
+
+ public WrapperControl(View view)
+ {
+ _view = view;
+ _view.MeasureInvalidated += (sender, args) => { InvalidateMeasure(); };
+
+ IVisualElementRenderer renderer = Platform.CreateRenderer(view);
+ Platform.SetRenderer(view, renderer);
+
+ NotifyWrapperAwareDescendants(view, renderer);
+
+ Content = renderer.ContainerElement;
+
+ // make sure we re-measure once the template is applied
+ FrameworkElement frameworkElement = renderer.ContainerElement;
+ if (frameworkElement != null)
+ frameworkElement.Loaded += (sender, args) => InvalidateMeasure();
+ }
+
+ protected override Windows.Foundation.Size ArrangeOverride(Windows.Foundation.Size finalSize)
+ {
+ _view.IsInNativeLayout = true;
+ Layout.LayoutChildIntoBoundingRegion(_view, new Rectangle(0, 0, finalSize.Width, finalSize.Height));
+ _view.IsInNativeLayout = false;
+
+ var content = Content as FrameworkElement;
+ content.Arrange(new Rect(0, 0, finalSize.Width, finalSize.Height));
+ return finalSize;
+ }
+
+ protected override Windows.Foundation.Size MeasureOverride(Windows.Foundation.Size availableSize)
+ {
+ var content = Content as FrameworkElement;
+ content?.Measure(availableSize);
+ Size request = _view.Measure(availableSize.Width, availableSize.Height, MeasureFlags.IncludeMargins).Request;
+
+ var result = new Windows.Foundation.Size();
+ if (_view.HorizontalOptions.Alignment == LayoutAlignment.Fill && !double.IsInfinity(availableSize.Width) && availableSize.Width != 0)
+ {
+ result = new Windows.Foundation.Size(availableSize.Width, request.Height);
+ }
+ else
+ {
+ result = new Windows.Foundation.Size(request.Width, request.Height);
+ }
+
+ _view.Layout(new Rectangle(0, 0, result.Width, result.Width));
+ return result;
+ }
+
+ void NotifyWrapperAwareDescendants(Element currentView, IVisualElementRenderer currentRenderer)
+ {
+ // If any of the child renderers need to handle anything differently because they're in
+ // a wrapper in a list view, let them know that they're being wrapped
+ var wrapperAwareRenderer = currentRenderer as IWrapperAware;
+ wrapperAwareRenderer?.NotifyWrapped();
+
+ foreach (Element child in currentView.LogicalChildren)
+ {
+ var childView = child as View;
+ if (childView == null)
+ {
+ continue;
+ }
+
+ NotifyWrapperAwareDescendants(childView, Platform.GetRenderer(childView));
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WinRT/VisualElementChangedEventArgs.cs b/Xamarin.Forms.Platform.WinRT/VisualElementChangedEventArgs.cs
new file mode 100644
index 00000000..d703901c
--- /dev/null
+++ b/Xamarin.Forms.Platform.WinRT/VisualElementChangedEventArgs.cs
@@ -0,0 +1,30 @@
+using System;
+
+#if WINDOWS_UWP
+
+namespace Xamarin.Forms.Platform.UWP
+#else
+
+namespace Xamarin.Forms.Platform.WinRT
+#endif
+{
+ public class VisualElementChangedEventArgs : ElementChangedEventArgs<VisualElement>
+ {
+ public VisualElementChangedEventArgs(VisualElement oldElement, VisualElement newElement) : base(oldElement, newElement)
+ {
+ }
+ }
+
+ public class ElementChangedEventArgs<TElement> : EventArgs where TElement : Element
+ {
+ public ElementChangedEventArgs(TElement oldElement, TElement newElement)
+ {
+ OldElement = oldElement;
+ NewElement = newElement;
+ }
+
+ public TElement NewElement { get; private set; }
+
+ public TElement OldElement { get; private set; }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WinRT/VisualElementExtensions.cs b/Xamarin.Forms.Platform.WinRT/VisualElementExtensions.cs
new file mode 100644
index 00000000..9dfd12bb
--- /dev/null
+++ b/Xamarin.Forms.Platform.WinRT/VisualElementExtensions.cs
@@ -0,0 +1,56 @@
+using System;
+
+#if WINDOWS_UWP
+
+namespace Xamarin.Forms.Platform.UWP
+#else
+
+namespace Xamarin.Forms.Platform.WinRT
+#endif
+{
+ public static class VisualElementExtensions
+ {
+ public static IVisualElementRenderer GetOrCreateRenderer(this VisualElement self)
+ {
+ if (self == null)
+ throw new ArgumentNullException("self");
+
+ IVisualElementRenderer renderer = Platform.GetRenderer(self);
+ if (renderer == null)
+ {
+ renderer = RendererFactory.CreateRenderer(self);
+ Platform.SetRenderer(self, renderer);
+ }
+
+ return renderer;
+ }
+
+ internal static void Cleanup(this VisualElement self)
+ {
+ if (self == null)
+ throw new ArgumentNullException("self");
+
+ IVisualElementRenderer renderer = Platform.GetRenderer(self);
+
+ foreach (Element element in self.Descendants())
+ {
+ var visual = element as VisualElement;
+ if (visual == null)
+ continue;
+
+ IVisualElementRenderer childRenderer = Platform.GetRenderer(visual);
+ if (childRenderer != null)
+ {
+ childRenderer.Dispose();
+ Platform.SetRenderer(visual, null);
+ }
+ }
+
+ if (renderer != null)
+ {
+ renderer.Dispose();
+ Platform.SetRenderer(self, null);
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WinRT/VisualElementPackager.cs b/Xamarin.Forms.Platform.WinRT/VisualElementPackager.cs
new file mode 100644
index 00000000..1c4036e0
--- /dev/null
+++ b/Xamarin.Forms.Platform.WinRT/VisualElementPackager.cs
@@ -0,0 +1,147 @@
+using System;
+using System.Collections.ObjectModel;
+using Windows.UI.Xaml.Controls;
+
+#if WINDOWS_UWP
+
+namespace Xamarin.Forms.Platform.UWP
+#else
+
+namespace Xamarin.Forms.Platform.WinRT
+#endif
+{
+ public class VisualElementPackager : IDisposable
+ {
+ readonly int _column;
+ readonly int _columnSpan;
+
+ readonly Panel _panel;
+ readonly IVisualElementRenderer _renderer;
+ readonly int _row;
+ readonly int _rowSpan;
+ bool _disposed;
+ bool _isLoaded;
+
+ public VisualElementPackager(IVisualElementRenderer renderer)
+ {
+ if (renderer == null)
+ throw new ArgumentNullException("renderer");
+
+ _renderer = renderer;
+
+ _panel = renderer.ContainerElement as Panel;
+ if (_panel == null)
+ throw new ArgumentException("Renderer's container element must be a Panel");
+ }
+
+ public VisualElementPackager(IVisualElementRenderer renderer, int row = 0, int rowSpan = 0, int column = 0, int columnSpan = 0) : this(renderer)
+ {
+ _row = row;
+ _rowSpan = rowSpan;
+ _column = column;
+ _columnSpan = columnSpan;
+ }
+
+ public void Dispose()
+ {
+ if (_disposed)
+ return;
+
+ _disposed = true;
+
+ VisualElement element = _renderer.Element;
+ if (element != null)
+ {
+ element.ChildAdded -= OnChildAdded;
+ element.ChildRemoved -= OnChildRemoved;
+ }
+ }
+
+ public void Load()
+ {
+ if (_isLoaded)
+ return;
+
+ _isLoaded = true;
+ _renderer.Element.ChildAdded += OnChildAdded;
+ _renderer.Element.ChildRemoved += OnChildRemoved;
+
+ ReadOnlyCollection<Element> children = _renderer.Element.LogicalChildren;
+ for (var i = 0; i < children.Count; i++)
+ {
+ OnChildAdded(_renderer.Element, new ElementEventArgs(children[i]));
+ }
+ }
+
+ void EnsureZIndex()
+ {
+ if (_renderer.Element.LogicalChildren.Count == 0)
+ return;
+
+ for (var z = 0; z < _renderer.Element.LogicalChildren.Count; z++)
+ {
+ var child = _renderer.Element.LogicalChildren[z] as VisualElement;
+ if (child == null)
+ continue;
+
+ IVisualElementRenderer childRenderer = Platform.GetRenderer(child);
+
+ if (childRenderer == null)
+ {
+ continue;
+ }
+
+ Canvas.SetZIndex(childRenderer.ContainerElement, z + 1);
+ }
+ }
+
+ void OnChildAdded(object sender, ElementEventArgs e)
+ {
+ var view = e.Element as VisualElement;
+
+ if (view == null)
+ return;
+
+ IVisualElementRenderer childRenderer = Platform.CreateRenderer(view);
+ Platform.SetRenderer(view, childRenderer);
+
+ if (_row > 0)
+ Windows.UI.Xaml.Controls.Grid.SetRow(childRenderer.ContainerElement, _row);
+ if (_rowSpan > 0)
+ Windows.UI.Xaml.Controls.Grid.SetRowSpan(childRenderer.ContainerElement, _rowSpan);
+ if (_column > 0)
+ Windows.UI.Xaml.Controls.Grid.SetColumn(childRenderer.ContainerElement, _column);
+ if (_columnSpan > 0)
+ Windows.UI.Xaml.Controls.Grid.SetColumnSpan(childRenderer.ContainerElement, _columnSpan);
+
+ _panel.Children.Add(childRenderer.ContainerElement);
+
+ EnsureZIndex();
+ }
+
+ void OnChildRemoved(object sender, ElementEventArgs e)
+ {
+ var view = e.Element as VisualElement;
+
+ if (view == null)
+ return;
+
+ IVisualElementRenderer childRenderer = Platform.GetRenderer(view);
+ if (childRenderer != null)
+ {
+ if (_row > 0)
+ childRenderer.ContainerElement.ClearValue(Windows.UI.Xaml.Controls.Grid.RowProperty);
+ if (_rowSpan > 0)
+ childRenderer.ContainerElement.ClearValue(Windows.UI.Xaml.Controls.Grid.RowSpanProperty);
+ if (_column > 0)
+ childRenderer.ContainerElement.ClearValue(Windows.UI.Xaml.Controls.Grid.ColumnProperty);
+ if (_columnSpan > 0)
+ childRenderer.ContainerElement.ClearValue(Windows.UI.Xaml.Controls.Grid.ColumnSpanProperty);
+
+ _panel.Children.Remove(childRenderer.ContainerElement);
+
+ view.Cleanup();
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WinRT/VisualElementRenderer.cs b/Xamarin.Forms.Platform.WinRT/VisualElementRenderer.cs
new file mode 100644
index 00000000..96565d74
--- /dev/null
+++ b/Xamarin.Forms.Platform.WinRT/VisualElementRenderer.cs
@@ -0,0 +1,401 @@
+using System;
+using System.ComponentModel;
+using Windows.Foundation;
+using Windows.UI.Xaml;
+using Windows.UI.Xaml.Automation;
+using Windows.UI.Xaml.Controls;
+
+#if WINDOWS_UWP
+
+namespace Xamarin.Forms.Platform.UWP
+#else
+
+namespace Xamarin.Forms.Platform.WinRT
+#endif
+{
+ public class VisualElementRenderer<TElement, TNativeElement> : Panel, IVisualElementRenderer, IDisposable, IEffectControlProvider where TElement : VisualElement
+ where TNativeElement : FrameworkElement
+ {
+ bool _disposed;
+ EventHandler<VisualElementChangedEventArgs> _elementChangedHandlers;
+ VisualElementTracker<TElement, TNativeElement> _tracker;
+
+ public TNativeElement Control { get; private set; }
+
+ public TElement Element { get; private set; }
+
+ protected bool AutoPackage { get; set; } = true;
+
+ protected bool AutoTrack { get; set; } = true;
+
+ protected VisualElementTracker<TElement, TNativeElement> Tracker
+ {
+ get { return _tracker; }
+ set
+ {
+ if (_tracker == value)
+ return;
+
+ if (_tracker != null)
+ {
+ _tracker.Dispose();
+ _tracker.Updated -= OnTrackerUpdated;
+ }
+
+ _tracker = value;
+
+ if (_tracker != null)
+ {
+ _tracker.Updated += OnTrackerUpdated;
+ UpdateTracker();
+ }
+ }
+ }
+
+ VisualElementPackager Packager { get; set; }
+
+ public void Dispose()
+ {
+ Dispose(true);
+ }
+
+ void IEffectControlProvider.RegisterEffect(Effect effect)
+ {
+ var platformEffect = effect as PlatformEffect;
+ if (platformEffect != null)
+ OnRegisterEffect(platformEffect);
+ }
+
+ public FrameworkElement ContainerElement
+ {
+ get { return this; }
+ }
+
+ VisualElement IVisualElementRenderer.Element
+ {
+ get { return Element; }
+ }
+
+ event EventHandler<VisualElementChangedEventArgs> IVisualElementRenderer.ElementChanged
+ {
+ add
+ {
+ if (_elementChangedHandlers == null)
+ _elementChangedHandlers = value;
+ else
+ _elementChangedHandlers = (EventHandler<VisualElementChangedEventArgs>)Delegate.Combine(_elementChangedHandlers, value);
+ }
+
+ remove { _elementChangedHandlers = (EventHandler<VisualElementChangedEventArgs>)Delegate.Remove(_elementChangedHandlers, value); }
+ }
+
+ public virtual SizeRequest GetDesiredSize(double widthConstraint, double heightConstraint)
+ {
+ if (Children.Count == 0)
+ return new SizeRequest();
+
+ var constraint = new Windows.Foundation.Size(widthConstraint, heightConstraint);
+ TNativeElement child = Control;
+
+ child.Measure(constraint);
+ var result = new Size(Math.Ceiling(child.DesiredSize.Width), Math.Ceiling(child.DesiredSize.Height));
+
+ return new SizeRequest(result);
+ }
+
+ public void SetElement(VisualElement element)
+ {
+ TElement oldElement = Element;
+ Element = (TElement)element;
+
+ if (oldElement != null)
+ {
+ oldElement.PropertyChanged -= OnElementPropertyChanged;
+ oldElement.FocusChangeRequested -= OnElementFocusChangeRequested;
+ }
+
+ if (element != null)
+ {
+ Element.PropertyChanged += OnElementPropertyChanged;
+ Element.FocusChangeRequested += OnElementFocusChangeRequested;
+
+ if (AutoPackage && Packager == null)
+ Packager = new VisualElementPackager(this);
+
+ if (AutoTrack && Tracker == null)
+ {
+ Tracker = new VisualElementTracker<TElement, TNativeElement>();
+ }
+
+ // Disabled until reason for crashes with unhandled exceptions is discovered
+ // Without this some layouts may end up with improper sizes, however their children
+ // will position correctly
+ //Loaded += (sender, args) => {
+ if (Packager != null)
+ Packager.Load();
+ //};
+ }
+
+ OnElementChanged(new ElementChangedEventArgs<TElement>(oldElement, Element));
+
+ var controller = (IElementController)oldElement;
+ if (controller != null && controller.EffectControlProvider == this)
+ {
+ controller.EffectControlProvider = null;
+ }
+
+ controller = element;
+ if (controller != null)
+ controller.EffectControlProvider = this;
+ }
+
+ public event EventHandler<ElementChangedEventArgs<TElement>> ElementChanged;
+
+ protected override Windows.Foundation.Size ArrangeOverride(Windows.Foundation.Size finalSize)
+ {
+ if (Element == null || finalSize.Width * finalSize.Height == 0)
+ return finalSize;
+
+ Element.IsInNativeLayout = true;
+
+ if (Control != null)
+ {
+ Control.Arrange(new Rect(0, 0, finalSize.Width, finalSize.Height));
+ }
+
+ for (var i = 0; i < Element.LogicalChildren.Count; i++)
+ {
+ var child = Element.LogicalChildren[i] as VisualElement;
+ if (child == null)
+ continue;
+ IVisualElementRenderer renderer = Platform.GetRenderer(child);
+ if (renderer == null)
+ continue;
+ Rectangle bounds = child.Bounds;
+
+ renderer.ContainerElement.Arrange(new Rect(bounds.X, bounds.Y, Math.Max(0, bounds.Width), Math.Max(0, bounds.Height)));
+ }
+
+ Element.IsInNativeLayout = false;
+
+ return finalSize;
+ }
+
+ protected virtual void Dispose(bool disposing)
+ {
+ if (!disposing || _disposed)
+ return;
+
+ _disposed = true;
+
+ Tracker?.Dispose();
+ Tracker = null;
+
+ Packager?.Dispose();
+ Packager = null;
+
+ SetNativeControl(null);
+ SetElement(null);
+ }
+
+ protected override Windows.Foundation.Size MeasureOverride(Windows.Foundation.Size availableSize)
+ {
+ if (Element == null || availableSize.Width * availableSize.Height == 0)
+ return new Windows.Foundation.Size(0, 0);
+
+ Element.IsInNativeLayout = true;
+
+ for (var i = 0; i < Element.LogicalChildren.Count; i++)
+ {
+ var child = Element.LogicalChildren[i] as VisualElement;
+ if (child == null)
+ continue;
+ IVisualElementRenderer renderer = Platform.GetRenderer(child);
+ if (renderer == null)
+ continue;
+
+ renderer.ContainerElement.Measure(availableSize);
+ }
+
+ double width = Math.Max(0, Element.Width);
+ double height = Math.Max(0, Element.Height);
+ var result = new Windows.Foundation.Size(width, height);
+ if (Control != null)
+ {
+ double w = Element.Width;
+ double h = Element.Height;
+ if (w == -1)
+ w = availableSize.Width;
+ if (h == -1)
+ h = availableSize.Height;
+ w = Math.Max(0, w);
+ h = Math.Max(0, h);
+ Control.Measure(new Windows.Foundation.Size(w, h));
+ }
+
+ Element.IsInNativeLayout = false;
+
+ return result;
+ }
+
+ protected virtual void OnElementChanged(ElementChangedEventArgs<TElement> e)
+ {
+ var args = new VisualElementChangedEventArgs(e.OldElement, e.NewElement);
+ if (_elementChangedHandlers != null)
+ _elementChangedHandlers(this, args);
+
+ EventHandler<ElementChangedEventArgs<TElement>> changed = ElementChanged;
+ if (changed != null)
+ changed(this, e);
+ }
+
+ protected virtual void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ if (e.PropertyName == VisualElement.IsEnabledProperty.PropertyName)
+ UpdateEnabled();
+ else if (e.PropertyName == VisualElement.BackgroundColorProperty.PropertyName)
+ UpdateBackgroundColor();
+ }
+
+ protected virtual void OnRegisterEffect(PlatformEffect effect)
+ {
+ effect.Container = this;
+ effect.Control = Control;
+ }
+
+ protected virtual void SetAutomationId(string id)
+ {
+ SetValue(AutomationProperties.AutomationIdProperty, id);
+ }
+
+ protected void SetNativeControl(TNativeElement control)
+ {
+ TNativeElement oldControl = Control;
+ Control = control;
+
+ if (oldControl != null)
+ {
+ Children.Remove(oldControl);
+
+ oldControl.Loaded -= OnControlLoaded;
+ oldControl.GotFocus -= OnControlGotFocus;
+ oldControl.LostFocus -= OnControlLostFocus;
+ }
+
+ UpdateTracker();
+
+ if (control == null)
+ return;
+
+ Control.HorizontalAlignment = HorizontalAlignment.Stretch;
+ Control.VerticalAlignment = VerticalAlignment.Stretch;
+
+ Children.Add(control);
+
+ Element.IsNativeStateConsistent = false;
+ control.Loaded += OnControlLoaded;
+
+ control.GotFocus += OnControlGotFocus;
+ control.LostFocus += OnControlLostFocus;
+
+ UpdateBackgroundColor();
+
+ if (Element != null && !string.IsNullOrEmpty(Element.AutomationId))
+ SetAutomationId(Element.AutomationId);
+ }
+
+ protected virtual void UpdateBackgroundColor()
+ {
+ Color backgroundColor = Element.BackgroundColor;
+ var control = Control as Control;
+ if (control != null)
+ {
+ if (!backgroundColor.IsDefault)
+ {
+ control.Background = backgroundColor.ToBrush();
+ }
+ else
+ {
+ control.ClearValue(Windows.UI.Xaml.Controls.Control.BackgroundProperty);
+ }
+ }
+ else
+ {
+ if (!backgroundColor.IsDefault)
+ {
+ Background = backgroundColor.ToBrush();
+ }
+ else
+ {
+ ClearValue(BackgroundProperty);
+ }
+ }
+ }
+
+ protected virtual void UpdateNativeControl()
+ {
+ UpdateEnabled();
+ }
+
+ internal virtual void OnElementFocusChangeRequested(object sender, VisualElement.FocusRequestArgs args)
+ {
+ var control = Control as Control;
+ if (control == null)
+ return;
+
+ if (args.Focus)
+ args.Result = control.Focus(FocusState.Programmatic);
+ else
+ {
+ UnfocusControl(control);
+ args.Result = true;
+ }
+ }
+
+ internal void UnfocusControl(Control control)
+ {
+ if (control == null || !control.IsEnabled)
+ return;
+
+ control.IsEnabled = false;
+ control.IsEnabled = true;
+ }
+
+ void OnControlGotFocus(object sender, RoutedEventArgs args)
+ {
+ ((IVisualElementController)Element).SetValueFromRenderer(VisualElement.IsFocusedPropertyKey, true);
+ }
+
+ void OnControlLoaded(object sender, RoutedEventArgs args)
+ {
+ Element.IsNativeStateConsistent = true;
+ }
+
+ void OnControlLostFocus(object sender, RoutedEventArgs args)
+ {
+ ((IVisualElementController)Element).SetValueFromRenderer(VisualElement.IsFocusedPropertyKey, false);
+ }
+
+ void OnTrackerUpdated(object sender, EventArgs e)
+ {
+ UpdateNativeControl();
+ }
+
+ void UpdateEnabled()
+ {
+ var control = Control as Control;
+ if (control != null)
+ control.IsEnabled = Element.IsEnabled;
+ }
+
+ void UpdateTracker()
+ {
+ if (_tracker == null)
+ return;
+
+ _tracker.Control = Control;
+ _tracker.Element = Element;
+ _tracker.Container = ContainerElement;
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WinRT/VisualElementTracker.cs b/Xamarin.Forms.Platform.WinRT/VisualElementTracker.cs
new file mode 100644
index 00000000..0c3f7595
--- /dev/null
+++ b/Xamarin.Forms.Platform.WinRT/VisualElementTracker.cs
@@ -0,0 +1,534 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Collections.Specialized;
+using System.ComponentModel;
+using System.Linq;
+using Windows.UI.Xaml;
+using Windows.UI.Xaml.Input;
+using Windows.UI.Xaml.Media;
+
+#if WINDOWS_UWP
+
+namespace Xamarin.Forms.Platform.UWP
+#else
+
+namespace Xamarin.Forms.Platform.WinRT
+#endif
+{
+ public class VisualElementTracker<TElement, TNativeElement> : IDisposable where TElement : VisualElement where TNativeElement : FrameworkElement
+ {
+ readonly NotifyCollectionChangedEventHandler _collectionChangedHandler;
+ readonly List<uint> _fingers = new List<uint>();
+ FrameworkElement _container;
+ TNativeElement _control;
+ TElement _element;
+
+ bool _invalidateArrangeNeeded;
+
+ bool _isDisposed;
+ bool _isPanning;
+ bool _isPinching;
+ bool _wasPanGestureStartedSent;
+ bool _wasPinchGestureStartedSent;
+
+ public VisualElementTracker()
+ {
+ _collectionChangedHandler = ModelGestureRecognizersOnCollectionChanged;
+ }
+
+ public FrameworkElement Container
+ {
+ get { return _container; }
+ set
+ {
+ if (_container == value)
+ return;
+
+ if (_container != null)
+ {
+ _container.Tapped -= OnTap;
+ _container.DoubleTapped -= OnDoubleTap;
+ _container.ManipulationDelta -= OnManipulationDelta;
+ _container.ManipulationStarted -= OnManipulationStarted;
+ _container.ManipulationCompleted -= OnManipulationCompleted;
+ _container.PointerPressed -= OnPointerPressed;
+ _container.PointerExited -= OnPointerExited;
+ _container.PointerReleased -= OnPointerReleased;
+ _container.PointerCanceled -= OnPointerCanceled;
+ }
+
+ _container = value;
+
+ UpdatingGestureRecognizers();
+
+ UpdateNativeControl();
+ }
+ }
+
+ public TNativeElement Control
+ {
+ get { return _control; }
+ set
+ {
+ if (_control == value)
+ return;
+
+ _control = value;
+ UpdateNativeControl();
+ }
+ }
+
+ public TElement Element
+ {
+ get { return _element; }
+ set
+ {
+ if (_element == value)
+ return;
+
+ if (_element != null)
+ {
+ _element.BatchCommitted -= OnRedrawNeeded;
+ _element.PropertyChanged -= OnPropertyChanged;
+
+ var view = _element as View;
+ if (view != null)
+ {
+ var oldRecognizers = (ObservableCollection<IGestureRecognizer>)view.GestureRecognizers;
+ oldRecognizers.CollectionChanged -= _collectionChangedHandler;
+ }
+ }
+
+ _element = value;
+
+ if (_element != null)
+ {
+ _element.BatchCommitted += OnRedrawNeeded;
+ _element.PropertyChanged += OnPropertyChanged;
+
+ var view = _element as View;
+ if (view != null)
+ {
+ var newRecognizers = (ObservableCollection<IGestureRecognizer>)view.GestureRecognizers;
+ newRecognizers.CollectionChanged += _collectionChangedHandler;
+ }
+ }
+
+ UpdateNativeControl();
+ }
+ }
+
+ public void Dispose()
+ {
+ Dispose(true);
+ }
+
+ public event EventHandler Updated;
+
+ protected virtual void Dispose(bool disposing)
+ {
+ if (_isDisposed)
+ return;
+
+ _isDisposed = true;
+
+ if (!disposing)
+ return;
+
+ if (_container != null)
+ {
+ _container.PointerPressed -= OnPointerPressed;
+ _container.PointerExited -= OnPointerExited;
+ _container.PointerReleased -= OnPointerReleased;
+ _container.PointerCanceled -= OnPointerCanceled;
+ _container.Tapped -= OnTap;
+ _container.DoubleTapped -= OnDoubleTap;
+ _container.ManipulationDelta -= OnManipulationDelta;
+ _container.ManipulationStarted -= OnManipulationStarted;
+ _container.ManipulationCompleted -= OnManipulationCompleted;
+ }
+
+ if (_element != null)
+ {
+ _element.BatchCommitted -= OnRedrawNeeded;
+ _element.PropertyChanged -= OnPropertyChanged;
+
+ var view = _element as View;
+ if (view != null)
+ {
+ var oldRecognizers = (ObservableCollection<IGestureRecognizer>)view.GestureRecognizers;
+ oldRecognizers.CollectionChanged -= _collectionChangedHandler;
+ }
+ }
+
+ Control = null;
+ Element = null;
+ Container = null;
+ }
+
+ protected virtual void OnPropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ if (Element.Batched)
+ {
+ if (e.PropertyName == VisualElement.XProperty.PropertyName || e.PropertyName == VisualElement.YProperty.PropertyName || e.PropertyName == VisualElement.WidthProperty.PropertyName ||
+ e.PropertyName == VisualElement.HeightProperty.PropertyName)
+ {
+ _invalidateArrangeNeeded = true;
+ }
+ return;
+ }
+
+ if (e.PropertyName == VisualElement.XProperty.PropertyName || e.PropertyName == VisualElement.YProperty.PropertyName || e.PropertyName == VisualElement.WidthProperty.PropertyName ||
+ e.PropertyName == VisualElement.HeightProperty.PropertyName)
+ {
+ MaybeInvalidate();
+ }
+ else if (e.PropertyName == VisualElement.AnchorXProperty.PropertyName || e.PropertyName == VisualElement.AnchorYProperty.PropertyName)
+ {
+ UpdateScaleAndRotation(Element, Container);
+ }
+ else if (e.PropertyName == VisualElement.ScaleProperty.PropertyName)
+ {
+ UpdateScaleAndRotation(Element, Container);
+ }
+ else if (e.PropertyName == VisualElement.TranslationXProperty.PropertyName || e.PropertyName == VisualElement.TranslationYProperty.PropertyName ||
+ e.PropertyName == VisualElement.RotationProperty.PropertyName || e.PropertyName == VisualElement.RotationXProperty.PropertyName || e.PropertyName == VisualElement.RotationYProperty.PropertyName)
+ {
+ UpdateRotation(Element, Container);
+ }
+ else if (e.PropertyName == VisualElement.IsVisibleProperty.PropertyName)
+ {
+ UpdateVisibility(Element, Container);
+ }
+ else if (e.PropertyName == VisualElement.OpacityProperty.PropertyName)
+ {
+ UpdateOpacity(Element, Container);
+ }
+ else if (e.PropertyName == VisualElement.InputTransparentProperty.PropertyName)
+ {
+ UpdateInputTransparent(Element, Container);
+ }
+ }
+
+ protected virtual void UpdateNativeControl()
+ {
+ if (Element == null || Container == null)
+ return;
+
+ UpdateVisibility(Element, Container);
+ UpdateOpacity(Element, Container);
+ UpdateScaleAndRotation(Element, Container);
+ UpdateInputTransparent(Element, Container);
+
+ if (_invalidateArrangeNeeded)
+ {
+ MaybeInvalidate();
+ }
+ _invalidateArrangeNeeded = false;
+
+ OnUpdated();
+ }
+
+ void HandlePan(ManipulationDeltaRoutedEventArgs e, View view)
+ {
+ if (view == null)
+ return;
+
+ _isPanning = true;
+
+ foreach (PanGestureRecognizer recognizer in view.GestureRecognizers.GetGesturesFor<PanGestureRecognizer>().Where(g => g.TouchPoints == _fingers.Count))
+ {
+ if (!_wasPanGestureStartedSent)
+ {
+ ((IPanGestureController)recognizer).SendPanStarted(view, Application.Current.PanGestureId);
+ }
+ ((IPanGestureController)recognizer).SendPan(view, e.Delta.Translation.X + e.Cumulative.Translation.X, e.Delta.Translation.Y + e.Cumulative.Translation.Y, Application.Current.PanGestureId);
+ }
+ _wasPanGestureStartedSent = true;
+ }
+
+ void HandlePinch(ManipulationDeltaRoutedEventArgs e, View view)
+ {
+ if (_fingers.Count < 2 || view == null)
+ return;
+
+ _isPinching = true;
+
+ Windows.Foundation.Point translationPoint = e.Container.TransformToVisual(Container).TransformPoint(e.Position);
+
+ var scaleOriginPoint = new Point(translationPoint.X / view.Width, translationPoint.Y / view.Height);
+ IEnumerable<PinchGestureRecognizer> pinchGestures = view.GestureRecognizers.GetGesturesFor<PinchGestureRecognizer>();
+ foreach (PinchGestureRecognizer recognizer in pinchGestures)
+ {
+ if (!_wasPinchGestureStartedSent)
+ {
+ ((IPinchGestureController)recognizer).SendPinchStarted(view, scaleOriginPoint);
+ }
+ ((IPinchGestureController)recognizer).SendPinch(view, e.Delta.Scale, scaleOriginPoint);
+ }
+ _wasPinchGestureStartedSent = true;
+ }
+
+ void MaybeInvalidate()
+ {
+ if (Element.IsInNativeLayout)
+ return;
+ var parent = (FrameworkElement)Container.Parent;
+ parent?.InvalidateMeasure();
+ Container.InvalidateMeasure();
+ }
+
+ void ModelGestureRecognizersOnCollectionChanged(object sender, NotifyCollectionChangedEventArgs notifyCollectionChangedEventArgs)
+ {
+ UpdatingGestureRecognizers();
+ }
+
+ void OnDoubleTap(object sender, DoubleTappedRoutedEventArgs e)
+ {
+ var view = Element as View;
+ if (view == null)
+ return;
+
+ IEnumerable<TapGestureRecognizer> doubleTapGestures = view.GestureRecognizers.GetGesturesFor<TapGestureRecognizer>(g => g.NumberOfTapsRequired == 2);
+ foreach (TapGestureRecognizer recognizer in doubleTapGestures)
+ recognizer.SendTapped(view);
+ }
+
+ void OnManipulationCompleted(object sender, ManipulationCompletedRoutedEventArgs e)
+ {
+ PinchComplete(true);
+ PanComplete(true);
+ }
+
+ void OnManipulationDelta(object sender, ManipulationDeltaRoutedEventArgs e)
+ {
+ var view = Element as View;
+ if (view == null)
+ return;
+
+ HandlePinch(e, view);
+ HandlePan(e, view);
+ }
+
+ void OnManipulationStarted(object sender, ManipulationStartedRoutedEventArgs e)
+ {
+ var view = Element as View;
+ if (view == null)
+ return;
+ _wasPinchGestureStartedSent = false;
+ _wasPanGestureStartedSent = false;
+ }
+
+ void OnPointerCanceled(object sender, PointerRoutedEventArgs e)
+ {
+ uint id = e.Pointer.PointerId;
+ if (_fingers.Contains(id))
+ _fingers.Remove(id);
+
+ PinchComplete(false);
+ PanComplete(false);
+ }
+
+ void OnPointerExited(object sender, PointerRoutedEventArgs e)
+ {
+ uint id = e.Pointer.PointerId;
+ if (_fingers.Contains(id))
+ _fingers.Remove(id);
+
+ PinchComplete(true);
+ PanComplete(true);
+ }
+
+ void OnPointerPressed(object sender, PointerRoutedEventArgs e)
+ {
+ uint id = e.Pointer.PointerId;
+ if (!_fingers.Contains(id))
+ _fingers.Add(id);
+ }
+
+ void OnPointerReleased(object sender, PointerRoutedEventArgs e)
+ {
+ uint id = e.Pointer.PointerId;
+ if (_fingers.Contains(id))
+ _fingers.Remove(id);
+
+ PinchComplete(true);
+ PanComplete(true);
+ }
+
+ void OnRedrawNeeded(object sender, EventArgs e)
+ {
+ UpdateNativeControl();
+ }
+
+ void OnTap(object sender, TappedRoutedEventArgs e)
+ {
+ var view = Element as View;
+ if (view == null)
+ return;
+
+ IEnumerable<TapGestureRecognizer> tapGestures = view.GestureRecognizers.GetGesturesFor<TapGestureRecognizer>(g => g.NumberOfTapsRequired == 1);
+ foreach (TapGestureRecognizer recognizer in tapGestures)
+ {
+ recognizer.SendTapped(view);
+ e.Handled = true;
+ }
+ }
+
+ void OnUpdated()
+ {
+ if (Updated != null)
+ Updated(this, EventArgs.Empty);
+ }
+
+ void PanComplete(bool success)
+ {
+ var view = Element as View;
+ if (view == null || !_isPanning)
+ return;
+
+ foreach (PanGestureRecognizer recognizer in view.GestureRecognizers.GetGesturesFor<PanGestureRecognizer>().Where(g => g.TouchPoints == _fingers.Count))
+ {
+ if (success)
+ {
+ ((IPanGestureController)recognizer).SendPanCompleted(view, Application.Current.PanGestureId);
+ }
+ else
+ {
+ ((IPanGestureController)recognizer).SendPanCanceled(view, Application.Current.PanGestureId);
+ }
+ }
+
+ Application.Current.PanGestureId++;
+ _isPanning = false;
+ }
+
+ void PinchComplete(bool success)
+ {
+ var view = Element as View;
+ if (view == null || !_isPinching)
+ return;
+
+ IEnumerable<PinchGestureRecognizer> pinchGestures = view.GestureRecognizers.GetGesturesFor<PinchGestureRecognizer>();
+ foreach (PinchGestureRecognizer recognizer in pinchGestures)
+ {
+ if (success)
+ {
+ ((IPinchGestureController)recognizer).SendPinchEnded(view);
+ }
+ else
+ {
+ ((IPinchGestureController)recognizer).SendPinchCanceled(view);
+ }
+ }
+
+ _isPinching = false;
+ }
+
+ static void UpdateInputTransparent(VisualElement view, FrameworkElement frameworkElement)
+ {
+ frameworkElement.IsHitTestVisible = !view.InputTransparent;
+ }
+
+ static void UpdateOpacity(VisualElement view, FrameworkElement frameworkElement)
+ {
+ frameworkElement.Opacity = view.Opacity;
+ }
+
+ static void UpdateRotation(VisualElement view, FrameworkElement frameworkElement)
+ {
+ double anchorX = view.AnchorX;
+ double anchorY = view.AnchorY;
+ double rotationX = view.RotationX;
+ double rotationY = view.RotationY;
+ double rotation = view.Rotation;
+ double translationX = view.TranslationX;
+ double translationY = view.TranslationY;
+ double scale = view.Scale;
+
+ if (rotationX % 360 == 0 && rotationY % 360 == 0 && rotation % 360 == 0 && translationX == 0 && translationY == 0 && scale == 1)
+ {
+ frameworkElement.Projection = null;
+ }
+ else
+ {
+ frameworkElement.Projection = new PlaneProjection
+ {
+ CenterOfRotationX = anchorX,
+ CenterOfRotationY = anchorY,
+ GlobalOffsetX = translationX / scale,
+ GlobalOffsetY = translationY / scale,
+ RotationX = -rotationX,
+ RotationY = -rotationY,
+ RotationZ = -rotation
+ };
+ }
+ }
+
+ static void UpdateScaleAndRotation(VisualElement view, FrameworkElement frameworkElement)
+ {
+ double anchorX = view.AnchorX;
+ double anchorY = view.AnchorY;
+ double scale = view.Scale;
+ frameworkElement.RenderTransformOrigin = new Windows.Foundation.Point(anchorX, anchorY);
+ frameworkElement.RenderTransform = new ScaleTransform { ScaleX = scale, ScaleY = scale };
+
+ UpdateRotation(view, frameworkElement);
+ }
+
+ static void UpdateVisibility(VisualElement view, FrameworkElement frameworkElement)
+ {
+ frameworkElement.Visibility = view.IsVisible ? Visibility.Visible : Visibility.Collapsed;
+ }
+
+ void UpdatingGestureRecognizers()
+ {
+ var view = Element as View;
+ IList<IGestureRecognizer> gestures = view?.GestureRecognizers;
+
+ if (_container == null || gestures == null)
+ return;
+
+ _container.Tapped -= OnTap;
+ _container.DoubleTapped -= OnDoubleTap;
+ _container.ManipulationDelta -= OnManipulationDelta;
+ _container.ManipulationStarted -= OnManipulationStarted;
+ _container.ManipulationCompleted -= OnManipulationCompleted;
+ _container.PointerPressed -= OnPointerPressed;
+ _container.PointerExited -= OnPointerExited;
+ _container.PointerReleased -= OnPointerReleased;
+ _container.PointerCanceled -= OnPointerCanceled;
+
+ if (gestures.GetGesturesFor<TapGestureRecognizer>(g => g.NumberOfTapsRequired == 1).GetEnumerator().MoveNext())
+ _container.Tapped += OnTap;
+
+ if (gestures.GetGesturesFor<TapGestureRecognizer>(g => g.NumberOfTapsRequired == 2).GetEnumerator().MoveNext())
+ _container.DoubleTapped += OnDoubleTap;
+
+ bool hasPinchGesture = gestures.GetGesturesFor<PinchGestureRecognizer>().GetEnumerator().MoveNext();
+ bool hasPanGesture = gestures.GetGesturesFor<PanGestureRecognizer>().GetEnumerator().MoveNext();
+ if (!hasPinchGesture && !hasPanGesture)
+ return;
+
+ //We can't handle ManipulationMode.Scale and System , so we don't support pinch/pan on a scrollview
+ if (Element is ScrollView)
+ {
+ if (hasPinchGesture)
+ Log.Warning("Gestures", "PinchGestureRecognizer is not supported on a ScrollView in Windows Platforms");
+ if (hasPanGesture)
+ Log.Warning("Gestures", "PanGestureRecognizer is not supported on a ScrollView in Windows Platforms");
+ return;
+ }
+
+ _container.ManipulationMode = ManipulationModes.Scale | ManipulationModes.TranslateX | ManipulationModes.TranslateY;
+ _container.ManipulationDelta += OnManipulationDelta;
+ _container.ManipulationStarted += OnManipulationStarted;
+ _container.ManipulationCompleted += OnManipulationCompleted;
+ _container.PointerPressed += OnPointerPressed;
+ _container.PointerExited += OnPointerExited;
+ _container.PointerReleased += OnPointerReleased;
+ _container.PointerCanceled += OnPointerCanceled;
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WinRT/WebViewRenderer.cs b/Xamarin.Forms.Platform.WinRT/WebViewRenderer.cs
new file mode 100644
index 00000000..8074f3f1
--- /dev/null
+++ b/Xamarin.Forms.Platform.WinRT/WebViewRenderer.cs
@@ -0,0 +1,174 @@
+using System;
+using System.ComponentModel;
+using Windows.UI.Core;
+using Windows.UI.Xaml.Controls;
+
+#if WINDOWS_UWP
+
+namespace Xamarin.Forms.Platform.UWP
+#else
+
+namespace Xamarin.Forms.Platform.WinRT
+#endif
+{
+ public class WebViewRenderer : ViewRenderer<WebView, Windows.UI.Xaml.Controls.WebView>, IWebViewRenderer
+ {
+ WebNavigationEvent _eventState;
+ bool _updating;
+
+ public void LoadHtml(string html, string baseUrl)
+ {
+ /*
+ * FIXME: If baseUrl is a file URL, set the Base property to its path.
+ * Otherwise, it doesn't seem as if WebBrowser can handle it.
+ */
+ Control.NavigateToString(html);
+ }
+
+ public void LoadUrl(string url)
+ {
+ Control.Source = new Uri(url);
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ if (Control != null)
+ {
+ Control.NavigationStarting -= OnNavigationStarted;
+ Control.NavigationCompleted -= OnNavigationCompleted;
+ Control.NavigationFailed -= OnNavigationFailed;
+ }
+ }
+
+ base.Dispose(disposing);
+ }
+
+ protected override void OnElementChanged(ElementChangedEventArgs<WebView> e)
+ {
+ base.OnElementChanged(e);
+
+ if (e.OldElement != null)
+ {
+ e.OldElement.EvalRequested -= OnEvalRequested;
+ e.OldElement.GoBackRequested -= OnGoBackRequested;
+ e.OldElement.GoForwardRequested -= OnGoForwardRequested;
+ }
+
+ if (e.NewElement != null)
+ {
+ if (Control == null)
+ {
+ var webView = new Windows.UI.Xaml.Controls.WebView();
+ webView.NavigationStarting += OnNavigationStarted;
+ webView.NavigationCompleted += OnNavigationCompleted;
+ webView.NavigationFailed += OnNavigationFailed;
+ SetNativeControl(webView);
+ }
+
+ e.NewElement.EvalRequested += OnEvalRequested;
+ e.NewElement.GoForwardRequested += OnGoForwardRequested;
+ e.NewElement.GoBackRequested += OnGoBackRequested;
+
+ Load();
+ }
+ }
+
+ protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ base.OnElementPropertyChanged(sender, e);
+
+ if (e.PropertyName == WebView.SourceProperty.PropertyName)
+ {
+ if (!_updating)
+ Load();
+ }
+ }
+
+ void Load()
+ {
+ if (Element.Source != null)
+ Element.Source.Load(this);
+
+ UpdateCanGoBackForward();
+ }
+
+ async void OnEvalRequested(object sender, EventArg<string> eventArg)
+ {
+ await Control.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => Control.InvokeScript("eval", new[] { eventArg.Data }));
+ }
+
+ void OnGoBackRequested(object sender, EventArgs eventArgs)
+ {
+ if (Control.CanGoBack)
+ {
+ _eventState = WebNavigationEvent.Back;
+ Control.GoBack();
+ }
+
+ UpdateCanGoBackForward();
+ }
+
+ void OnGoForwardRequested(object sender, EventArgs eventArgs)
+ {
+ if (Control.CanGoForward)
+ {
+ _eventState = WebNavigationEvent.Forward;
+ Control.GoForward();
+ }
+
+ UpdateCanGoBackForward();
+ }
+
+ void OnNavigationCompleted(Windows.UI.Xaml.Controls.WebView sender, WebViewNavigationCompletedEventArgs e)
+ {
+ if (e.Uri != null)
+ SendNavigated(new UrlWebViewSource { Url = e.Uri.AbsoluteUri }, _eventState, WebNavigationResult.Success);
+
+ UpdateCanGoBackForward();
+ }
+
+ void OnNavigationFailed(object sender, WebViewNavigationFailedEventArgs e)
+ {
+ if (e.Uri != null)
+ SendNavigated(new UrlWebViewSource { Url = e.Uri.AbsoluteUri }, _eventState, WebNavigationResult.Failure);
+ }
+
+ void OnNavigationStarted(Windows.UI.Xaml.Controls.WebView sender, WebViewNavigationStartingEventArgs e)
+ {
+ Uri uri = e.Uri;
+
+ if (uri != null)
+ {
+ var args = new WebNavigatingEventArgs(_eventState, new UrlWebViewSource { Url = uri.AbsoluteUri }, uri.AbsoluteUri);
+
+ Element.SendNavigating(args);
+ e.Cancel = args.Cancel;
+
+ // reset in this case because this is the last event we will get
+ if (args.Cancel)
+ _eventState = WebNavigationEvent.NewPage;
+ }
+ }
+
+ void SendNavigated(UrlWebViewSource source, WebNavigationEvent evnt, WebNavigationResult result)
+ {
+ _updating = true;
+ ((IElementController)Element).SetValueFromRenderer(WebView.SourceProperty, source);
+ _updating = false;
+
+ Element.SendNavigated(new WebNavigatedEventArgs(evnt, source, source.Url, result));
+
+ UpdateCanGoBackForward();
+ _eventState = WebNavigationEvent.NewPage;
+ }
+
+ // Nasty hack because we cant bind this because OneWayToSource isn't a thing in WP8, yay
+ void UpdateCanGoBackForward()
+ {
+ Element.CanGoBack = Control.CanGoBack;
+ Element.CanGoForward = Control.CanGoForward;
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WinRT/WindowsBasePage.cs b/Xamarin.Forms.Platform.WinRT/WindowsBasePage.cs
new file mode 100644
index 00000000..31973212
--- /dev/null
+++ b/Xamarin.Forms.Platform.WinRT/WindowsBasePage.cs
@@ -0,0 +1,56 @@
+using System;
+using System.ComponentModel;
+using Windows.ApplicationModel;
+
+#if WINDOWS_UWP
+
+namespace Xamarin.Forms.Platform.UWP
+#else
+
+namespace Xamarin.Forms.Platform.WinRT
+#endif
+{
+ public abstract class WindowsBasePage : Windows.UI.Xaml.Controls.Page
+ {
+ public WindowsBasePage()
+ {
+ Windows.UI.Xaml.Application.Current.Suspending += OnApplicationSuspending;
+ Windows.UI.Xaml.Application.Current.Resuming += OnApplicationResuming;
+ }
+
+ protected Platform Platform { get; private set; }
+
+ protected abstract Platform CreatePlatform();
+
+ protected void LoadApplication(Application application)
+ {
+ if (application == null)
+ throw new ArgumentNullException("application");
+
+ Application.Current = application;
+ Platform = CreatePlatform();
+ Platform.SetPage(Application.Current.MainPage);
+ application.PropertyChanged += OnApplicationPropertyChanged;
+
+ Application.Current.SendStart();
+ }
+
+ void OnApplicationPropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ if (e.PropertyName == "MainPage")
+ Platform.SetPage(Application.Current.MainPage);
+ }
+
+ void OnApplicationResuming(object sender, object e)
+ {
+ Application.Current.SendResume();
+ }
+
+ async void OnApplicationSuspending(object sender, SuspendingEventArgs e)
+ {
+ SuspendingDeferral deferral = e.SuspendingOperation.GetDeferral();
+ await Application.Current.SendSleepAsync();
+ deferral.Complete();
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WinRT/WindowsBasePlatformServices.cs b/Xamarin.Forms.Platform.WinRT/WindowsBasePlatformServices.cs
new file mode 100644
index 00000000..bff32b78
--- /dev/null
+++ b/Xamarin.Forms.Platform.WinRT/WindowsBasePlatformServices.cs
@@ -0,0 +1,179 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Net.Http;
+using System.Reflection;
+using System.Runtime.InteropServices.WindowsRuntime;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using Windows.ApplicationModel;
+using Windows.ApplicationModel.Core;
+using Windows.Security.Cryptography;
+using Windows.Security.Cryptography.Core;
+using Windows.Storage;
+using Windows.Storage.Search;
+using Windows.Storage.Streams;
+using Windows.System;
+using Windows.UI.Core;
+using Windows.UI.Xaml;
+
+#if WINDOWS_UWP
+
+namespace Xamarin.Forms.Platform.UWP
+#else
+
+namespace Xamarin.Forms.Platform.WinRT
+#endif
+{
+ internal abstract class WindowsBasePlatformServices : IPlatformServices
+ {
+ CoreDispatcher _dispatcher;
+
+ public WindowsBasePlatformServices(CoreDispatcher dispatcher)
+ {
+ if (dispatcher == null)
+ throw new ArgumentNullException("dispatcher");
+
+ _dispatcher = dispatcher;
+ }
+
+ public void BeginInvokeOnMainThread(Action action)
+ {
+ _dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => action()).WatchForError();
+ }
+
+ public ITimer CreateTimer(Action<object> callback)
+ {
+ return new WindowsTimer(new Timer(o => callback(o), null, Timeout.Infinite, Timeout.Infinite));
+ }
+
+ public ITimer CreateTimer(Action<object> callback, object state, int dueTime, int period)
+ {
+ return new WindowsTimer(new Timer(o => callback(o), state, dueTime, period));
+ }
+
+ public ITimer CreateTimer(Action<object> callback, object state, long dueTime, long period)
+ {
+ return CreateTimer(callback, state, TimeSpan.FromMilliseconds(dueTime), TimeSpan.FromMilliseconds(period));
+ }
+
+ public ITimer CreateTimer(Action<object> callback, object state, TimeSpan dueTime, TimeSpan period)
+ {
+ return new WindowsTimer(new Timer(o => callback(o), state, dueTime, period));
+ }
+
+ public ITimer CreateTimer(Action<object> callback, object state, uint dueTime, uint period)
+ {
+ return CreateTimer(callback, state, TimeSpan.FromMilliseconds(dueTime), TimeSpan.FromMilliseconds(period));
+ }
+
+ public virtual Assembly[] GetAssemblies()
+ {
+ var options = new QueryOptions { FileTypeFilter = { ".exe", ".dll" } };
+
+ StorageFileQueryResult query = Package.Current.InstalledLocation.CreateFileQueryWithOptions(options);
+ IReadOnlyList<StorageFile> files = query.GetFilesAsync().AsTask().Result;
+
+ var assemblies = new List<Assembly>(files.Count);
+ for (var i = 0; i < files.Count; i++)
+ {
+ StorageFile file = files[i];
+ try
+ {
+ Assembly assembly = Assembly.Load(new AssemblyName { Name = Path.GetFileNameWithoutExtension(file.Name) });
+
+ assemblies.Add(assembly);
+ }
+ catch (IOException)
+ {
+ }
+ catch (BadImageFormatException)
+ {
+ }
+ }
+
+ Assembly thisAssembly = GetType().GetTypeInfo().Assembly;
+ // this happens with .NET Native
+ if (!assemblies.Contains(thisAssembly))
+ assemblies.Add(thisAssembly);
+
+ return assemblies.ToArray();
+ }
+
+ public string GetMD5Hash(string input)
+ {
+ HashAlgorithmProvider algorithm = HashAlgorithmProvider.OpenAlgorithm(HashAlgorithmNames.Md5);
+ IBuffer buffer = algorithm.HashData(Encoding.Unicode.GetBytes(input).AsBuffer());
+ return CryptographicBuffer.EncodeToHexString(buffer);
+ }
+
+ public double GetNamedSize(NamedSize size, Type targetElementType, bool useOldSizes)
+ {
+ return size.GetFontSize();
+ }
+
+ public async Task<Stream> GetStreamAsync(Uri uri, CancellationToken cancellationToken)
+ {
+ using(var client = new HttpClient())
+ {
+ HttpResponseMessage streamResponse = await client.GetAsync(uri.AbsoluteUri).ConfigureAwait(false);
+ return await streamResponse.Content.ReadAsStreamAsync().ConfigureAwait(false);
+ }
+ }
+
+ public IIsolatedStorageFile GetUserStoreForApplication()
+ {
+ return new WindowsIsolatedStorage(ApplicationData.Current.LocalFolder);
+ }
+
+ public bool IsInvokeRequired => !CoreApplication.MainView.CoreWindow.Dispatcher.HasThreadAccess;
+
+ public void OpenUriAction(Uri uri)
+ {
+ Launcher.LaunchUriAsync(uri).WatchForError();
+ }
+
+ public void StartTimer(TimeSpan interval, Func<bool> callback)
+ {
+ var timer = new DispatcherTimer { Interval = interval };
+ timer.Start();
+ timer.Tick += (sender, args) =>
+ {
+ bool result = callback();
+ if (!result)
+ timer.Stop();
+ };
+ }
+
+ internal class WindowsTimer : ITimer
+ {
+ readonly Timer _timer;
+
+ public WindowsTimer(Timer timer)
+ {
+ _timer = timer;
+ }
+
+ public void Change(int dueTime, int period)
+ {
+ _timer.Change(dueTime, period);
+ }
+
+ public void Change(long dueTime, long period)
+ {
+ Change(TimeSpan.FromMilliseconds(dueTime), TimeSpan.FromMilliseconds(period));
+ }
+
+ public void Change(TimeSpan dueTime, TimeSpan period)
+ {
+ _timer.Change(dueTime, period);
+ }
+
+ public void Change(uint dueTime, uint period)
+ {
+ Change(TimeSpan.FromMilliseconds(dueTime), TimeSpan.FromMilliseconds(period));
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WinRT/WindowsDeviceInfo.cs b/Xamarin.Forms.Platform.WinRT/WindowsDeviceInfo.cs
new file mode 100644
index 00000000..f3d6d100
--- /dev/null
+++ b/Xamarin.Forms.Platform.WinRT/WindowsDeviceInfo.cs
@@ -0,0 +1,110 @@
+using System;
+using Windows.Foundation;
+using Windows.Graphics.Display;
+using Windows.UI.Xaml;
+
+#if WINDOWS_UWP
+
+namespace Xamarin.Forms.Platform.UWP
+#else
+
+namespace Xamarin.Forms.Platform.WinRT
+#endif
+{
+ internal class WindowsDeviceInfo : DeviceInfo
+ {
+ DisplayInformation _information;
+ bool _isDisposed;
+
+ public WindowsDeviceInfo()
+ {
+ // TODO: Screen size and DPI can change at any time
+ _information = DisplayInformation.GetForCurrentView();
+ _information.OrientationChanged += OnOrientationChanged;
+ CurrentOrientation = GetDeviceOrientation(_information.CurrentOrientation);
+ }
+
+ public override Size PixelScreenSize
+ {
+ get
+ {
+ double scaling = ScalingFactor;
+ Size scaled = ScaledScreenSize;
+ double width = Math.Round(scaled.Width * scaling);
+ double height = Math.Round(scaled.Height * scaling);
+
+ return new Size(width, height);
+ }
+ }
+
+ public override Size ScaledScreenSize
+ {
+ get
+ {
+ Rect windowSize = Window.Current.Bounds;
+ return new Size(windowSize.Width, windowSize.Height);
+ }
+ }
+
+ public override double ScalingFactor
+ {
+ get
+ {
+ ResolutionScale scale = _information.ResolutionScale;
+ switch (scale)
+ {
+ case ResolutionScale.Scale120Percent:
+ return 1.2;
+ case ResolutionScale.Scale140Percent:
+ return 1.4;
+ case ResolutionScale.Scale150Percent:
+ return 1.5;
+ case ResolutionScale.Scale160Percent:
+ return 1.6;
+ case ResolutionScale.Scale180Percent:
+ return 1.8;
+ case ResolutionScale.Scale225Percent:
+ return 2.25;
+ case ResolutionScale.Scale100Percent:
+ default:
+ return 1;
+ }
+ }
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ if (_isDisposed)
+ return;
+
+ _isDisposed = true;
+ _information.OrientationChanged -= OnOrientationChanged;
+ _information = null;
+
+ base.Dispose(disposing);
+ }
+
+ static DeviceOrientation GetDeviceOrientation(DisplayOrientations orientations)
+ {
+ switch (orientations)
+ {
+ case DisplayOrientations.Landscape:
+ case DisplayOrientations.LandscapeFlipped:
+ return DeviceOrientation.Landscape;
+
+ case DisplayOrientations.Portrait:
+ case DisplayOrientations.PortraitFlipped:
+ return DeviceOrientation.Portrait;
+
+ default:
+ case DisplayOrientations.None:
+ return DeviceOrientation.Other;
+ }
+ }
+
+ void OnOrientationChanged(DisplayInformation sender, object args)
+ {
+ CurrentOrientation = GetDeviceOrientation(sender.CurrentOrientation);
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WinRT/WindowsExpressionSearch.cs b/Xamarin.Forms.Platform.WinRT/WindowsExpressionSearch.cs
new file mode 100644
index 00000000..778cc135
--- /dev/null
+++ b/Xamarin.Forms.Platform.WinRT/WindowsExpressionSearch.cs
@@ -0,0 +1,170 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Linq.Expressions;
+using System.Reflection;
+
+#if WINDOWS_UWP
+
+namespace Xamarin.Forms.Platform.UWP
+#else
+
+namespace Xamarin.Forms.Platform.WinRT
+#endif
+{
+ internal sealed class WindowsExpressionSearch : IExpressionSearch
+ {
+ List<object> _results;
+ Type _targeType;
+
+ public List<T> FindObjects<T>(Expression expression) where T : class
+ {
+ _results = new List<object>();
+ _targeType = typeof(T);
+
+ Visit(expression);
+
+ List<T> final = _results.Cast<T>().ToList();
+ _results = null;
+ return final;
+ }
+
+ void Visit(Expression expression)
+ {
+ if (expression == null)
+ return;
+
+ switch (expression.NodeType)
+ {
+ case ExpressionType.Negate:
+ case ExpressionType.NegateChecked:
+ case ExpressionType.Not:
+ case ExpressionType.Convert:
+ case ExpressionType.ConvertChecked:
+ case ExpressionType.ArrayLength:
+ case ExpressionType.Quote:
+ case ExpressionType.TypeAs:
+ case ExpressionType.UnaryPlus:
+ Visit(((UnaryExpression)expression).Operand);
+ break;
+ case ExpressionType.Add:
+ case ExpressionType.AddChecked:
+ case ExpressionType.Subtract:
+ case ExpressionType.SubtractChecked:
+ case ExpressionType.Multiply:
+ case ExpressionType.MultiplyChecked:
+ case ExpressionType.Divide:
+ case ExpressionType.Modulo:
+ case ExpressionType.Power:
+ case ExpressionType.And:
+ case ExpressionType.AndAlso:
+ case ExpressionType.Or:
+ case ExpressionType.OrElse:
+ case ExpressionType.LessThan:
+ case ExpressionType.LessThanOrEqual:
+ case ExpressionType.GreaterThan:
+ case ExpressionType.GreaterThanOrEqual:
+ case ExpressionType.Equal:
+ case ExpressionType.NotEqual:
+ case ExpressionType.Coalesce:
+ case ExpressionType.ArrayIndex:
+ case ExpressionType.RightShift:
+ case ExpressionType.LeftShift:
+ case ExpressionType.ExclusiveOr:
+ var binary = (BinaryExpression)expression;
+ Visit(binary.Left);
+ Visit(binary.Right);
+ Visit(binary.Conversion);
+ break;
+ case ExpressionType.TypeIs:
+ Visit(((TypeBinaryExpression)expression).Expression);
+ break;
+ case ExpressionType.Conditional:
+ var conditional = (ConditionalExpression)expression;
+ Visit(conditional.Test);
+ Visit(conditional.IfTrue);
+ Visit(conditional.IfFalse);
+ break;
+ case ExpressionType.MemberAccess:
+ VisitMemberAccess((MemberExpression)expression);
+ break;
+ case ExpressionType.Call:
+ var methodCall = (MethodCallExpression)expression;
+ Visit(methodCall.Object);
+ VisitList(methodCall.Arguments, Visit);
+ break;
+ case ExpressionType.Lambda:
+ Visit(((LambdaExpression)expression).Body);
+ break;
+ case ExpressionType.New:
+ VisitList(((NewExpression)expression).Arguments, Visit);
+ break;
+ case ExpressionType.NewArrayInit:
+ case ExpressionType.NewArrayBounds:
+ VisitList(((NewArrayExpression)expression).Expressions, Visit);
+ break;
+ case ExpressionType.Invoke:
+ var invocation = (InvocationExpression)expression;
+ VisitList(invocation.Arguments, Visit);
+ Visit(invocation.Expression);
+ break;
+ case ExpressionType.MemberInit:
+ var init = (MemberInitExpression)expression;
+ VisitList(init.NewExpression.Arguments, Visit);
+ VisitList(init.Bindings, VisitBinding);
+ break;
+ case ExpressionType.ListInit:
+ var init1 = (ListInitExpression)expression;
+ VisitList(init1.NewExpression.Arguments, Visit);
+ VisitList(init1.Initializers, initializer => VisitList(initializer.Arguments, Visit));
+ break;
+ case ExpressionType.Constant:
+ break;
+ default:
+ throw new ArgumentException(string.Format("Unhandled expression type: '{0}'", expression.NodeType));
+ }
+ }
+
+ void VisitBinding(MemberBinding binding)
+ {
+ switch (binding.BindingType)
+ {
+ case MemberBindingType.Assignment:
+ Visit(((MemberAssignment)binding).Expression);
+ break;
+ case MemberBindingType.MemberBinding:
+ VisitList(((MemberMemberBinding)binding).Bindings, VisitBinding);
+ break;
+ case MemberBindingType.ListBinding:
+ VisitList(((MemberListBinding)binding).Initializers, initializer => VisitList(initializer.Arguments, Visit));
+ break;
+ default:
+ throw new ArgumentException(string.Format("Unhandled binding type '{0}'", binding.BindingType));
+ }
+ }
+
+ static void VisitList<TList>(IEnumerable<TList> list, Action<TList> visitor)
+ {
+ foreach (TList element in list)
+ {
+ visitor(element);
+ }
+ }
+
+ // All important magic happens here
+ void VisitMemberAccess(MemberExpression member)
+ {
+ if (member.Expression is ConstantExpression && member.Member is FieldInfo)
+ {
+ object container = ((ConstantExpression)member.Expression).Value;
+ object value = ((FieldInfo)member.Member).GetValue(container);
+
+ if (_targeType.IsInstanceOfType(value))
+ {
+ _results.Add(value);
+ }
+ }
+ Visit(member.Expression);
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WinRT/WindowsIsolatedStorage.cs b/Xamarin.Forms.Platform.WinRT/WindowsIsolatedStorage.cs
new file mode 100644
index 00000000..a6e48fea
--- /dev/null
+++ b/Xamarin.Forms.Platform.WinRT/WindowsIsolatedStorage.cs
@@ -0,0 +1,119 @@
+using System;
+using System.IO;
+using System.Threading.Tasks;
+using Windows.Storage;
+using Windows.Storage.FileProperties;
+using Windows.Storage.Streams;
+
+#if WINDOWS_UWP
+
+namespace Xamarin.Forms.Platform.UWP
+#else
+
+namespace Xamarin.Forms.Platform.WinRT
+#endif
+{
+ internal class WindowsIsolatedStorage : IIsolatedStorageFile
+ {
+ StorageFolder _folder;
+
+ public WindowsIsolatedStorage(StorageFolder folder)
+ {
+ if (folder == null)
+ throw new ArgumentNullException("folder");
+
+ _folder = folder;
+ }
+
+ public Task CreateDirectoryAsync(string path)
+ {
+ return _folder.CreateFolderAsync(path).AsTask();
+ }
+
+ public async Task<bool> GetDirectoryExistsAsync(string path)
+ {
+ try
+ {
+ await _folder.GetFolderAsync(path).AsTask().ConfigureAwait(false);
+ return true;
+ }
+ catch (FileNotFoundException)
+ {
+ return false;
+ }
+ }
+
+ public async Task<bool> GetFileExistsAsync(string path)
+ {
+ try
+ {
+ await _folder.GetFileAsync(path).AsTask().ConfigureAwait(false);
+ return true;
+ }
+ catch (FileNotFoundException)
+ {
+ return false;
+ }
+ }
+
+ public async Task<DateTimeOffset> GetLastWriteTimeAsync(string path)
+ {
+ StorageFile file = await _folder.GetFileAsync(path).AsTask().ConfigureAwait(false);
+ BasicProperties properties = await file.GetBasicPropertiesAsync().AsTask().ConfigureAwait(false);
+ return properties.DateModified;
+ }
+
+ public async Task<Stream> OpenFileAsync(string path, FileMode mode, FileAccess access)
+ {
+ StorageFile file;
+
+ switch (mode)
+ {
+ case FileMode.CreateNew:
+ file = await _folder.CreateFileAsync(path, CreationCollisionOption.FailIfExists).AsTask().ConfigureAwait(false);
+ break;
+
+ case FileMode.Create:
+ case FileMode.Truncate: // TODO See if ReplaceExisting already truncates
+ file = await _folder.CreateFileAsync(path, CreationCollisionOption.ReplaceExisting).AsTask().ConfigureAwait(false);
+ break;
+
+ case FileMode.OpenOrCreate:
+ case FileMode.Append:
+ file = await _folder.CreateFileAsync(path, CreationCollisionOption.OpenIfExists).AsTask().ConfigureAwait(false);
+ break;
+
+ case FileMode.Open:
+ file = await _folder.GetFileAsync(path);
+ break;
+
+ default:
+ throw new ArgumentException("mode was an invalid FileMode", "mode");
+ }
+
+ switch (access)
+ {
+ case FileAccess.Read:
+ return await file.OpenStreamForReadAsync().ConfigureAwait(false);
+ case FileAccess.Write:
+ Stream stream = await file.OpenStreamForWriteAsync().ConfigureAwait(false);
+ if (mode == FileMode.Append)
+ stream.Position = stream.Length;
+
+ return stream;
+
+ case FileAccess.ReadWrite:
+ IRandomAccessStream randStream = await file.OpenAsync(FileAccessMode.ReadWrite).AsTask().ConfigureAwait(false);
+ return randStream.AsStream();
+
+ default:
+ throw new ArgumentException("access was an invalid FileAccess", "access");
+ }
+ }
+
+ public Task<Stream> OpenFileAsync(string path, FileMode mode, FileAccess access, FileShare share)
+ {
+ return OpenFileAsync(path, mode, access);
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WinRT/WindowsSerializer.cs b/Xamarin.Forms.Platform.WinRT/WindowsSerializer.cs
new file mode 100644
index 00000000..4e0ad794
--- /dev/null
+++ b/Xamarin.Forms.Platform.WinRT/WindowsSerializer.cs
@@ -0,0 +1,60 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Runtime.Serialization;
+using System.Threading.Tasks;
+using Windows.Storage;
+
+#if WINDOWS_UWP
+
+namespace Xamarin.Forms.Platform.UWP
+#else
+
+namespace Xamarin.Forms.Platform.WinRT
+#endif
+{
+ internal sealed class WindowsSerializer : IDeserializer
+ {
+ const string PropertyStoreFile = "PropertyStore.forms";
+
+ public async Task<IDictionary<string, object>> DeserializePropertiesAsync()
+ {
+ try
+ {
+ StorageFile file = await ApplicationData.Current.RoamingFolder.GetFileAsync(PropertyStoreFile).DontSync();
+ using(Stream stream = (await file.OpenReadAsync().DontSync()).AsStreamForRead())
+ {
+ if (stream.Length == 0)
+ return new Dictionary<string, object>(4);
+
+ var serializer = new DataContractSerializer(typeof(IDictionary<string, object>));
+ return (IDictionary<string, object>)serializer.ReadObject(stream);
+ }
+ }
+ catch (FileNotFoundException)
+ {
+ return new Dictionary<string, object>(4);
+ }
+ }
+
+ public async Task SerializePropertiesAsync(IDictionary<string, object> properties)
+ {
+ StorageFile file = await ApplicationData.Current.RoamingFolder.CreateFileAsync(PropertyStoreFile, CreationCollisionOption.ReplaceExisting).DontSync();
+ using(StorageStreamTransaction transaction = await file.OpenTransactedWriteAsync().DontSync())
+ {
+ try
+ {
+ Stream stream = transaction.Stream.AsStream();
+ var serializer = new DataContractSerializer(typeof(IDictionary<string, object>));
+ serializer.WriteObject(stream, properties);
+ await transaction.CommitAsync().DontSync();
+ }
+ catch (Exception e)
+ {
+ Debug.WriteLine("Could not move new serialized property file over old: " + e.Message);
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WinRT/WindowsTicker.cs b/Xamarin.Forms.Platform.WinRT/WindowsTicker.cs
new file mode 100644
index 00000000..2e8d941d
--- /dev/null
+++ b/Xamarin.Forms.Platform.WinRT/WindowsTicker.cs
@@ -0,0 +1,32 @@
+using System;
+using Windows.UI.Xaml;
+
+#if WINDOWS_UWP
+
+namespace Xamarin.Forms.Platform.UWP
+#else
+
+namespace Xamarin.Forms.Platform.WinRT
+#endif
+{
+ internal class WindowsTicker : Ticker
+ {
+ DispatcherTimer _timer;
+
+ public WindowsTicker()
+ {
+ _timer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(15) };
+ _timer.Tick += (sender, args) => SendSignals();
+ }
+
+ protected override void DisableTimer()
+ {
+ _timer.Stop();
+ }
+
+ protected override void EnableTimer()
+ {
+ _timer.Start();
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WinRT/Xamarin.Forms.Platform.WinRT.csproj b/Xamarin.Forms.Platform.WinRT/Xamarin.Forms.Platform.WinRT.csproj
new file mode 100644
index 00000000..e5793114
--- /dev/null
+++ b/Xamarin.Forms.Platform.WinRT/Xamarin.Forms.Platform.WinRT.csproj
@@ -0,0 +1,179 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
+ <PropertyGroup>
+ <MinimumVisualStudioVersion>12.0</MinimumVisualStudioVersion>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <ProjectGuid>{F3FDD7AC-8899-4E41-BFD7-EC83403E736D}</ProjectGuid>
+ <OutputType>Library</OutputType>
+ <AppDesignerFolder>Properties</AppDesignerFolder>
+ <RootNamespace>Xamarin.Forms.Platform.WinRT</RootNamespace>
+ <AssemblyName>Xamarin.Forms.Platform.WinRT</AssemblyName>
+ <DefaultLanguage>en-US</DefaultLanguage>
+ <FileAlignment>512</FileAlignment>
+ <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+ </PropertyGroup>
+ <Choose>
+ <When Condition=" '$(OS)' != 'Unix' ">
+ <PropertyGroup>
+ <TargetFrameworkVersion>v4.6</TargetFrameworkVersion>
+ <TargetFrameworkProfile>Profile32</TargetFrameworkProfile>
+ </PropertyGroup>
+ </When>
+ <When Condition=" '$(OS)' == 'Unix' ">
+ <PropertyGroup>
+ <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+ <TargetFrameworkProfile>Profile259</TargetFrameworkProfile>
+ </PropertyGroup>
+ </When>
+ </Choose>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+ <DebugSymbols>true</DebugSymbols>
+ <DebugType>full</DebugType>
+ <Optimize>false</Optimize>
+ <OutputPath>bin\Debug\</OutputPath>
+ <DefineConstants>DEBUG;TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+ <DebugType>pdbonly</DebugType>
+ <Optimize>true</Optimize>
+ <OutputPath>bin\Release\</OutputPath>
+ <DefineConstants>TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <ItemGroup>
+ <!-- A reference to the entire .NET Framework is automatically included -->
+ <ProjectReference Include="..\Xamarin.Forms.Core\Xamarin.Forms.Core.csproj">
+ <Project>{57b8b73d-c3b5-4c42-869e-7b2f17d354ac}</Project>
+ <Name>Xamarin.Forms.Core</Name>
+ </ProjectReference>
+ </ItemGroup>
+ <ItemGroup Condition=" '$(OS)' != 'Unix' ">
+ <TargetPlatform Include="WindowsPhoneApp, Version=8.1" />
+ <TargetPlatform Include="Windows, Version=8.1" />
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="..\Xamarin.Forms.Core\Properties\GlobalAssemblyInfo.cs">
+ <Link>Properties\GlobalAssemblyInfo.cs</Link>
+ </Compile>
+ </ItemGroup>
+ <ItemGroup Condition=" '$(OS)' != 'Unix' ">
+ <Compile Include="CarouselViewRenderer.cs" />
+ <Compile Include="NativeViewWrapper.cs" />
+ <Compile Include="NativeViewWrapperRenderer.cs" />
+ <Compile Include="ViewExtensions.cs" />
+ <Compile Include="LayoutExtensions.cs" />
+ <Compile Include="PlatformEffect.cs" />
+ <Compile Include="FormsComboBox.cs" />
+ <Compile Include="IWrapperAware.cs" />
+ <Compile Include="FormsDatePicker.cs" />
+ <Compile Include="FormsTimePicker.cs" />
+ <Compile Include="FormsTextBox.cs" />
+ <Compile Include="ITitleProvider.cs" />
+ <Compile Include="IToolbarProvider.cs" />
+ <Compile Include="FrameworkElementExtensions.cs" />
+ <Compile Include="ActivityIndicatorRenderer.cs" />
+ <Compile Include="AsyncValue.cs" />
+ <Compile Include="BackgroundTracker.cs" />
+ <Compile Include="BoolToVisibilityConverter.cs" />
+ <Compile Include="ButtonRenderer.cs" />
+ <Compile Include="CarouselPageRenderer.cs" />
+ <Compile Include="CellControl.cs" />
+ <Compile Include="CollapseWhenEmptyConverter.cs" />
+ <Compile Include="ColorConverter.cs" />
+ <Compile Include="ConvertExtensions.cs" />
+ <Compile Include="DatePickerRenderer.cs" />
+ <Compile Include="DefaultRenderer.cs" />
+ <Compile Include="EditorRenderer.cs" />
+ <Compile Include="EntryCellTextBox.cs" />
+ <Compile Include="EntryRenderer.cs" />
+ <Compile Include="ExportRendererAttribute.cs" />
+ <Compile Include="FileImageSourcePathConverter.cs" />
+ <Compile Include="FormsButton.cs" />
+ <Compile Include="FrameRenderer.cs" />
+ <Compile Include="ImageConverter.cs" />
+ <Compile Include="KeyboardConverter.cs" />
+ <Compile Include="KeyboardExtensions.cs" />
+ <Compile Include="ListGroupHeaderPresenter.cs" />
+ <Compile Include="ListViewGroupStyleSelector.cs" />
+ <Compile Include="MasterBackgroundConverter.cs" />
+ <Compile Include="MasterDetailControl.cs" />
+ <Compile Include="MasterDetailPageRenderer.cs" />
+ <Compile Include="MenuItemCommand.cs" />
+ <Compile Include="PageToRenderedElementConverter.cs" />
+ <Compile Include="PickerRenderer.cs" />
+ <Compile Include="StepperControl.xaml.cs">
+ <DependentUpon>StepperControl.xaml</DependentUpon>
+ </Compile>
+ <Compile Include="StepperRenderer.cs" />
+ <Compile Include="TableViewRenderer.cs" />
+ <Compile Include="TimePickerRenderer.cs" />
+ <Compile Include="ViewToRendererConverter.cs" />
+ <Compile Include="VisualElementExtensions.cs" />
+ <Compile Include="Extensions.cs" />
+ <Compile Include="FileImageSourceHandler.cs" />
+ <Compile Include="FontExtensions.cs" />
+ <Compile Include="HeightConverter.cs" />
+ <Compile Include="ICellRenderer.cs" />
+ <Compile Include="IImageSourceHandler.cs" />
+ <Compile Include="ImageLoaderSourceHandler.cs" />
+ <Compile Include="ImageRenderer.cs" />
+ <Compile Include="IVisualElementRenderer.cs" />
+ <Compile Include="LabelRenderer.cs" />
+ <Compile Include="CaseConverter.cs" />
+ <Compile Include="ListViewRenderer.cs" />
+ <Compile Include="NavigationPageRenderer.cs" />
+ <Compile Include="PageControl.xaml.cs">
+ <DependentUpon>PageControl.xaml</DependentUpon>
+ </Compile>
+ <Compile Include="Platform.cs" />
+ <Compile Include="ProgressBarRenderer.cs" />
+ <Compile Include="RendererFactory.cs" />
+ <Compile Include="BoxViewRenderer.cs" />
+ <Compile Include="LayoutRenderer.cs" />
+ <Compile Include="PageRenderer.cs" />
+ <Compile Include="ScrollViewRenderer.cs" />
+ <Compile Include="SliderRenderer.cs" />
+ <Compile Include="StreamImagesourceHandler.cs" />
+ <Compile Include="SwitchRenderer.cs" />
+ <Compile Include="TaskExtensions.cs" />
+ <Compile Include="TextCellRenderer.cs" />
+ <Compile Include="ViewRenderer.cs" />
+ <Compile Include="VisualElementPackager.cs" />
+ <Compile Include="VisualElementRenderer.cs" />
+ <Compile Include="VisualElementTracker.cs" />
+ <Compile Include="WebViewRenderer.cs" />
+ <Compile Include="WindowsBasePage.cs" />
+ <Compile Include="WindowsExpressionSearch.cs" />
+ <Compile Include="Properties\AssemblyInfo.cs" />
+ <Compile Include="WindowsDeviceInfo.cs" />
+ <Compile Include="WindowsIsolatedStorage.cs" />
+ <Compile Include="WindowsBasePlatformServices.cs" />
+ <Compile Include="WindowsSerializer.cs" />
+ <Compile Include="WindowsTicker.cs" />
+ <Compile Include="VisualElementChangedEventArgs.cs" />
+ <Compile Include="HorizontalTextAlignmentConverter.cs" />
+ <Compile Include="AlignmentExtensions.cs" />
+ <Compile Include="TextAlignmentToHorizontalAlignmentConverter.cs" />
+ <Page Include="PageControl.xaml">
+ <SubType>Designer</SubType>
+ <Generator>MSBuild:Compile</Generator>
+ </Page>
+ <Page Include="StepperControl.xaml">
+ <Generator>MSBuild:Compile</Generator>
+ <SubType>Designer</SubType>
+ </Page>
+ </ItemGroup>
+ <Import Project="$(MSBuildExtensionsPath32)\Microsoft\Portable\$(TargetFrameworkVersion)\Microsoft.Portable.CSharp.targets" />
+ <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
+ Other similar extension points exist, see Microsoft.Common.targets.
+ <Target Name="BeforeBuild">
+ </Target>
+ <Target Name="AfterBuild">
+ </Target>
+ -->
+</Project> \ No newline at end of file