summaryrefslogtreecommitdiff
path: root/Xamarin.Forms.Platform.WP8
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.WP8
downloadxamarin-forms-17fdde66d94155fc62a034fa6658995bef6fd6e5.tar.gz
xamarin-forms-17fdde66d94155fc62a034fa6658995bef6fd6e5.tar.bz2
xamarin-forms-17fdde66d94155fc62a034fa6658995bef6fd6e5.zip
Initial import
Diffstat (limited to 'Xamarin.Forms.Platform.WP8')
-rw-r--r--Xamarin.Forms.Platform.WP8/ActivityIndicatorRenderer.cs36
-rw-r--r--Xamarin.Forms.Platform.WP8/AlignmentExtensions.cs33
-rw-r--r--Xamarin.Forms.Platform.WP8/Animatable.cs13
-rw-r--r--Xamarin.Forms.Platform.WP8/AsyncValue.cs89
-rw-r--r--Xamarin.Forms.Platform.WP8/BoxViewRenderer.cs17
-rw-r--r--Xamarin.Forms.Platform.WP8/ButtonRenderer.cs119
-rw-r--r--Xamarin.Forms.Platform.WP8/CarouselPageRenderer.cs219
-rw-r--r--Xamarin.Forms.Platform.WP8/CarouselViewRenderer.cs13
-rw-r--r--Xamarin.Forms.Platform.WP8/CellControl.cs101
-rw-r--r--Xamarin.Forms.Platform.WP8/CellTemplateSelector.cs60
-rw-r--r--Xamarin.Forms.Platform.WP8/CollapseWhenEmptyConverter.cs28
-rw-r--r--Xamarin.Forms.Platform.WP8/ConvertExtensions.cs17
-rw-r--r--Xamarin.Forms.Platform.WP8/Converters/CaseConverter.cs24
-rw-r--r--Xamarin.Forms.Platform.WP8/Converters/ColorConverter.cs24
-rw-r--r--Xamarin.Forms.Platform.WP8/Converters/HorizontalTextAlignmentConverter.cs20
-rw-r--r--Xamarin.Forms.Platform.WP8/Converters/ImageConverter.cs28
-rw-r--r--Xamarin.Forms.Platform.WP8/Converters/KeyboardConverter.cs19
-rw-r--r--Xamarin.Forms.Platform.WP8/CustomContextMenu.cs27
-rw-r--r--Xamarin.Forms.Platform.WP8/DataTemplateSelector.cs17
-rw-r--r--Xamarin.Forms.Platform.WP8/DatePickerRenderer.cs59
-rw-r--r--Xamarin.Forms.Platform.WP8/Deserializer.cs86
-rw-r--r--Xamarin.Forms.Platform.WP8/EditorRenderer.cs108
-rw-r--r--Xamarin.Forms.Platform.WP8/ElementChangedEventArgs.cs24
-rw-r--r--Xamarin.Forms.Platform.WP8/EntryRenderer.cs324
-rw-r--r--Xamarin.Forms.Platform.WP8/ExportCellAttribute.cs12
-rw-r--r--Xamarin.Forms.Platform.WP8/ExportImageSourceHandlerAttribute.cs12
-rw-r--r--Xamarin.Forms.Platform.WP8/ExportRendererAttribute.cs12
-rw-r--r--Xamarin.Forms.Platform.WP8/Extensions.cs30
-rw-r--r--Xamarin.Forms.Platform.WP8/FontExtensions.cs162
-rw-r--r--Xamarin.Forms.Platform.WP8/Forms.cs254
-rw-r--r--Xamarin.Forms.Platform.WP8/FormsApplicationPage.cs86
-rw-r--r--Xamarin.Forms.Platform.WP8/FormsListPicker.cs25
-rw-r--r--Xamarin.Forms.Platform.WP8/FormsPhoneTextBox.cs236
-rw-r--r--Xamarin.Forms.Platform.WP8/FrameRenderer.cs57
-rw-r--r--Xamarin.Forms.Platform.WP8/FrameworkElementExtensions.cs73
-rw-r--r--Xamarin.Forms.Platform.WP8/ICellRenderer.cs7
-rw-r--r--Xamarin.Forms.Platform.WP8/IVisualElementRenderer.cs18
-rw-r--r--Xamarin.Forms.Platform.WP8/ImageRenderer.cs166
-rw-r--r--Xamarin.Forms.Platform.WP8/LabelRenderer.cs187
-rw-r--r--Xamarin.Forms.Platform.WP8/LayoutExtensions.cs27
-rw-r--r--Xamarin.Forms.Platform.WP8/ListViewRenderer.cs717
-rw-r--r--Xamarin.Forms.Platform.WP8/MD5.cs12
-rw-r--r--Xamarin.Forms.Platform.WP8/MD5CryptoServiceProvider.cs444
-rw-r--r--Xamarin.Forms.Platform.WP8/MasterDetailRenderer.cs183
-rw-r--r--Xamarin.Forms.Platform.WP8/NativeViewWrapper.cs24
-rw-r--r--Xamarin.Forms.Platform.WP8/NativeViewWrapperRenderer.cs62
-rw-r--r--Xamarin.Forms.Platform.WP8/NavigationMenuRenderer.cs85
-rw-r--r--Xamarin.Forms.Platform.WP8/NavigationPageRenderer.cs209
-rw-r--r--Xamarin.Forms.Platform.WP8/PageRenderer.cs18
-rw-r--r--Xamarin.Forms.Platform.WP8/PageToRendererConverter.cs24
-rw-r--r--Xamarin.Forms.Platform.WP8/PickerRenderer.cs256
-rw-r--r--Xamarin.Forms.Platform.WP8/Platform.cs628
-rw-r--r--Xamarin.Forms.Platform.WP8/PlatformEffect.cs8
-rw-r--r--Xamarin.Forms.Platform.WP8/ProgressBarRenderer.cs35
-rw-r--r--Xamarin.Forms.Platform.WP8/Properties/AssemblyInfo.cs78
-rw-r--r--Xamarin.Forms.Platform.WP8/RendererFactory.cs13
-rw-r--r--Xamarin.Forms.Platform.WP8/ResourcesProvider.cs59
-rw-r--r--Xamarin.Forms.Platform.WP8/ScrollViewRenderer.cs194
-rw-r--r--Xamarin.Forms.Platform.WP8/SearchBarRenderer.cs159
-rw-r--r--Xamarin.Forms.Platform.WP8/ServiceReferences.ClientConfig26
-rw-r--r--Xamarin.Forms.Platform.WP8/SliderRenderer.cs43
-rw-r--r--Xamarin.Forms.Platform.WP8/StepperRenderer.cs68
-rw-r--r--Xamarin.Forms.Platform.WP8/SwitchRenderer.cs50
-rw-r--r--Xamarin.Forms.Platform.WP8/TabbedPageRenderer.cs105
-rw-r--r--Xamarin.Forms.Platform.WP8/TableView.xaml31
-rw-r--r--Xamarin.Forms.Platform.WP8/TableView.xaml.cs10
-rw-r--r--Xamarin.Forms.Platform.WP8/TableViewRenderer.cs91
-rw-r--r--Xamarin.Forms.Platform.WP8/TextAlignmentToHorizontalAlignmentConverter.cs43
-rw-r--r--Xamarin.Forms.Platform.WP8/TextCellRenderer.cs88
-rw-r--r--Xamarin.Forms.Platform.WP8/TimePickerRenderer.cs60
-rw-r--r--Xamarin.Forms.Platform.WP8/Toolkit.Content/ApplicationBar.Cancel.pngbin0 -> 350 bytes
-rw-r--r--Xamarin.Forms.Platform.WP8/Toolkit.Content/ApplicationBar.Check.pngbin0 -> 414 bytes
-rw-r--r--Xamarin.Forms.Platform.WP8/Toolkit.Content/ApplicationBar.Delete.pngbin0 -> 445 bytes
-rw-r--r--Xamarin.Forms.Platform.WP8/Toolkit.Content/ApplicationBar.Select.pngbin0 -> 863 bytes
-rw-r--r--Xamarin.Forms.Platform.WP8/ViewExtensions.cs19
-rw-r--r--Xamarin.Forms.Platform.WP8/ViewRenderer.cs47
-rw-r--r--Xamarin.Forms.Platform.WP8/ViewToRendererConverter.cs77
-rw-r--r--Xamarin.Forms.Platform.WP8/VisualElementPackager.cs88
-rw-r--r--Xamarin.Forms.Platform.WP8/VisualElementRenderer.cs326
-rw-r--r--Xamarin.Forms.Platform.WP8/VisualElementTracker.cs370
-rw-r--r--Xamarin.Forms.Platform.WP8/WP8PlatformServices.cs232
-rw-r--r--Xamarin.Forms.Platform.WP8/WPResources.xaml542
-rw-r--r--Xamarin.Forms.Platform.WP8/WebViewRenderer.cs166
-rw-r--r--Xamarin.Forms.Platform.WP8/WinPhoneTicker.cs26
-rw-r--r--Xamarin.Forms.Platform.WP8/Xamarin.Forms.Platform.WP8.csproj245
-rw-r--r--Xamarin.Forms.Platform.WP8/packages.config4
86 files changed, 8834 insertions, 0 deletions
diff --git a/Xamarin.Forms.Platform.WP8/ActivityIndicatorRenderer.cs b/Xamarin.Forms.Platform.WP8/ActivityIndicatorRenderer.cs
new file mode 100644
index 00000000..0e0ceb61
--- /dev/null
+++ b/Xamarin.Forms.Platform.WP8/ActivityIndicatorRenderer.cs
@@ -0,0 +1,36 @@
+using System.ComponentModel;
+
+namespace Xamarin.Forms.Platform.WinPhone
+{
+ public class ActivityIndicatorRenderer : ViewRenderer<ActivityIndicator, System.Windows.Controls.ProgressBar>
+ {
+ protected override void OnElementChanged(ElementChangedEventArgs<ActivityIndicator> e)
+ {
+ base.OnElementChanged(e);
+
+ SetNativeControl(new System.Windows.Controls.ProgressBar());
+
+ Control.IsIndeterminate = Element.IsRunning;
+ UpdateColor();
+ }
+
+ protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ base.OnElementPropertyChanged(sender, e);
+
+ if (e.PropertyName == ActivityIndicator.IsRunningProperty.PropertyName)
+ Control.IsIndeterminate = Element.IsRunning;
+ else if (e.PropertyName == ActivityIndicator.ColorProperty.PropertyName)
+ UpdateColor();
+ }
+
+ void UpdateColor()
+ {
+ Color color = Element.Color;
+ if (color == Color.Default)
+ Control.ClearValue(System.Windows.Controls.Control.ForegroundProperty);
+ else
+ Control.Foreground = color.ToBrush();
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WP8/AlignmentExtensions.cs b/Xamarin.Forms.Platform.WP8/AlignmentExtensions.cs
new file mode 100644
index 00000000..7629d3eb
--- /dev/null
+++ b/Xamarin.Forms.Platform.WP8/AlignmentExtensions.cs
@@ -0,0 +1,33 @@
+using System.Windows;
+
+namespace Xamarin.Forms.Platform.WinPhone
+{
+ internal static class AlignmentExtensions
+ {
+ internal static System.Windows.TextAlignment ToNativeTextAlignment(this TextAlignment alignment)
+ {
+ switch (alignment)
+ {
+ case TextAlignment.Center:
+ return System.Windows.TextAlignment.Center;
+ case TextAlignment.End:
+ return System.Windows.TextAlignment.Right;
+ default:
+ return System.Windows.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.WP8/Animatable.cs b/Xamarin.Forms.Platform.WP8/Animatable.cs
new file mode 100644
index 00000000..c8a36dff
--- /dev/null
+++ b/Xamarin.Forms.Platform.WP8/Animatable.cs
@@ -0,0 +1,13 @@
+namespace Xamarin.Forms.Platform.WinPhone
+{
+ internal class Animatable : IAnimatable
+ {
+ public void BatchBegin()
+ {
+ }
+
+ public void BatchCommit()
+ {
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WP8/AsyncValue.cs b/Xamarin.Forms.Platform.WP8/AsyncValue.cs
new file mode 100644
index 00000000..0b41177d
--- /dev/null
+++ b/Xamarin.Forms.Platform.WP8/AsyncValue.cs
@@ -0,0 +1,89 @@
+//
+// 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;
+
+namespace Xamarin.Forms.Platform.WinPhone
+{
+ 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.WP8/BoxViewRenderer.cs b/Xamarin.Forms.Platform.WP8/BoxViewRenderer.cs
new file mode 100644
index 00000000..84650996
--- /dev/null
+++ b/Xamarin.Forms.Platform.WP8/BoxViewRenderer.cs
@@ -0,0 +1,17 @@
+using System.Windows.Shapes;
+
+namespace Xamarin.Forms.Platform.WinPhone
+{
+ public class BoxViewRenderer : ViewRenderer<BoxView, System.Windows.Shapes.Rectangle>
+ {
+ protected override void OnElementChanged(ElementChangedEventArgs<BoxView> e)
+ {
+ base.OnElementChanged(e);
+
+ var rect = new System.Windows.Shapes.Rectangle();
+ rect.DataContext = Element;
+ rect.SetBinding(Shape.FillProperty, new System.Windows.Data.Binding("Color") { Converter = new ColorConverter() });
+ SetNativeControl(rect);
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WP8/ButtonRenderer.cs b/Xamarin.Forms.Platform.WP8/ButtonRenderer.cs
new file mode 100644
index 00000000..886a7768
--- /dev/null
+++ b/Xamarin.Forms.Platform.WP8/ButtonRenderer.cs
@@ -0,0 +1,119 @@
+using System;
+using System.ComponentModel;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using WButton = System.Windows.Controls.Button;
+using WImage = System.Windows.Controls.Image;
+using WThickness = System.Windows.Thickness;
+
+namespace Xamarin.Forms.Platform.WinPhone
+{
+ public class ButtonRenderer : ViewRenderer<Button, WButton>
+ {
+ bool _fontApplied;
+
+ protected override void OnElementChanged(ElementChangedEventArgs<Button> e)
+ {
+ base.OnElementChanged(e);
+
+ var button = new WButton();
+ button.Click += HandleButtonClick;
+ 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();
+
+ 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();
+ }
+
+ void HandleButtonClick(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)System.Windows.Application.Current.Resources["PhoneBackgroundBrush"];
+ }
+
+ void UpdateBorderColor()
+ {
+ Control.BorderBrush = Element.BorderColor != Color.Default ? Element.BorderColor.ToBrush() : (Brush)System.Windows.Application.Current.Resources["PhoneForegroundBrush"];
+ }
+
+ void UpdateBorderWidth()
+ {
+ Control.BorderThickness = Element.BorderWidth == 0d ? new WThickness(3) : new WThickness(Element.BorderWidth);
+ }
+
+ void UpdateContent()
+ {
+ if (Element.Image != null)
+ {
+ Control.Content = new StackPanel
+ {
+ Orientation = Orientation.Horizontal,
+ Children =
+ {
+ new WImage { Source = new BitmapImage(new Uri("/" + Element.Image.File, UriKind.Relative)), Width = 30, Height = 30, Margin = new WThickness(0, 0, 20, 0) },
+ new TextBlock { Text = Element.Text }
+ }
+ };
+ }
+ 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)System.Windows.Application.Current.Resources["PhoneForegroundBrush"];
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WP8/CarouselPageRenderer.cs b/Xamarin.Forms.Platform.WP8/CarouselPageRenderer.cs
new file mode 100644
index 00000000..29446c43
--- /dev/null
+++ b/Xamarin.Forms.Platform.WP8/CarouselPageRenderer.cs
@@ -0,0 +1,219 @@
+using System;
+using System.Collections.Specialized;
+using System.ComponentModel;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using Microsoft.Phone.Controls;
+
+namespace Xamarin.Forms.Platform.WinPhone
+{
+ internal 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 HandlePropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ if (e.PropertyName == VisualElement.BackgroundColorProperty.PropertyName || e.PropertyName == Page.BackgroundImageProperty.PropertyName)
+ UpdateBackground();
+
+ base.HandlePropertyChanged(sender, e);
+ }
+
+ protected override void UpdateNativeControl()
+ {
+ base.UpdateNativeControl();
+
+ if (_backgroundNeedsUpdate)
+ UpdateBackground();
+ }
+
+ void UpdateBackground()
+ {
+ if (Model == null || Element == null)
+ return;
+
+ if (Model.BackgroundImage != null)
+ {
+ Element.SetValue(_backgroundProperty, new ImageBrush { ImageSource = new BitmapImage(new Uri(Model.BackgroundImage, UriKind.Relative)) });
+ }
+ else if (Model.BackgroundColor != Color.Default)
+ Element.SetValue(_backgroundProperty, Model.BackgroundColor.ToBrush());
+
+ _backgroundNeedsUpdate = false;
+ }
+ }
+
+ public class CarouselPagePresenter : System.Windows.Controls.ContentPresenter
+ {
+ public override void OnApplyTemplate()
+ {
+ base.OnApplyTemplate();
+
+ DependencyObject parent = VisualTreeHelper.GetParent(this);
+ while (parent != null && !(parent is PanoramaItem))
+ parent = VisualTreeHelper.GetParent(parent);
+
+ var panoramaItem = parent as PanoramaItem;
+ if (panoramaItem == null)
+ throw new Exception("No parent PanoramaItem found for carousel page");
+
+ var element = (FrameworkElement)VisualTreeHelper.GetChild(panoramaItem, 0);
+ element.SizeChanged += (s, e) =>
+ {
+ if (element.ActualWidth > 0 && element.ActualHeight > 0)
+ {
+ var carouselItem = (Page)DataContext;
+ ((CarouselPage)carouselItem.RealParent).ContainerArea = new Rectangle(0, 0, element.ActualWidth, element.ActualHeight);
+ }
+ };
+ }
+ }
+
+ public class CarouselPageRenderer : Panorama, IVisualElementRenderer
+ {
+ static readonly System.Windows.DataTemplate PageTemplate;
+
+ static readonly BindableProperty PageContainerProperty = BindableProperty.CreateAttached("PageContainer", typeof(PanoramaItem), typeof(CarouselPageRenderer), null);
+
+ CarouselPage _page;
+ BackgroundTracker<Control> _tracker;
+
+ static CarouselPageRenderer()
+ {
+ PageTemplate = (System.Windows.DataTemplate)System.Windows.Application.Current.Resources["CarouselPage"];
+ }
+
+ public CarouselPageRenderer()
+ {
+ SetBinding(TitleProperty, new System.Windows.Data.Binding("Title"));
+ }
+
+ public UIElement ContainerElement
+ {
+ get { return this; }
+ }
+
+ public VisualElement Element
+ {
+ get { return _page; }
+ }
+
+ public event EventHandler<VisualElementChangedEventArgs> ElementChanged;
+
+ public SizeRequest GetDesiredSize(double widthConstraint, double heightConstraint)
+ {
+ return new SizeRequest(new Size(widthConstraint, heightConstraint));
+ }
+
+ public void SetElement(VisualElement element)
+ {
+ CarouselPage oldElement = _page;
+ _page = (CarouselPage)element;
+ _tracker = new BackgroundTracker<Control>(BackgroundProperty) { Model = _page, Element = this };
+
+ DataContext = _page;
+
+ SelectionChanged += OnSelectionChanged;
+
+ OnPagesChanged(_page, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
+
+ _page.PagesChanged += OnPagesChanged;
+ _page.PropertyChanged += OnPropertyChanged;
+
+ Loaded += (sender, args) => _page.SendAppearing();
+ Unloaded += (sender, args) => _page.SendDisappearing();
+
+ OnElementChanged(new VisualElementChangedEventArgs(oldElement, element));
+ }
+
+ protected virtual void OnElementChanged(VisualElementChangedEventArgs e)
+ {
+ EventHandler<VisualElementChangedEventArgs> changed = ElementChanged;
+ if (changed != null)
+ changed(this, e);
+ }
+
+ static PanoramaItem GetPageContainer(BindableObject bindable)
+ {
+ return (PanoramaItem)bindable.GetValue(PageContainerProperty);
+ }
+
+ void InsertItem(object item, int index, bool newItem)
+ {
+ DependencyObject pageContent = PageTemplate.LoadContent();
+
+ var pageItem = (Page)item;
+ PanoramaItem container = GetPageContainer(pageItem);
+ if (container == null)
+ {
+ container = new PanoramaItem { DataContext = item, Content = pageContent };
+
+ SetPageContainer(pageItem, container);
+ }
+
+ Items.Insert(index, container);
+ }
+
+ void OnPagesChanged(object sender, NotifyCollectionChangedEventArgs e)
+ {
+ e.Apply(InsertItem, RemoveItem, Reset);
+ }
+
+ void OnPropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ if (e.PropertyName == "CurrentPage")
+ {
+ ContentPage current = _page.CurrentPage;
+ if (current == null)
+ return;
+
+ SetValue(SelectedItemProperty, GetPageContainer(current));
+ OnItemsChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
+ }
+ }
+
+ void OnSelectionChanged(object sender, SelectionChangedEventArgs e)
+ {
+ var panoramaItem = (PanoramaItem)SelectedItem;
+ if (panoramaItem == null)
+ _page.CurrentPage = null;
+ else
+ _page.CurrentPage = (ContentPage)panoramaItem.DataContext;
+ }
+
+ void RemoveItem(object item, int index)
+ {
+ Items.RemoveAt(index);
+ }
+
+ void Reset()
+ {
+ Items.Clear();
+
+ var i = 0;
+ foreach (Page pageItem in _page.Children)
+ InsertItem(pageItem, i++, true);
+
+ ContentPage current = _page.CurrentPage;
+ if (current != null)
+ SetValue(SelectedItemProperty, GetPageContainer(current));
+ }
+
+ static void SetPageContainer(BindableObject bindable, PanoramaItem container)
+ {
+ bindable.SetValue(PageContainerProperty, container);
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WP8/CarouselViewRenderer.cs b/Xamarin.Forms.Platform.WP8/CarouselViewRenderer.cs
new file mode 100644
index 00000000..6050d07d
--- /dev/null
+++ b/Xamarin.Forms.Platform.WP8/CarouselViewRenderer.cs
@@ -0,0 +1,13 @@
+using Microsoft.Phone.Controls;
+using SLButton = System.Windows.Controls.Button;
+using SLBinding = System.Windows.Data.Binding;
+
+namespace Xamarin.Forms.Platform.WinPhone
+{
+ public class CarouselViewRenderer : ViewRenderer<CarouselView, LongListSelector>
+ {
+ protected override void OnElementChanged(ElementChangedEventArgs<CarouselView> e)
+ {
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WP8/CellControl.cs b/Xamarin.Forms.Platform.WP8/CellControl.cs
new file mode 100644
index 00000000..743f4ba7
--- /dev/null
+++ b/Xamarin.Forms.Platform.WP8/CellControl.cs
@@ -0,0 +1,101 @@
+using System.ComponentModel;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Media;
+using Microsoft.Phone.Controls;
+
+namespace Xamarin.Forms.Platform.WinPhone
+{
+ public class CellControl : ContentControl
+ {
+ public static readonly DependencyProperty CellProperty = DependencyProperty.Register("Cell", typeof(object), typeof(CellControl),
+ new PropertyMetadata((o, e) => ((CellControl)o).SetSource((Cell)e.OldValue, (Cell)e.NewValue)));
+
+ public static readonly DependencyProperty ShowContextActionsProperty = DependencyProperty.Register("ShowContextActions", typeof(bool), typeof(CellControl), new PropertyMetadata(true));
+
+ readonly PropertyChangedEventHandler _propertyChangedHandler;
+
+ public CellControl()
+ {
+ Unloaded += (sender, args) =>
+ {
+ var cell = DataContext as Cell;
+ if (cell != null)
+ cell.SendDisappearing();
+ };
+
+ _propertyChangedHandler = OnCellPropertyChanged;
+ }
+
+ public Cell Cell
+ {
+ get { return (Cell)GetValue(CellProperty); }
+ set { SetValue(CellProperty, value); }
+ }
+
+ public bool ShowContextActions
+ {
+ get { return (bool)GetValue(ShowContextActionsProperty); }
+ set { SetValue(ShowContextActionsProperty, value); }
+ }
+
+ System.Windows.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 SetSource(Cell oldCell, Cell newCell)
+ {
+ if (oldCell != null)
+ {
+ oldCell.PropertyChanged -= _propertyChangedHandler;
+ oldCell.SendDisappearing();
+ }
+
+ DependencyObject content = null;
+ if (newCell != null)
+ {
+ newCell.SendAppearing();
+
+ if (oldCell == null || oldCell.GetType() != newCell.GetType())
+ ContentTemplate = GetTemplate(newCell);
+
+ Content = newCell;
+
+ SetupContextMenu();
+
+ newCell.PropertyChanged += _propertyChangedHandler;
+ }
+ else
+ Content = null;
+ }
+
+ void SetupContextMenu()
+ {
+ if (Content == null || !ShowContextActions)
+ return;
+
+ if (!Cell.HasContextActions)
+ {
+ if (VisualTreeHelper.GetChildrenCount(this) > 0)
+ ContextMenuService.SetContextMenu(VisualTreeHelper.GetChild(this, 0), null);
+
+ return;
+ }
+
+ ApplyTemplate();
+
+ ContextMenu menu = new CustomContextMenu();
+ menu.SetBinding(ItemsControl.ItemsSourceProperty, new System.Windows.Data.Binding("ContextActions"));
+
+ ContextMenuService.SetContextMenu(VisualTreeHelper.GetChild(this, 0), menu);
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WP8/CellTemplateSelector.cs b/Xamarin.Forms.Platform.WP8/CellTemplateSelector.cs
new file mode 100644
index 00000000..09d939fd
--- /dev/null
+++ b/Xamarin.Forms.Platform.WP8/CellTemplateSelector.cs
@@ -0,0 +1,60 @@
+using System;
+using System.Globalization;
+using System.Windows;
+
+namespace Xamarin.Forms.Platform.WinPhone
+{
+ public class HeightConverter : System.Windows.Data.IValueConverter
+ {
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ var val = (double)value;
+ return val > 0 ? val : double.NaN;
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ throw new NotImplementedException();
+ }
+ }
+
+ [Obsolete("Deprecated in favor of CellControl")]
+ public class CellTemplateSelector : DataTemplateSelector
+ {
+ public static readonly DependencyProperty SourceProperty = DependencyProperty.Register("Source", typeof(object), typeof(CellTemplateSelector),
+ new PropertyMetadata((o, e) => ((CellTemplateSelector)o).SetSource(e.OldValue, e.NewValue)));
+
+ public CellTemplateSelector()
+ {
+ Loaded += (sender, args) => SetBinding(SourceProperty, new System.Windows.Data.Binding());
+ Unloaded += (sender, args) =>
+ {
+ var cell = DataContext as Cell;
+ if (cell != null)
+ cell.SendDisappearing();
+ };
+ }
+
+ public override System.Windows.DataTemplate SelectTemplate(object item, DependencyObject container)
+ {
+ var cell = item as Cell;
+ if (cell == null)
+ return null;
+
+ var renderer = Registrar.Registered.GetHandler<ICellRenderer>(cell.GetType());
+ return renderer.GetTemplate(cell);
+ }
+
+ void SetSource(object oldSource, object newSource)
+ {
+ var oldCell = oldSource as Cell;
+ var newCell = newSource as Cell;
+
+ if (oldCell != null)
+ oldCell.SendDisappearing();
+
+ if (newCell != null)
+ newCell.SendAppearing();
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WP8/CollapseWhenEmptyConverter.cs b/Xamarin.Forms.Platform.WP8/CollapseWhenEmptyConverter.cs
new file mode 100644
index 00000000..d7dc6de6
--- /dev/null
+++ b/Xamarin.Forms.Platform.WP8/CollapseWhenEmptyConverter.cs
@@ -0,0 +1,28 @@
+using System;
+using System.Globalization;
+using System.Windows;
+
+namespace Xamarin.Forms.Platform.WinPhone
+{
+ public class CollapseWhenEmptyConverter : System.Windows.Data.IValueConverter
+ {
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ 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, CultureInfo culture)
+ {
+ throw new NotSupportedException();
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WP8/ConvertExtensions.cs b/Xamarin.Forms.Platform.WP8/ConvertExtensions.cs
new file mode 100644
index 00000000..ed96ecdf
--- /dev/null
+++ b/Xamarin.Forms.Platform.WP8/ConvertExtensions.cs
@@ -0,0 +1,17 @@
+using System.Windows.Media;
+
+namespace Xamarin.Forms.Platform.WinPhone
+{
+ internal static class ConvertExtensions
+ {
+ public static Brush ToBrush(this Color color)
+ {
+ return new SolidColorBrush(color.ToMediaColor());
+ }
+
+ public static System.Windows.Media.Color ToMediaColor(this Color color)
+ {
+ return System.Windows.Media.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.WP8/Converters/CaseConverter.cs b/Xamarin.Forms.Platform.WP8/Converters/CaseConverter.cs
new file mode 100644
index 00000000..b972fc57
--- /dev/null
+++ b/Xamarin.Forms.Platform.WP8/Converters/CaseConverter.cs
@@ -0,0 +1,24 @@
+using System;
+using System.Globalization;
+
+namespace Xamarin.Forms.Platform.WinPhone
+{
+ public class CaseConverter : System.Windows.Data.IValueConverter
+ {
+ public bool ConvertToUpper { get; set; }
+
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ if (value == null)
+ return null;
+
+ var v = (string)value;
+ return ConvertToUpper ? v.ToUpper() : v.ToLower();
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ throw new NotSupportedException();
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WP8/Converters/ColorConverter.cs b/Xamarin.Forms.Platform.WP8/Converters/ColorConverter.cs
new file mode 100644
index 00000000..1e495ecf
--- /dev/null
+++ b/Xamarin.Forms.Platform.WP8/Converters/ColorConverter.cs
@@ -0,0 +1,24 @@
+using System;
+using System.Globalization;
+using System.Windows.Media;
+
+namespace Xamarin.Forms.Platform.WinPhone
+{
+ public class ColorConverter : System.Windows.Data.IValueConverter
+ {
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ var color = (Color)value;
+ var defaultColorKey = (string)parameter;
+
+ Brush defaultBrush = defaultColorKey != null ? (Brush)System.Windows.Application.Current.Resources[defaultColorKey] : new SolidColorBrush(Colors.Transparent);
+
+ return color == Color.Default ? defaultBrush : color.ToBrush();
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ throw new NotSupportedException();
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WP8/Converters/HorizontalTextAlignmentConverter.cs b/Xamarin.Forms.Platform.WP8/Converters/HorizontalTextAlignmentConverter.cs
new file mode 100644
index 00000000..7923b1d3
--- /dev/null
+++ b/Xamarin.Forms.Platform.WP8/Converters/HorizontalTextAlignmentConverter.cs
@@ -0,0 +1,20 @@
+using System;
+using System.Globalization;
+
+namespace Xamarin.Forms.Platform.WinPhone
+{
+ public class HorizontalTextAlignmentConverter : System.Windows.Data.IValueConverter
+ {
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ var textAlign = (TextAlignment)value;
+
+ return textAlign.ToNativeTextAlignment();
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ throw new NotSupportedException();
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WP8/Converters/ImageConverter.cs b/Xamarin.Forms.Platform.WP8/Converters/ImageConverter.cs
new file mode 100644
index 00000000..2e8bdef6
--- /dev/null
+++ b/Xamarin.Forms.Platform.WP8/Converters/ImageConverter.cs
@@ -0,0 +1,28 @@
+using System;
+using System.Globalization;
+using System.Threading.Tasks;
+
+namespace Xamarin.Forms.Platform.WinPhone
+{
+ public class ImageConverter : System.Windows.Data.IValueConverter
+ {
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ var source = (ImageSource)value;
+ IImageSourceHandler handler;
+
+ if (source != null && (handler = Registrar.Registered.GetHandler<IImageSourceHandler>(source.GetType())) != null)
+ {
+ Task<System.Windows.Media.ImageSource> task = handler.LoadImageAsync(source);
+ return new AsyncValue<System.Windows.Media.ImageSource>(task, null);
+ }
+
+ return null;
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ throw new NotSupportedException();
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WP8/Converters/KeyboardConverter.cs b/Xamarin.Forms.Platform.WP8/Converters/KeyboardConverter.cs
new file mode 100644
index 00000000..cd082b29
--- /dev/null
+++ b/Xamarin.Forms.Platform.WP8/Converters/KeyboardConverter.cs
@@ -0,0 +1,19 @@
+using System;
+using System.Globalization;
+
+namespace Xamarin.Forms.Platform.WinPhone
+{
+ public class KeyboardConverter : System.Windows.Data.IValueConverter
+ {
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ var keyboard = (Keyboard)value;
+ return keyboard.ToInputScope();
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ throw new NotSupportedException();
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WP8/CustomContextMenu.cs b/Xamarin.Forms.Platform.WP8/CustomContextMenu.cs
new file mode 100644
index 00000000..4946b2b2
--- /dev/null
+++ b/Xamarin.Forms.Platform.WP8/CustomContextMenu.cs
@@ -0,0 +1,27 @@
+using System.Windows;
+using System.Windows.Controls;
+using Microsoft.Phone.Controls;
+using WMenuItem = Microsoft.Phone.Controls.MenuItem;
+using WApplication = System.Windows.Application;
+
+namespace Xamarin.Forms.Platform.WinPhone
+{
+ public sealed class CustomContextMenu : ContextMenu
+ {
+ protected override DependencyObject GetContainerForItemOverride()
+ {
+ var item = new WMenuItem();
+ item.SetBinding(HeaderedItemsControl.HeaderProperty, new System.Windows.Data.Binding("Text") { Converter = (System.Windows.Data.IValueConverter)WApplication.Current.Resources["LowerConverter"] });
+
+ item.Click += (sender, args) =>
+ {
+ IsOpen = false;
+
+ var menuItem = item.DataContext as MenuItem;
+ if (menuItem != null)
+ menuItem.Activate();
+ };
+ return item;
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WP8/DataTemplateSelector.cs b/Xamarin.Forms.Platform.WP8/DataTemplateSelector.cs
new file mode 100644
index 00000000..6a9acca6
--- /dev/null
+++ b/Xamarin.Forms.Platform.WP8/DataTemplateSelector.cs
@@ -0,0 +1,17 @@
+using System.Windows;
+using System.Windows.Controls;
+
+namespace Xamarin.Forms.Platform.WinPhone
+{
+ public abstract class DataTemplateSelector : ContentControl
+ {
+ public abstract System.Windows.DataTemplate SelectTemplate(object item, DependencyObject container);
+
+ protected override void OnContentChanged(object oldContent, object newContent)
+ {
+ base.OnContentChanged(oldContent, newContent);
+
+ ContentTemplate = SelectTemplate(newContent, this);
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WP8/DatePickerRenderer.cs b/Xamarin.Forms.Platform.WP8/DatePickerRenderer.cs
new file mode 100644
index 00000000..9553171b
--- /dev/null
+++ b/Xamarin.Forms.Platform.WP8/DatePickerRenderer.cs
@@ -0,0 +1,59 @@
+using System;
+using System.ComponentModel;
+using System.Reflection;
+using Microsoft.Phone.Controls;
+
+namespace Xamarin.Forms.Platform.WinPhone
+{
+ public class DatePickerRenderer : ViewRenderer<DatePicker, Microsoft.Phone.Controls.DatePicker>
+ {
+ protected override void OnElementChanged(ElementChangedEventArgs<DatePicker> e)
+ {
+ base.OnElementChanged(e);
+
+ var datePicker = new Microsoft.Phone.Controls.DatePicker { Value = Element.Date };
+ datePicker.ValueChanged += DatePickerOnValueChanged;
+ SetNativeControl(datePicker);
+
+ UpdateFormatString();
+ }
+
+ protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ if (e.PropertyName == "Date")
+ Control.Value = Element.Date;
+ else if (e.PropertyName == DatePicker.FormatProperty.PropertyName)
+ UpdateFormatString();
+ base.OnElementPropertyChanged(sender, e);
+ }
+
+ internal override void OnModelFocusChangeRequested(object sender, VisualElement.FocusRequestArgs args)
+ {
+ Microsoft.Phone.Controls.DatePicker control = Control;
+ if (control == null)
+ return;
+
+ if (args.Focus)
+ {
+ typeof(DateTimePickerBase).InvokeMember("OpenPickerPage", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.InvokeMethod, Type.DefaultBinder, control, null);
+ args.Result = true;
+ }
+ else
+ {
+ UnfocusControl(control);
+ args.Result = true;
+ }
+ }
+
+ void DatePickerOnValueChanged(object sender, DateTimeValueChangedEventArgs dateTimeValueChangedEventArgs)
+ {
+ if (Control.Value.HasValue)
+ ((IElementController)Element).SetValueFromRenderer(DatePicker.DateProperty, Control.Value.Value);
+ }
+
+ void UpdateFormatString()
+ {
+ Control.ValueStringFormat = "{0:" + Element.Format + "}";
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WP8/Deserializer.cs b/Xamarin.Forms.Platform.WP8/Deserializer.cs
new file mode 100644
index 00000000..a0c2de00
--- /dev/null
+++ b/Xamarin.Forms.Platform.WP8/Deserializer.cs
@@ -0,0 +1,86 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO.IsolatedStorage;
+using System.Runtime.Serialization;
+using System.Threading.Tasks;
+using System.Xml;
+
+namespace Xamarin.Forms.Platform.WinPhone
+{
+ internal class Deserializer : IDeserializer
+ {
+ const string PropertyStoreFile = "PropertyStore.forms";
+
+ public Task<IDictionary<string, object>> DeserializePropertiesAsync()
+ {
+ // Deserialize property dictionary to local storage
+ // Make sure to use Internal
+ return Task.Run(() =>
+ {
+ using(IsolatedStorageFile store = IsolatedStorageFile.GetUserStoreForApplication())
+ using(IsolatedStorageFileStream stream = store.OpenFile(PropertyStoreFile, System.IO.FileMode.OpenOrCreate))
+ using(XmlDictionaryReader reader = XmlDictionaryReader.CreateBinaryReader(stream, XmlDictionaryReaderQuotas.Max))
+ {
+ if (stream.Length == 0)
+ return null;
+
+ try
+ {
+ var dcs = new DataContractSerializer(typeof(Dictionary<string, object>));
+ return (IDictionary<string, object>)dcs.ReadObject(reader);
+ }
+ catch (Exception e)
+ {
+ Debug.WriteLine("Could not deserialize properties: " + e.Message);
+ }
+ }
+
+ return null;
+ });
+ }
+
+ public Task SerializePropertiesAsync(IDictionary<string, object> properties)
+ {
+ properties = new Dictionary<string, object>(properties);
+ // Serialize property dictionary to local storage
+ // Make sure to use Internal
+ return Task.Run(() =>
+ {
+ var success = false;
+ using(IsolatedStorageFile store = IsolatedStorageFile.GetUserStoreForApplication())
+ using(IsolatedStorageFileStream stream = store.OpenFile(PropertyStoreFile + ".tmp", System.IO.FileMode.OpenOrCreate))
+ using(XmlDictionaryWriter writer = XmlDictionaryWriter.CreateBinaryWriter(stream))
+ {
+ try
+ {
+ var dcs = new DataContractSerializer(typeof(Dictionary<string, object>));
+ dcs.WriteObject(writer, properties);
+ writer.Flush();
+ success = true;
+ }
+ catch (Exception e)
+ {
+ Debug.WriteLine("Could not serialize properties: " + e.Message);
+ }
+ }
+
+ if (!success)
+ return;
+ using(IsolatedStorageFile store = IsolatedStorageFile.GetUserStoreForApplication())
+ {
+ try
+ {
+ if (store.FileExists(PropertyStoreFile))
+ store.DeleteFile(PropertyStoreFile);
+ store.MoveFile(PropertyStoreFile + ".tmp", PropertyStoreFile);
+ }
+ 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.WP8/EditorRenderer.cs b/Xamarin.Forms.Platform.WP8/EditorRenderer.cs
new file mode 100644
index 00000000..500bcfae
--- /dev/null
+++ b/Xamarin.Forms.Platform.WP8/EditorRenderer.cs
@@ -0,0 +1,108 @@
+using System.ComponentModel;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Media;
+
+namespace Xamarin.Forms.Platform.WinPhone
+{
+ public class EditorRenderer : ViewRenderer<Editor, TextBox>
+ {
+ bool _fontApplied;
+
+ protected override void OnElementChanged(ElementChangedEventArgs<Editor> e)
+ {
+ base.OnElementChanged(e);
+
+ var textBox = new TextBox { VerticalScrollBarVisibility = ScrollBarVisibility.Visible, TextWrapping = TextWrapping.Wrap, AcceptsReturn = true };
+
+ SetNativeControl(textBox);
+
+ UpdateText();
+ UpdateInputScope();
+ UpdateTextColor();
+
+ Control.LostFocus += (sender, args) => Element.SendCompleted();
+
+ textBox.TextChanged += TextBoxOnTextChanged;
+ }
+
+ protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ base.OnElementPropertyChanged(sender, e);
+
+ if (e.PropertyName == Editor.TextProperty.PropertyName)
+ UpdateText();
+ else if (e.PropertyName == InputView.KeyboardProperty.PropertyName)
+ UpdateInputScope();
+ else 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();
+ }
+
+ protected override void UpdateBackgroundColor()
+ {
+ Control.Background = Element.BackgroundColor == Color.Default ? (Brush)System.Windows.Application.Current.Resources["PhoneTextBoxBrush"] : Element.BackgroundColor.ToBrush();
+ }
+
+ void TextBoxOnTextChanged(object sender, System.Windows.Controls.TextChangedEventArgs textChangedEventArgs)
+ {
+ ((IElementController)Element).SetValueFromRenderer(Editor.TextProperty, Control.Text);
+ }
+
+ void UpdateFont()
+ {
+ if (Control == null)
+ return;
+
+ Editor editor = Element;
+
+ bool editorIsDefault = editor.FontFamily == null && editor.FontSize == Device.GetNamedSize(NamedSize.Default, typeof(Editor), true) && editor.FontAttributes == FontAttributes.None;
+ if (editor == null || (editorIsDefault && !_fontApplied))
+ return;
+
+ if (editorIsDefault)
+ {
+ Control.ClearValue(System.Windows.Controls.Control.FontStyleProperty);
+ Control.ClearValue(System.Windows.Controls.Control.FontSizeProperty);
+ Control.ClearValue(System.Windows.Controls.Control.FontFamilyProperty);
+ Control.ClearValue(System.Windows.Controls.Control.FontWeightProperty);
+ Control.ClearValue(System.Windows.Controls.Control.FontStretchProperty);
+ }
+ else
+ Control.ApplyFont(editor);
+
+ _fontApplied = true;
+ }
+
+ void UpdateInputScope()
+ {
+ 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)
+ Control.ClearValue(System.Windows.Controls.Control.ForegroundProperty);
+ else
+ Control.Foreground = textColor.ToBrush();
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WP8/ElementChangedEventArgs.cs b/Xamarin.Forms.Platform.WP8/ElementChangedEventArgs.cs
new file mode 100644
index 00000000..903acb18
--- /dev/null
+++ b/Xamarin.Forms.Platform.WP8/ElementChangedEventArgs.cs
@@ -0,0 +1,24 @@
+using System;
+
+namespace Xamarin.Forms.Platform.WinPhone
+{
+ 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.WP8/EntryRenderer.cs b/Xamarin.Forms.Platform.WP8/EntryRenderer.cs
new file mode 100644
index 00000000..f96ca312
--- /dev/null
+++ b/Xamarin.Forms.Platform.WP8/EntryRenderer.cs
@@ -0,0 +1,324 @@
+using System;
+using System.ComponentModel;
+using System.Diagnostics;
+using System.Windows;
+using System.Windows.Input;
+using System.Windows.Media;
+using static System.String;
+using WControl = System.Windows.Controls.Control;
+
+namespace Xamarin.Forms.Platform.WinPhone
+{
+ internal static class KeyboardExtensions
+ {
+ public static InputScope ToInputScope(this Keyboard 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.EmailNameOrAddress;
+ 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.Text;
+ else if (self == Keyboard.Url)
+ name.NameValue = InputScopeNameValue.Url;
+ else if (self is CustomKeyboard)
+ {
+ var custom = (CustomKeyboard)self;
+ bool capitalizedSentenceEnabled = (custom.Flags & KeyboardFlags.CapitalizeSentence) == KeyboardFlags.CapitalizeSentence;
+ bool spellcheckEnabled = (custom.Flags & KeyboardFlags.Spellcheck) == KeyboardFlags.Spellcheck;
+ bool suggestionsEnabled = (custom.Flags & KeyboardFlags.Suggestions) == KeyboardFlags.Suggestions;
+
+ if (!capitalizedSentenceEnabled && !spellcheckEnabled && !suggestionsEnabled)
+ name.NameValue = InputScopeNameValue.Default;
+ if (!capitalizedSentenceEnabled && !spellcheckEnabled && suggestionsEnabled)
+ name.NameValue = InputScopeNameValue.Search;
+ if (!capitalizedSentenceEnabled && spellcheckEnabled && !suggestionsEnabled)
+ {
+ Debug.WriteLine("Keyboard: Suggestions cannot be disabled in Windows Phone if spellcheck is enabled");
+ name.NameValue = InputScopeNameValue.Search;
+ }
+ if (!capitalizedSentenceEnabled && spellcheckEnabled && suggestionsEnabled)
+ name.NameValue = InputScopeNameValue.Search;
+ if (capitalizedSentenceEnabled && !spellcheckEnabled && !suggestionsEnabled)
+ {
+ Debug.WriteLine("Keyboard: Suggestions cannot be disabled in Windows Phone if auto Capitalization is enabled");
+ name.NameValue = InputScopeNameValue.Chat;
+ }
+ if (capitalizedSentenceEnabled && !spellcheckEnabled && suggestionsEnabled)
+ name.NameValue = InputScopeNameValue.Chat;
+ if (capitalizedSentenceEnabled && spellcheckEnabled && !suggestionsEnabled)
+ {
+ Debug.WriteLine("Keyboard: Suggestions cannot be disabled in Windows Phone if spellcheck is enabled");
+ name.NameValue = InputScopeNameValue.Text;
+ }
+ if (capitalizedSentenceEnabled && spellcheckEnabled && suggestionsEnabled)
+ name.NameValue = InputScopeNameValue.Text;
+ }
+ else
+ {
+ // Should never happens
+ name.NameValue = InputScopeNameValue.Default;
+ }
+ result.Names.Add(name);
+ return result;
+ }
+ }
+
+ public class EntryRenderer : ViewRenderer<Entry, FormsPhoneTextBox>
+ {
+ bool _fontApplied;
+ bool _ignoreTextChange;
+ Brush _placeholderDefaultBrush;
+
+ public override SizeRequest GetDesiredSize(double widthConstraint, double heightConstraint)
+ {
+ if (Children.Count == 0)
+ return new SizeRequest();
+
+ var constraint = new System.Windows.Size(widthConstraint, heightConstraint);
+
+ FormsPhoneTextBox child = Control;
+
+ 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);
+ }
+
+ protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
+ {
+ base.OnElementChanged(e);
+
+ var textBox = new FormsPhoneTextBox();
+
+ SetNativeControl(textBox);
+
+ UpdateInputScope();
+ UpdateIsPassword();
+ UpdateText();
+ UpdatePlaceholder();
+ UpdateColor();
+ UpdateFont();
+ UpdateAlignment();
+ UpdatePlaceholderColor();
+ UpdateIsEnabled();
+
+ Control.LostFocus += OnTextBoxUnfocused;
+ Control.TextChanged += TextBoxOnTextChanged;
+ Control.KeyUp += TextBoxOnKeyUp;
+ }
+
+ protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ base.OnElementPropertyChanged(sender, e);
+
+ if (e.PropertyName == Entry.TextProperty.PropertyName)
+ UpdateText();
+ else if (e.PropertyName == Entry.PlaceholderProperty.PropertyName)
+ UpdatePlaceholder();
+ else if (e.PropertyName == Entry.IsPasswordProperty.PropertyName)
+ UpdateIsPassword();
+ else if (e.PropertyName == VisualElement.IsEnabledProperty.PropertyName)
+ UpdateIsEnabled();
+ else if (e.PropertyName == Entry.TextColorProperty.PropertyName)
+ UpdateColor();
+ 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()
+ {
+ Control.Background = Element.BackgroundColor.IsDefault ? (Brush)System.Windows.Application.Current.Resources["PhoneTextBoxBrush"] : Element.BackgroundColor.ToBrush();
+ }
+
+ internal override void OnModelFocusChangeRequested(object sender, VisualElement.FocusRequestArgs args)
+ {
+ if (args.Focus)
+ args.Result = Control.Focus();
+ else
+ {
+ UnfocusControl(Control);
+ args.Result = true;
+ }
+ }
+
+ void OnTextBoxUnfocused(object sender, RoutedEventArgs e)
+ {
+ if (Element.TextColor.IsDefault)
+ return;
+
+ if (!IsNullOrEmpty(Element.Text))
+ Control.Foreground = Element.TextColor.ToBrush();
+ }
+
+ void TextBoxOnKeyUp(object sender, KeyEventArgs keyEventArgs)
+ {
+ if (keyEventArgs.Key == Key.Enter)
+ Element.SendCompleted();
+ }
+
+ void TextBoxOnTextChanged(object sender, System.Windows.Controls.TextChangedEventArgs textChangedEventArgs)
+ {
+ // Signal to the UpdateText method that the change to TextProperty doesn't need to update the control
+ // This prevents the cursor position from getting lost
+ _ignoreTextChange = true;
+ ((IElementController)Element).SetValueFromRenderer(Entry.TextProperty, Control.Text);
+
+ // If an Entry.TextChanged handler modified the value of the Entry's text, the values could now be
+ // out-of-sync; re-sync them and force the TextBox cursor to the end of the text
+ string entryText = Element.Text;
+ if (Control.Text != entryText)
+ {
+ Control.Text = entryText;
+ Control.SelectionStart = Control.Text.Length;
+ }
+
+ _ignoreTextChange = false;
+ }
+
+ void UpdateAlignment()
+ {
+ if (Control == null)
+ return;
+
+ Control.TextAlignment = Element.HorizontalTextAlignment.ToNativeTextAlignment();
+ }
+
+ void UpdateColor()
+ {
+ if (Control == null)
+ return;
+
+ Entry entry = Element;
+ if (entry != null)
+ {
+ if (!IsNullOrEmpty(entry.Text))
+ {
+ if (!entry.TextColor.IsDefault)
+ Control.Foreground = entry.TextColor.ToBrush();
+ else
+ Control.Foreground = (Brush)WControl.ForegroundProperty.GetMetadata(typeof(FormsPhoneTextBox)).DefaultValue;
+
+ // Force the PhoneTextBox control to do some internal bookkeeping
+ // so the colors change immediately and remain changed when the control gets focus
+ Control.OnApplyTemplate();
+ }
+ }
+ else
+ Control.Foreground = (Brush)WControl.ForegroundProperty.GetMetadata(typeof(FormsPhoneTextBox)).DefaultValue;
+ }
+
+ 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)
+ {
+ Control.ClearValue(WControl.FontStyleProperty);
+ Control.ClearValue(WControl.FontSizeProperty);
+ Control.ClearValue(WControl.FontFamilyProperty);
+ Control.ClearValue(WControl.FontWeightProperty);
+ Control.ClearValue(WControl.FontStretchProperty);
+ }
+ else
+ Control.ApplyFont(entry);
+
+ _fontApplied = true;
+ }
+
+ void UpdateInputScope()
+ {
+ Control.InputScope = Element.Keyboard.ToInputScope();
+ }
+
+ void UpdateIsEnabled()
+ {
+ Control.IsEnabled = Element.IsEnabled;
+ }
+
+ void UpdateIsPassword()
+ {
+ Control.IsPassword = Element.IsPassword;
+ }
+
+ void UpdatePlaceholder()
+ {
+ Control.Hint = 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()
+ {
+ // If the text property has changed because TextBoxOnTextChanged called SetValueFromRenderer,
+ // we don't want to re-update the text and reset the cursor position
+ if (_ignoreTextChange)
+ return;
+
+ if (Control.Text == Element.Text)
+ return;
+
+ Control.Text = Element.Text ?? "";
+ Control.Select(Control.Text.Length, 0);
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WP8/ExportCellAttribute.cs b/Xamarin.Forms.Platform.WP8/ExportCellAttribute.cs
new file mode 100644
index 00000000..96aec6cb
--- /dev/null
+++ b/Xamarin.Forms.Platform.WP8/ExportCellAttribute.cs
@@ -0,0 +1,12 @@
+using System;
+
+namespace Xamarin.Forms
+{
+ [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
+ public sealed class ExportCellAttribute : HandlerAttribute
+ {
+ public ExportCellAttribute(Type handler, Type target) : base(handler, target)
+ {
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WP8/ExportImageSourceHandlerAttribute.cs b/Xamarin.Forms.Platform.WP8/ExportImageSourceHandlerAttribute.cs
new file mode 100644
index 00000000..4c5c01fb
--- /dev/null
+++ b/Xamarin.Forms.Platform.WP8/ExportImageSourceHandlerAttribute.cs
@@ -0,0 +1,12 @@
+using System;
+
+namespace Xamarin.Forms
+{
+ [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.WP8/ExportRendererAttribute.cs b/Xamarin.Forms.Platform.WP8/ExportRendererAttribute.cs
new file mode 100644
index 00000000..bf18653b
--- /dev/null
+++ b/Xamarin.Forms.Platform.WP8/ExportRendererAttribute.cs
@@ -0,0 +1,12 @@
+using System;
+
+namespace Xamarin.Forms
+{
+ [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
+ public sealed class ExportRendererAttribute : HandlerAttribute
+ {
+ public ExportRendererAttribute(Type handler, Type target) : base(handler, target)
+ {
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WP8/Extensions.cs b/Xamarin.Forms.Platform.WP8/Extensions.cs
new file mode 100644
index 00000000..844c75ab
--- /dev/null
+++ b/Xamarin.Forms.Platform.WP8/Extensions.cs
@@ -0,0 +1,30 @@
+using Microsoft.Phone.Controls;
+
+namespace Xamarin.Forms.Platform.WinPhone
+{
+ internal static class Extensions
+ {
+ public static DeviceOrientation ToDeviceOrientation(this PageOrientation pageOrientation)
+ {
+ switch (pageOrientation)
+ {
+ case PageOrientation.None:
+ return DeviceOrientation.Other;
+ case PageOrientation.Portrait:
+ return DeviceOrientation.Portrait;
+ case PageOrientation.Landscape:
+ return DeviceOrientation.Landscape;
+ case PageOrientation.PortraitUp:
+ return DeviceOrientation.PortraitUp;
+ case PageOrientation.PortraitDown:
+ return DeviceOrientation.PortraitDown;
+ case PageOrientation.LandscapeRight:
+ return DeviceOrientation.LandscapeRight;
+ case PageOrientation.LandscapeLeft:
+ return DeviceOrientation.LandscapeLeft;
+ default:
+ return DeviceOrientation.Other;
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WP8/FontExtensions.cs b/Xamarin.Forms.Platform.WP8/FontExtensions.cs
new file mode 100644
index 00000000..186c6719
--- /dev/null
+++ b/Xamarin.Forms.Platform.WP8/FontExtensions.cs
@@ -0,0 +1,162 @@
+using System;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Documents;
+using System.Windows.Media;
+
+namespace Xamarin.Forms.Platform.WinPhone
+{
+ public static class FontExtensions
+ {
+ public static void ApplyFont(this Control self, Font font)
+ {
+ if (font.UseNamedSize)
+ {
+ switch (font.NamedSize)
+ {
+ case NamedSize.Micro:
+ self.FontSize = (double)System.Windows.Application.Current.Resources["PhoneFontSizeSmall"] - 3;
+ break;
+ case NamedSize.Small:
+ self.FontSize = (double)System.Windows.Application.Current.Resources["PhoneFontSizeSmall"];
+ break;
+ case NamedSize.Medium:
+ self.FontSize = (double)System.Windows.Application.Current.Resources["PhoneFontSizeMedium"];
+ // use medium instead of normal as this is the default for non-labels
+ break;
+ case NamedSize.Large:
+ self.FontSize = (double)System.Windows.Application.Current.Resources["PhoneFontSizeLarge"];
+ break;
+ default:
+ throw new ArgumentOutOfRangeException();
+ }
+ }
+ else
+ self.FontSize = font.FontSize;
+
+ if (!string.IsNullOrEmpty(font.FontFamily))
+ self.FontFamily = new FontFamily(font.FontFamily);
+ else
+ self.FontFamily = (FontFamily)System.Windows.Application.Current.Resources["PhoneFontFamilySemiBold"];
+
+ if (font.FontAttributes.HasFlag(FontAttributes.Italic))
+ self.FontStyle = FontStyles.Italic;
+ else
+ self.FontStyle = FontStyles.Normal;
+
+ if (font.FontAttributes.HasFlag(FontAttributes.Bold))
+ self.FontWeight = FontWeights.Bold;
+ else
+ self.FontWeight = FontWeights.Normal;
+ }
+
+ public static void ApplyFont(this TextBlock self, Font font)
+ {
+ if (font.UseNamedSize)
+ {
+ switch (font.NamedSize)
+ {
+ case NamedSize.Micro:
+ self.FontSize = (double)System.Windows.Application.Current.Resources["PhoneFontSizeSmall"] - 3;
+ break;
+ case NamedSize.Small:
+ self.FontSize = (double)System.Windows.Application.Current.Resources["PhoneFontSizeSmall"];
+ break;
+ case NamedSize.Medium:
+ self.FontSize = (double)System.Windows.Application.Current.Resources["PhoneFontSizeNormal"];
+ // use normal instead of medium as this is the default
+ break;
+ case NamedSize.Large:
+ self.FontSize = (double)System.Windows.Application.Current.Resources["PhoneFontSizeLarge"];
+ break;
+ default:
+ throw new ArgumentOutOfRangeException();
+ }
+ }
+ else
+ self.FontSize = font.FontSize;
+
+ if (!string.IsNullOrEmpty(font.FontFamily))
+ self.FontFamily = new FontFamily(font.FontFamily);
+ else
+ self.FontFamily = (FontFamily)System.Windows.Application.Current.Resources["PhoneFontFamilyNormal"];
+
+ if (font.FontAttributes.HasFlag(FontAttributes.Italic))
+ self.FontStyle = FontStyles.Italic;
+ else
+ self.FontStyle = FontStyles.Normal;
+
+ if (font.FontAttributes.HasFlag(FontAttributes.Bold))
+ self.FontWeight = FontWeights.Bold;
+ else
+ self.FontWeight = FontWeights.Normal;
+ }
+
+ public static void ApplyFont(this TextElement self, Font font)
+ {
+ if (font.UseNamedSize)
+ {
+ switch (font.NamedSize)
+ {
+ case NamedSize.Micro:
+ self.FontSize = (double)System.Windows.Application.Current.Resources["PhoneFontSizeSmall"] - 3;
+ break;
+ case NamedSize.Small:
+ self.FontSize = (double)System.Windows.Application.Current.Resources["PhoneFontSizeSmall"];
+ break;
+ case NamedSize.Medium:
+ self.FontSize = (double)System.Windows.Application.Current.Resources["PhoneFontSizeNormal"];
+ // use normal instead of medium as this is the default
+ break;
+ case NamedSize.Large:
+ self.FontSize = (double)System.Windows.Application.Current.Resources["PhoneFontSizeLarge"];
+ break;
+ default:
+ throw new ArgumentOutOfRangeException();
+ }
+ }
+ else
+ self.FontSize = font.FontSize;
+
+ if (!string.IsNullOrEmpty(font.FontFamily))
+ self.FontFamily = new FontFamily(font.FontFamily);
+ else
+ self.FontFamily = (FontFamily)System.Windows.Application.Current.Resources["PhoneFontFamilyNormal"];
+
+ if (font.FontAttributes.HasFlag(FontAttributes.Italic))
+ self.FontStyle = FontStyles.Italic;
+ else
+ self.FontStyle = FontStyles.Normal;
+
+ if (font.FontAttributes.HasFlag(FontAttributes.Bold))
+ self.FontWeight = FontWeights.Bold;
+ else
+ self.FontWeight = FontWeights.Normal;
+ }
+
+ internal static void ApplyFont(this Control self, IFontElement element)
+ {
+ self.FontSize = element.FontSize;
+
+ if (!string.IsNullOrEmpty(element.FontFamily))
+ self.FontFamily = new FontFamily(element.FontFamily);
+ else
+ self.FontFamily = (FontFamily)System.Windows.Application.Current.Resources["PhoneFontFamilySemiBold"];
+
+ if (element.FontAttributes.HasFlag(FontAttributes.Italic))
+ self.FontStyle = FontStyles.Italic;
+ else
+ self.FontStyle = FontStyles.Normal;
+
+ if (element.FontAttributes.HasFlag(FontAttributes.Bold))
+ self.FontWeight = FontWeights.Bold;
+ else
+ self.FontWeight = FontWeights.Normal;
+ }
+
+ 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.WP8/Forms.cs b/Xamarin.Forms.Platform.WP8/Forms.cs
new file mode 100644
index 00000000..b4435d3a
--- /dev/null
+++ b/Xamarin.Forms.Platform.WP8/Forms.cs
@@ -0,0 +1,254 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Linq.Expressions;
+using System.Reflection;
+using System.Windows;
+using System.Windows.Interop;
+using System.Windows.Media;
+using Microsoft.Phone.Controls;
+using Xamarin.Forms.Platform.WinPhone;
+
+using Expression = System.Linq.Expressions.Expression;
+
+
+namespace Xamarin.Forms
+{
+ public static class Forms
+ {
+ static bool s_isInitialized;
+
+ public static UIElement ConvertPageToUIElement(this Page page, PhoneApplicationPage applicationPage)
+ {
+ var application = new DefaultApplication();
+ application.MainPage = page;
+ var result = new Platform.WinPhone.Platform(applicationPage);
+ result.SetPage(page);
+ return result.GetCanvas();
+ }
+
+ public static void Init()
+ {
+ if (s_isInitialized)
+ return;
+
+ // Needed to prevent stripping of System.Windows.Interactivity
+ // which is current only referenced in the XAML DataTemplates
+ var eventTrigger = new System.Windows.Interactivity.EventTrigger();
+
+ string assemblyName = Assembly.GetExecutingAssembly().GetName().Name;
+
+ System.Windows.Application.Current.Resources.MergedDictionaries.Add(new System.Windows.ResourceDictionary
+ {
+ Source = new Uri(string.Format("/{0};component/WPResources.xaml", assemblyName), UriKind.Relative)
+ });
+
+ var accent = System.Windows.Application.Current.Resources["PhoneAccentBrush"] as SolidColorBrush;
+ System.Windows.Media.Color color = accent.Color;
+ Color.Accent = Color.FromRgba(color.R, color.G, color.B, color.A);
+
+ Log.Listeners.Add(new DelegateLogListener((c, m) => Console.WriteLine("[{0}] {1}", m, c)));
+
+ Device.OS = TargetPlatform.WinPhone;
+ Device.PlatformServices = new WP8PlatformServices();
+ Device.Info = new WP8DeviceInfo();
+
+ Registrar.RegisterAll(new[] { typeof(ExportRendererAttribute), typeof(ExportCellAttribute), typeof(ExportImageSourceHandlerAttribute) });
+
+ Ticker.Default = new WinPhoneTicker();
+
+ Device.Idiom = TargetIdiom.Phone;
+
+ ExpressionSearch.Default = new WinPhoneExpressionSearch();
+
+ s_isInitialized = true;
+ }
+
+ class DefaultApplication : Application
+ {
+ }
+
+ internal class WP8DeviceInfo : DeviceInfo
+ {
+ internal const string BWPorientationChangedName = "Xamarin.WP8.OrientationChanged";
+ readonly double _scalingFactor;
+
+ public WP8DeviceInfo()
+ {
+ MessagingCenter.Subscribe(this, BWPorientationChangedName, (FormsApplicationPage page, DeviceOrientation orientation) => { CurrentOrientation = orientation; });
+
+ Content content = System.Windows.Application.Current.Host.Content;
+
+ // Scaling Factor for Windows Phone 8 is relative to WVGA: https://msdn.microsoft.com/en-us/library/windows/apps/jj206974(v=vs.105).aspx
+ _scalingFactor = content.ScaleFactor / 100d;
+
+ PixelScreenSize = new Size(content.ActualWidth * _scalingFactor, content.ActualHeight * _scalingFactor);
+ ScaledScreenSize = new Size(content.ActualWidth, content.ActualHeight);
+ }
+
+ public override Size PixelScreenSize { get; }
+
+ public override Size ScaledScreenSize { get; }
+
+ public override double ScalingFactor
+ {
+ get { return _scalingFactor; }
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ MessagingCenter.Unsubscribe<FormsApplicationPage, DeviceOrientation>(this, BWPorientationChangedName);
+ base.Dispose(disposing);
+ }
+ }
+
+ sealed class WinPhoneExpressionSearch : 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);
+
+ return _results.Select(o => o as T).ToList();
+ }
+
+ 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);
+ }
+
+ 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.WP8/FormsApplicationPage.cs b/Xamarin.Forms.Platform.WP8/FormsApplicationPage.cs
new file mode 100644
index 00000000..ac6decb6
--- /dev/null
+++ b/Xamarin.Forms.Platform.WP8/FormsApplicationPage.cs
@@ -0,0 +1,86 @@
+using System.ComponentModel;
+using Microsoft.Phone.Controls;
+using Microsoft.Phone.Shell;
+
+namespace Xamarin.Forms.Platform.WinPhone
+{
+ public class FormsApplicationPage : PhoneApplicationPage
+ {
+ Application _application;
+ Platform _platform;
+
+ protected FormsApplicationPage()
+ {
+ PhoneApplicationService.Current.Launching += OnLaunching;
+ PhoneApplicationService.Current.Activated += OnActivated;
+ PhoneApplicationService.Current.Deactivated += OnDeactivated;
+ PhoneApplicationService.Current.Closing += OnClosing;
+
+ MessagingCenter.Send(this, Forms.WP8DeviceInfo.BWPorientationChangedName, Orientation.ToDeviceOrientation());
+ OrientationChanged += OnOrientationChanged;
+ //DeserializePropertyStore ();
+ }
+
+ protected void LoadApplication(Application application)
+ {
+ Application.Current = application;
+ application.PropertyChanged += ApplicationOnPropertyChanged;
+ _application = application;
+
+ // Hack around the fact that OnLaunching will haev already happened by this point, sad but needed.
+ application.SendStart();
+
+ SetMainPage();
+ }
+
+ void ApplicationOnPropertyChanged(object sender, PropertyChangedEventArgs args)
+ {
+ if (args.PropertyName == "MainPage")
+ SetMainPage();
+ }
+
+ void OnActivated(object sender, ActivatedEventArgs e)
+ {
+ // TODO : figure out consistency of get this to fire
+ // Check whether tombstoned (terminated, but OS retains information about navigation state and state dictionarys) or dormant
+ _application.SendResume();
+ }
+
+ // when app gets tombstoned, user press back past first page
+ void OnClosing(object sender, ClosingEventArgs e)
+ {
+ // populate isolated storage.
+ //SerializePropertyStore ();
+ _application.SendSleepAsync().Wait();
+ }
+
+ void OnDeactivated(object sender, DeactivatedEventArgs e)
+ {
+ // populate state dictionaries, properties
+ //SerializePropertyStore ();
+ _application.SendSleepAsync().Wait();
+ }
+
+ void OnLaunching(object sender, LaunchingEventArgs e)
+ {
+ // TODO : not currently firing, is fired before MainPage ctor is called
+ _application.SendStart();
+ }
+
+ void OnOrientationChanged(object sender, OrientationChangedEventArgs e)
+ {
+ MessagingCenter.Send(this, Forms.WP8DeviceInfo.BWPorientationChangedName, e.Orientation.ToDeviceOrientation());
+ }
+
+ void SetMainPage()
+ {
+ if (_platform == null)
+ _platform = new Platform(this);
+
+ _platform.SetPage(_application.MainPage);
+
+ if (!ReferenceEquals(Content, _platform))
+ Content = _platform.GetCanvas();
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WP8/FormsListPicker.cs b/Xamarin.Forms.Platform.WP8/FormsListPicker.cs
new file mode 100644
index 00000000..573f639b
--- /dev/null
+++ b/Xamarin.Forms.Platform.WP8/FormsListPicker.cs
@@ -0,0 +1,25 @@
+using System;
+using System.Windows;
+using Microsoft.Phone.Controls;
+
+namespace Xamarin.Forms.Platform.WinPhone
+{
+ public class FormsListPicker : ListPicker
+ {
+ internal static readonly DependencyProperty ListPickerModeChangedProperty = DependencyProperty.Register("ListPickerMode", typeof(ListPickerMode), typeof(FormsListPicker),
+ new PropertyMetadata(ModeChanged));
+
+ protected virtual void OnListPickerModeChanged(DependencyPropertyChangedEventArgs args)
+ {
+ ListPickerModeChanged?.Invoke(this, args);
+ }
+
+ internal event EventHandler<DependencyPropertyChangedEventArgs> ListPickerModeChanged;
+
+ static void ModeChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
+ {
+ var listPicker = dependencyObject as FormsListPicker;
+ listPicker?.OnListPickerModeChanged(dependencyPropertyChangedEventArgs);
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WP8/FormsPhoneTextBox.cs b/Xamarin.Forms.Platform.WP8/FormsPhoneTextBox.cs
new file mode 100644
index 00000000..342646cd
--- /dev/null
+++ b/Xamarin.Forms.Platform.WP8/FormsPhoneTextBox.cs
@@ -0,0 +1,236 @@
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Input;
+using System.Windows.Media;
+using Microsoft.Phone.Controls;
+
+namespace Xamarin.Forms.Platform.WinPhone
+{
+ /// <summary>
+ /// An intermediate class for injecting bindings for things the default
+ /// textbox doesn't allow us to bind/modify
+ /// </summary>
+ public class FormsPhoneTextBox : PhoneTextBox
+ {
+ const char ObfuscationCharacter = '●';
+
+ public static readonly DependencyProperty PlaceholderForegroundBrushProperty = DependencyProperty.Register("PlaceholderForegroundBrush", typeof(Brush), typeof(FormsPhoneTextBox),
+ new PropertyMetadata(default(Brush)));
+
+ public static readonly DependencyProperty IsPasswordProperty = DependencyProperty.Register("IsPassword", typeof(bool), typeof(FormsPhoneTextBox),
+ new PropertyMetadata(default(bool), OnIsPasswordChanged));
+
+ public new static readonly DependencyProperty TextProperty = DependencyProperty.Register("Text", typeof(string), typeof(FormsPhoneTextBox), new PropertyMetadata("", TextPropertyChanged));
+
+ protected internal static readonly DependencyProperty DisabledTextProperty = DependencyProperty.Register("DisabledText", typeof(string), typeof(FormsPhoneTextBox), new PropertyMetadata(""));
+
+ static InputScope s_passwordInputScope;
+ InputScope _cachedInputScope;
+ CancellationTokenSource _cts;
+ bool _internalChangeFlag;
+
+ public FormsPhoneTextBox()
+ {
+ TextChanged += OnTextChanged;
+ SelectionChanged += OnSelectionChanged;
+ }
+
+ 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 new string Text
+ {
+ get { return (string)GetValue(TextProperty); }
+ set { SetValue(TextProperty, value); }
+ }
+
+ protected internal string DisabledText
+ {
+ get { return (string)GetValue(DisabledTextProperty); }
+ set { SetValue(DisabledTextProperty, 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;
+ }
+ }
+
+ 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();
+ Dispatcher.BeginInvoke(() =>
+ {
+ 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)
+ {
+ int firstObfuscationChar = passwordText.IndexOf(ObfuscationCharacter);
+
+ if (firstObfuscationChar > 0)
+ {
+ // The user is typing faster than we can process, and the text is coming in at the beginning
+ // of the textbox instead of the end
+ passwordText = passwordText.Substring(firstObfuscationChar, passwordText.Length - firstObfuscationChar) + passwordText.Substring(0, firstObfuscationChar);
+ }
+
+ 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 dependencyPropertyChangedEventArgs)
+ {
+ var textBox = (FormsPhoneTextBox)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, System.Windows.Controls.TextChangedEventArgs textChangedEventArgs)
+ {
+ if (IsPassword)
+ 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;
+ DisabledText = base.Text;
+
+ SelectionStart = base.Text.Length;
+ }
+
+ static void TextPropertyChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
+ {
+ var textBox = (FormsPhoneTextBox)dependencyObject;
+ textBox.SyncBaseText();
+ }
+
+ void UpdateInputScope()
+ {
+ if (IsPassword)
+ {
+ _cachedInputScope = InputScope;
+ InputScope = PasswordInputScope; // We don't want suggestions turned on if we're in password mode
+ }
+ else
+ InputScope = _cachedInputScope;
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WP8/FrameRenderer.cs b/Xamarin.Forms.Platform.WP8/FrameRenderer.cs
new file mode 100644
index 00000000..00472161
--- /dev/null
+++ b/Xamarin.Forms.Platform.WP8/FrameRenderer.cs
@@ -0,0 +1,57 @@
+using System.ComponentModel;
+using System.Windows;
+using System.Windows.Controls;
+
+namespace Xamarin.Forms.Platform.WinPhone
+{
+ public class FrameRenderer : ViewRenderer<Frame, Border>
+ {
+ public FrameRenderer()
+ {
+ AutoPackage = false;
+ }
+
+ protected override void OnElementChanged(ElementChangedEventArgs<Frame> e)
+ {
+ base.OnElementChanged(e);
+
+ 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;
+
+ Platform.SetRenderer(Element.Content, Platform.CreateRenderer(Element.Content));
+
+ UIElement element = Platform.GetRenderer(Element.Content).ContainerElement;
+ Control.Child = element;
+ }
+
+ void UpdateBorder()
+ {
+ Control.CornerRadius = new CornerRadius(5);
+ if (Element.OutlineColor != Color.Default)
+ {
+ Control.BorderBrush = Element.OutlineColor.ToBrush();
+ Control.BorderThickness = new System.Windows.Thickness(1);
+ }
+ else
+ Control.BorderBrush = new Color(0, 0, 0, 0).ToBrush();
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WP8/FrameworkElementExtensions.cs b/Xamarin.Forms.Platform.WP8/FrameworkElementExtensions.cs
new file mode 100644
index 00000000..f8c77cef
--- /dev/null
+++ b/Xamarin.Forms.Platform.WP8/FrameworkElementExtensions.cs
@@ -0,0 +1,73 @@
+using System;
+using System.Linq;
+using System.Reflection;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Media;
+
+namespace Xamarin.Forms.Platform.WinPhone
+{
+ 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 System.Windows.Data.Binding GetForegroundBinding(this FrameworkElement element)
+ {
+ System.Windows.Data.BindingExpression expr = element.GetBindingExpression(GetForegroundProperty(element));
+ if (expr == null)
+ return null;
+
+ return expr.ParentBinding;
+ }
+
+ 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, System.Windows.Data.Binding binding)
+ {
+ if (element == null)
+ throw new ArgumentNullException("element");
+
+ element.SetBinding(GetForegroundProperty(element), binding);
+ }
+
+ 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(BindingFlags.Public | BindingFlags.Static).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.WP8/ICellRenderer.cs b/Xamarin.Forms.Platform.WP8/ICellRenderer.cs
new file mode 100644
index 00000000..350b72d6
--- /dev/null
+++ b/Xamarin.Forms.Platform.WP8/ICellRenderer.cs
@@ -0,0 +1,7 @@
+namespace Xamarin.Forms.Platform.WinPhone
+{
+ public interface ICellRenderer : IRegisterable
+ {
+ System.Windows.DataTemplate GetTemplate(Cell cell);
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WP8/IVisualElementRenderer.cs b/Xamarin.Forms.Platform.WP8/IVisualElementRenderer.cs
new file mode 100644
index 00000000..c4e097cb
--- /dev/null
+++ b/Xamarin.Forms.Platform.WP8/IVisualElementRenderer.cs
@@ -0,0 +1,18 @@
+using System;
+using System.Windows;
+
+namespace Xamarin.Forms.Platform.WinPhone
+{
+ public interface IVisualElementRenderer : IRegisterable
+ {
+ UIElement 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.WP8/ImageRenderer.cs b/Xamarin.Forms.Platform.WP8/ImageRenderer.cs
new file mode 100644
index 00000000..8be9c15d
--- /dev/null
+++ b/Xamarin.Forms.Platform.WP8/ImageRenderer.cs
@@ -0,0 +1,166 @@
+using System;
+using System.ComponentModel;
+using System.IO;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+
+namespace Xamarin.Forms.Platform.WinPhone
+{
+ internal static class ImageExtensions
+ {
+ public static Stretch ToStretch(this Aspect aspect)
+ {
+ switch (aspect)
+ {
+ case Aspect.Fill:
+ return Stretch.Fill;
+ case Aspect.AspectFill:
+ return Stretch.UniformToFill;
+ default:
+ case Aspect.AspectFit:
+ return Stretch.Uniform;
+ }
+ }
+ }
+
+ public class ImageRenderer : ViewRenderer<Image, System.Windows.Controls.Image>
+ {
+ public override SizeRequest GetDesiredSize(double widthConstraint, double heightConstraint)
+ {
+ // Someone reported a NRE happening in this method which can only be explained by Control being null
+ // which only happens at the very beginning of the view lifecycle. Honest I have no idea how this might
+ // happen because it really shouldn't measure at that point. Add check anyway and live in fear...
+ if (Control?.Source == null)
+ return new SizeRequest();
+
+ var result = new Size { Width = ((BitmapImage)Control.Source).PixelWidth, Height = ((BitmapImage)Control.Source).PixelHeight };
+
+ return new SizeRequest(result);
+ }
+
+ protected override void OnElementChanged(ElementChangedEventArgs<Image> e)
+ {
+ base.OnElementChanged(e);
+
+ if (e.NewElement != null)
+ {
+ if (Control == null)
+ {
+ var image = new System.Windows.Controls.Image();
+ SetNativeControl(image);
+ }
+
+ SetSource(Control);
+ SetAspect(Control);
+ }
+ }
+
+ protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ base.OnElementPropertyChanged(sender, e);
+
+ if (e.PropertyName == Image.SourceProperty.PropertyName)
+ SetSource(Control);
+ else if (e.PropertyName == Image.AspectProperty.PropertyName)
+ SetAspect(Control);
+ }
+
+ void SetAspect(System.Windows.Controls.Image image)
+ {
+ Aspect aspect = Element.Aspect;
+
+ image.Stretch = aspect.ToStretch();
+ }
+
+ async void SetSource(System.Windows.Controls.Image image)
+ {
+ ((IElementController)Element).SetValueFromRenderer(Image.IsLoadingPropertyKey, true);
+
+ ImageSource source = Element.Source;
+ IImageSourceHandler handler;
+ if (source != null && (handler = Registrar.Registered.GetHandler<IImageSourceHandler>(source.GetType())) != null)
+ {
+ System.Windows.Media.ImageSource imagesource;
+ try
+ {
+ imagesource = await handler.LoadImageAsync(source);
+ }
+ catch (TaskCanceledException)
+ {
+ imagesource = null;
+ }
+ image.Source = imagesource;
+ // if you dont at least measure the thing once it wont load the image
+ // then the whole thing falls over.
+ image.Measure(new System.Windows.Size(100, 100));
+ ((IVisualElementController)Element).NativeSizeChanged();
+ }
+ else
+ image.Source = null;
+
+ ((IElementController)Element).SetValueFromRenderer(Image.IsLoadingPropertyKey, false);
+ }
+ }
+
+ public interface IImageSourceHandler : IRegisterable
+ {
+ Task<System.Windows.Media.ImageSource> LoadImageAsync(ImageSource imagesoure, CancellationToken cancelationToken = default(CancellationToken));
+ }
+
+ public sealed class FileImageSourceHandler : IImageSourceHandler
+ {
+ public Task<System.Windows.Media.ImageSource> LoadImageAsync(ImageSource imagesoure, CancellationToken cancelationToken = new CancellationToken())
+ {
+ System.Windows.Media.ImageSource image = null;
+ var filesource = imagesoure as FileImageSource;
+ if (filesource != null)
+ {
+ string file = filesource.File;
+ image = new BitmapImage(new Uri("/" + file, UriKind.Relative));
+ }
+ return Task.FromResult(image);
+ }
+ }
+
+ public sealed class StreamImagesourceHandler : IImageSourceHandler
+ {
+ public async Task<System.Windows.Media.ImageSource> LoadImageAsync(ImageSource imagesource, CancellationToken cancelationToken = new CancellationToken())
+ {
+ BitmapImage bitmapimage = null;
+
+ var streamsource = imagesource as StreamImageSource;
+ if (streamsource != null && streamsource.Stream != null)
+ {
+ using(Stream stream = await streamsource.GetStreamAsync(cancelationToken))
+ {
+ bitmapimage = new BitmapImage();
+ bitmapimage.SetSource(stream);
+ }
+ }
+ return (System.Windows.Media.ImageSource)bitmapimage;
+ }
+ }
+
+ public sealed class ImageLoaderSourceHandler : IImageSourceHandler
+ {
+ public async Task<System.Windows.Media.ImageSource> LoadImageAsync(ImageSource imagesoure, CancellationToken cancelationToken = new CancellationToken())
+ {
+ BitmapImage bitmapimage = null;
+ var imageLoader = imagesoure as UriImageSource;
+ if (imageLoader != null && imageLoader.Uri != null)
+ {
+ using(Stream streamimage = await imageLoader.GetStreamAsync(cancelationToken))
+ {
+ if (streamimage != null && streamimage.CanRead)
+ {
+ bitmapimage = new BitmapImage();
+ bitmapimage.SetSource(streamimage);
+ }
+ }
+ }
+ return bitmapimage;
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WP8/LabelRenderer.cs b/Xamarin.Forms.Platform.WP8/LabelRenderer.cs
new file mode 100644
index 00000000..86c68adf
--- /dev/null
+++ b/Xamarin.Forms.Platform.WP8/LabelRenderer.cs
@@ -0,0 +1,187 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Documents;
+using System.Windows.Media;
+
+namespace Xamarin.Forms.Platform.WinPhone
+{
+ public static class FormattedStringExtensions
+ {
+ public static IEnumerable<Inline> ToInlines(this FormattedString formattedString)
+ {
+ foreach (Span span in formattedString.Spans)
+ yield return span.ToRun();
+ }
+
+ 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 System.Windows.Size ArrangeOverride(System.Windows.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);
+
+ var textBlock = new TextBlock { Foreground = (Brush)System.Windows.Application.Current.Resources["PhoneForegroundBrush"] };
+ UpdateText(textBlock);
+ UpdateColor(textBlock);
+ UpdateAlign(textBlock);
+ UpdateFont(textBlock);
+ UpdateLineBreakMode(textBlock);
+ SetNativeControl(textBlock);
+ }
+
+ protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ if (e.PropertyName == Label.TextProperty.PropertyName || e.PropertyName == Label.FormattedTextProperty.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.Foreground = (Brush)System.Windows.Application.Current.Resources["PhoneForegroundBrush"];
+ }
+
+ 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.None;
+ 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)
+ {
+ if (label.FormattedText == null)
+ textBlock.Text = label.Text;
+ else
+ {
+ FormattedString formattedText = label.FormattedText ?? label.Text;
+
+ textBlock.Inlines.Clear();
+ foreach (Inline inline in formattedText.ToInlines())
+ textBlock.Inlines.Add(inline);
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WP8/LayoutExtensions.cs b/Xamarin.Forms.Platform.WP8/LayoutExtensions.cs
new file mode 100644
index 00000000..1fb1f247
--- /dev/null
+++ b/Xamarin.Forms.Platform.WP8/LayoutExtensions.cs
@@ -0,0 +1,27 @@
+using System.Collections.Generic;
+using System.Windows;
+using WSize = System.Windows.Size;
+
+namespace Xamarin.Forms.Platform.WinPhone
+{
+ 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.WP8/ListViewRenderer.cs b/Xamarin.Forms.Platform.WP8/ListViewRenderer.cs
new file mode 100644
index 00000000..362c993c
--- /dev/null
+++ b/Xamarin.Forms.Platform.WP8/ListViewRenderer.cs
@@ -0,0 +1,717 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Linq;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Controls.Primitives;
+using System.Windows.Input;
+using System.Windows.Media;
+using Microsoft.Phone.Controls;
+using GestureEventArgs = System.Windows.Input.GestureEventArgs;
+using SLButton = System.Windows.Controls.Button;
+using SLBinding = System.Windows.Data.Binding;
+
+namespace Xamarin.Forms.Platform.WinPhone
+{
+ // Fixes a weird crash, don't ask.
+ internal class FixedLongListSelector : LongListSelector
+ {
+ bool _isInPullToRefresh;
+
+ System.Windows.Point _lastPosition;
+ double _pullToRefreshStatus;
+
+ public FixedLongListSelector()
+ {
+ Loaded += OnLoaded;
+ }
+
+ public bool IsInPullToRefresh
+ {
+ get { return _isInPullToRefresh; }
+ private set
+ {
+ if (_isInPullToRefresh == value)
+ return;
+ _isInPullToRefresh = value;
+
+ if (_isInPullToRefresh)
+ {
+ EventHandler handler = PullToRefreshStarted;
+ if (handler != null)
+ handler(this, EventArgs.Empty);
+ }
+ else
+ {
+ EventHandler handler = PullToRefreshStatus >= 1 ? PullToRefreshCompleted : PullToRefreshCanceled;
+ if (handler != null)
+ handler(this, EventArgs.Empty);
+ _pullToRefreshStatus = 0;
+ }
+ }
+ }
+
+ public double PullToRefreshStatus
+ {
+ get { return _pullToRefreshStatus; }
+ set
+ {
+ if (_pullToRefreshStatus == value)
+ return;
+ _pullToRefreshStatus = value;
+ EventHandler handler = PullToRefreshStatusUpdated;
+ if (handler != null)
+ handler(this, EventArgs.Empty);
+ }
+ }
+
+ public ViewportControl ViewportControl { get; private set; }
+
+ bool ViewportAtTop
+ {
+ get { return ViewportControl.Viewport.Top == 0; }
+ }
+
+ public override void OnApplyTemplate()
+ {
+ base.OnApplyTemplate();
+
+ if (ViewportControl != null)
+ {
+ ViewportControl.ViewportChanged -= OnViewportChanged;
+ ViewportControl.ManipulationStateChanged -= OnManipulationStateChanged;
+ }
+
+ ViewportControl = (ViewportControl)VisualTreeHelper.GetChild(VisualTreeHelper.GetChild(VisualTreeHelper.GetChild(this, 0), 0), 0);
+ ViewportControl.ViewportChanged += OnViewportChanged;
+ ViewportControl.ManipulationStateChanged += OnManipulationStateChanged;
+ }
+
+ public event EventHandler PullToRefreshCanceled;
+
+ public event EventHandler PullToRefreshCompleted;
+
+ public event EventHandler PullToRefreshStarted;
+
+ public event EventHandler PullToRefreshStatusUpdated;
+
+ protected override System.Windows.Size MeasureOverride(System.Windows.Size availableSize)
+ {
+ try
+ {
+ return base.MeasureOverride(availableSize);
+ }
+ catch (ArgumentException)
+ {
+ return base.MeasureOverride(availableSize);
+ }
+ }
+
+ void OnFrameReported(object sender, TouchFrameEventArgs e)
+ {
+ TouchPoint touchPoint;
+ try
+ {
+ touchPoint = e.GetPrimaryTouchPoint(this);
+ }
+ catch (Exception)
+ {
+ return;
+ }
+
+ if (touchPoint == null || touchPoint.Action != TouchAction.Move)
+ return;
+
+ System.Windows.Point position = touchPoint.Position;
+
+ if (IsInPullToRefresh)
+ {
+ double delta = position.Y - _lastPosition.Y;
+ PullToRefreshStatus += delta / 150.0;
+ }
+
+ _lastPosition = position;
+ }
+
+ void OnLoaded(object sender, RoutedEventArgs routedEventArgs)
+ {
+ Loaded -= OnLoaded;
+ Unloaded += OnUnloaded;
+
+ Touch.FrameReported += OnFrameReported;
+ }
+
+ void OnManipulationStateChanged(object o, ManipulationStateChangedEventArgs args)
+ {
+ switch (ViewportControl.ManipulationState)
+ {
+ case ManipulationState.Idle:
+ // thing is rested
+ IsInPullToRefresh = false;
+ break;
+ case ManipulationState.Manipulating:
+ // user interaction
+ IsInPullToRefresh = ViewportAtTop;
+ break;
+ case ManipulationState.Animating:
+ // user let go
+ IsInPullToRefresh = false;
+ break;
+ default:
+ throw new ArgumentOutOfRangeException();
+ }
+ }
+
+ void OnUnloaded(object sender, RoutedEventArgs routedEventArgs)
+ {
+ Loaded += OnLoaded;
+ Unloaded -= OnUnloaded;
+
+ Touch.FrameReported -= OnFrameReported;
+ }
+
+ void OnViewportChanged(object o, ViewportChangedEventArgs args)
+ {
+ if (ViewportControl.ManipulationState == ManipulationState.Manipulating)
+ IsInPullToRefresh = ViewportAtTop;
+ }
+ }
+
+ public class ListViewRenderer : ViewRenderer<ListView, LongListSelector>
+ {
+ public static readonly DependencyProperty HighlightWhenSelectedProperty = DependencyProperty.RegisterAttached("HighlightWhenSelected", typeof(bool), typeof(ListViewRenderer),
+ new PropertyMetadata(false));
+
+ readonly List<Tuple<FrameworkElement, SLBinding, Brush>> _previousHighlights = new List<Tuple<FrameworkElement, SLBinding, Brush>>();
+
+ Animatable _animatable;
+ object _fromNative;
+ bool _itemNeedsSelecting;
+ FixedLongListSelector _listBox;
+ System.Windows.Controls.ProgressBar _progressBar;
+
+ ViewportControl _viewport;
+
+ public override SizeRequest GetDesiredSize(double widthConstraint, double heightConstraint)
+ {
+ SizeRequest result = base.GetDesiredSize(widthConstraint, heightConstraint);
+ result.Minimum = new Size(40, 40);
+ return result;
+ }
+
+ public static bool GetHighlightWhenSelected(DependencyObject dependencyObject)
+ {
+ return (bool)dependencyObject.GetValue(HighlightWhenSelectedProperty);
+ }
+
+ public static void SetHighlightWhenSelected(DependencyObject dependencyObject, bool value)
+ {
+ dependencyObject.SetValue(HighlightWhenSelectedProperty, value);
+ }
+
+ protected override System.Windows.Size ArrangeOverride(System.Windows.Size finalSize)
+ {
+ System.Windows.Size result = base.ArrangeOverride(finalSize);
+
+ _progressBar.Measure(finalSize);
+ _progressBar.Arrange(new Rect(0, 0, finalSize.Width, _progressBar.DesiredSize.Height));
+
+ return result;
+ }
+
+ protected override void OnElementChanged(ElementChangedEventArgs<ListView> e)
+ {
+ base.OnElementChanged(e);
+
+ Element.ScrollToRequested += OnScrollToRequested;
+
+ if (Element.SelectedItem != null)
+ _itemNeedsSelecting = true;
+
+ _listBox = new FixedLongListSelector
+ {
+ DataContext = Element,
+ ItemsSource = Element.TemplatedItems,
+ ItemTemplate = (System.Windows.DataTemplate)System.Windows.Application.Current.Resources["CellTemplate"],
+ GroupHeaderTemplate = (System.Windows.DataTemplate)System.Windows.Application.Current.Resources["ListViewHeader"],
+ ListHeaderTemplate = (System.Windows.DataTemplate)System.Windows.Application.Current.Resources["View"],
+ ListFooterTemplate = (System.Windows.DataTemplate)System.Windows.Application.Current.Resources["View"]
+ };
+ _listBox.SetBinding(LongListSelector.IsGroupingEnabledProperty, new SLBinding("IsGroupingEnabled"));
+
+ _listBox.SelectionChanged += OnNativeSelectionChanged;
+ _listBox.Tap += OnNativeItemTapped;
+ _listBox.ItemRealized += OnItemRealized;
+
+ _listBox.PullToRefreshStarted += OnPullToRefreshStarted;
+ _listBox.PullToRefreshCompleted += OnPullToRefreshCompleted;
+ _listBox.PullToRefreshCanceled += OnPullToRefreshCanceled;
+ _listBox.PullToRefreshStatusUpdated += OnPullToRefreshStatusUpdated;
+
+ SetNativeControl(_listBox);
+
+ _progressBar = new System.Windows.Controls.ProgressBar { Maximum = 1, Visibility = Visibility.Collapsed };
+ Children.Add(_progressBar);
+
+ UpdateHeader();
+ UpdateFooter();
+ UpdateJumpList();
+ }
+
+ protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ base.OnElementPropertyChanged(sender, e);
+
+ if (e.PropertyName == ListView.SelectedItemProperty.PropertyName)
+ OnItemSelected(Element.SelectedItem);
+ else if (e.PropertyName == "HeaderElement")
+ UpdateHeader();
+ else if (e.PropertyName == "FooterElement")
+ UpdateFooter();
+ else if ((e.PropertyName == ListView.IsRefreshingProperty.PropertyName) || (e.PropertyName == ListView.IsPullToRefreshEnabledProperty.PropertyName) || (e.PropertyName == "CanRefresh"))
+ UpdateIsRefreshing();
+ else if (e.PropertyName == "GroupShortNameBinding")
+ UpdateJumpList();
+ }
+
+ protected override void UpdateNativeWidget()
+ {
+ base.UpdateNativeWidget();
+
+ if (_progressBar != null)
+ _progressBar.Width = Element.Width;
+ }
+
+ Cell FindCell(GestureEventArgs e, out FrameworkElement element)
+ {
+ Cell cell = null;
+ element = e.OriginalSource as FrameworkElement;
+ if (element != null)
+ cell = element.DataContext as Cell;
+
+ if (cell == null)
+ {
+ System.Windows.Point pos = e.GetPosition(_listBox);
+ IEnumerable<UIElement> elements = VisualTreeHelper.FindElementsInHostCoordinates(pos, _listBox);
+ foreach (FrameworkElement frameworkElement in elements.OfType<FrameworkElement>())
+ {
+ if ((cell = frameworkElement.DataContext as Cell) != null)
+ {
+ element = frameworkElement;
+ break;
+ }
+ }
+ }
+
+ return cell;
+ }
+
+ 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;
+ }
+ }
+
+ FrameworkElement FindElement(Cell cell)
+ {
+ foreach (CellControl selector in FindDescendants<CellControl>(_listBox))
+ {
+ if (ReferenceEquals(cell, selector.DataContext))
+ return selector;
+ }
+
+ return null;
+ }
+
+ IEnumerable<FrameworkElement> FindHighlight(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 FindHighlightCore(parent);
+ }
+
+ IEnumerable<FrameworkElement> FindHighlightCore(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 FindHighlightCore(childElement))
+ yield return recursedElement;
+ }
+ }
+
+ double GetHeight(Dictionary<System.Windows.DataTemplate, FrameworkElement> reusables, System.Windows.DataTemplate template, object bindingContext)
+ {
+ double width = Control.ActualWidth;
+
+ FrameworkElement content;
+ if (!reusables.TryGetValue(template, out content))
+ {
+ content = (FrameworkElement)template.LoadContent();
+
+ // Windows Phone refuses to properly bind things on a first pass or even a second pass unless it has different content
+ // so we'll force it to cycle here the first time for each template.
+ content.DataContext = bindingContext;
+ content.Measure(new System.Windows.Size(width, double.PositiveInfinity));
+ content.DataContext = null;
+ content.Measure(new System.Windows.Size(width, double.PositiveInfinity));
+
+ var control = content as Control;
+ if (control != null)
+ {
+ // Since we're not adding to the visual tree, we need to inherit the font to measure correctly.
+ control.FontFamily = Control.FontFamily;
+ }
+
+ reusables[template] = content;
+ }
+
+ content.DataContext = bindingContext;
+ content.Measure(new System.Windows.Size(width, double.PositiveInfinity));
+ return content.DesiredSize.Height;
+ }
+
+ void OnItemRealized(object sender, ItemRealizationEventArgs e)
+ {
+ if (!_itemNeedsSelecting)
+ return;
+
+ var cell = e.Container.DataContext as Cell;
+ if (cell == null || !Equals(cell.BindingContext, Element.SelectedItem))
+ return;
+
+ _itemNeedsSelecting = false;
+ OnItemSelected(Element.SelectedItem);
+ }
+
+ void OnItemSelected(object selectedItem)
+ {
+ if (_fromNative != null && Equals(selectedItem, _fromNative))
+ {
+ _fromNative = null;
+ return;
+ }
+
+ RestorePreviousSelectedVisual();
+
+ if (selectedItem == null)
+ {
+ _listBox.SelectedItem = selectedItem;
+ return;
+ }
+
+ IEnumerable<CellControl> items = FindDescendants<CellControl>(_listBox);
+
+ CellControl item = items.FirstOrDefault(i =>
+ {
+ var cell = (Cell)i.DataContext;
+ return Equals(cell.BindingContext, selectedItem);
+ });
+
+ if (item == null)
+ {
+ _itemNeedsSelecting = true;
+ return;
+ }
+
+ SetSelectedVisual(item);
+ }
+
+ void OnNativeItemTapped(object sender, GestureEventArgs e)
+ {
+ var cell = (Cell)Control.SelectedItem;
+ if (cell == null)
+ return;
+
+ Cell parentCell = null;
+
+ if (Element.IsGroupingEnabled)
+ {
+ TemplatedItemsList<ItemsView<Cell>, Cell> til = TemplatedItemsList<ItemsView<Cell>, Cell>.GetGroup(cell);
+ parentCell = til.HeaderContent;
+ }
+
+ _fromNative = cell.BindingContext;
+
+ if (Element.IsGroupingEnabled)
+ {
+ Element.NotifyRowTapped(TemplatedItemsList<ItemsView<Cell>, Cell>.GetIndex(parentCell), TemplatedItemsList<ItemsView<Cell>, Cell>.GetIndex(cell));
+ }
+ else
+ Element.NotifyRowTapped(TemplatedItemsList<ItemsView<Cell>, Cell>.GetIndex(cell));
+ }
+
+ void OnNativeSelectionChanged(object sender, SelectionChangedEventArgs e)
+ {
+ if (e.AddedItems.Count == 0)
+ return;
+
+ var cell = (Cell)e.AddedItems[0];
+ Cell parentCell = null;
+
+ if (cell == null)
+ {
+ RestorePreviousSelectedVisual();
+ return;
+ }
+
+ RestorePreviousSelectedVisual();
+ FrameworkElement element = FindElement(cell);
+ if (element != null)
+ SetSelectedVisual(element);
+ }
+
+ void OnPullToRefreshCanceled(object sender, EventArgs args)
+ {
+ if (Element.IsPullToRefreshEnabled && ((IListViewController)Element).RefreshAllowed)
+ _progressBar.Visibility = Visibility.Collapsed;
+ }
+
+ void OnPullToRefreshCompleted(object sender, EventArgs args)
+ {
+ if (Element.IsPullToRefreshEnabled && ((IListViewController)Element).RefreshAllowed)
+ {
+ _progressBar.IsIndeterminate = true;
+ ((IListViewController)Element).SendRefreshing();
+ }
+ }
+
+ void OnPullToRefreshStarted(object sender, EventArgs args)
+ {
+ if (Element.IsPullToRefreshEnabled && ((IListViewController)Element).RefreshAllowed)
+ {
+ _progressBar.Visibility = Visibility.Visible;
+ _progressBar.IsIndeterminate = false;
+ _progressBar.Value = Math.Max(0, Math.Min(1, _listBox.PullToRefreshStatus));
+ }
+ }
+
+ void OnPullToRefreshStatusUpdated(object sender, EventArgs eventArgs)
+ {
+ if (Element.IsPullToRefreshEnabled && ((IListViewController)Element).RefreshAllowed)
+ _progressBar.Value = Math.Max(0, Math.Min(1, _listBox.PullToRefreshStatus));
+ }
+
+ void OnScrollToRequested(object sender, ScrollToRequestedEventArgs e)
+ {
+ if (_animatable == null && e.ShouldAnimate)
+ _animatable = new Animatable();
+
+ if (_viewport == null)
+ {
+ // Making sure we're actually loaded
+ if (VisualTreeHelper.GetChildrenCount(_listBox) == 0)
+ {
+ RoutedEventHandler handler = null;
+ handler = (o, args) =>
+ {
+ Control.Loaded -= handler;
+ OnScrollToRequested(sender, e);
+ };
+ Control.Loaded += handler;
+
+ return;
+ }
+ _viewport = (ViewportControl)VisualTreeHelper.GetChild(VisualTreeHelper.GetChild(VisualTreeHelper.GetChild(_listBox, 0), 0), 0);
+ if (_viewport.Viewport.Bottom == 0)
+ {
+ EventHandler<ViewportChangedEventArgs> viewportChanged = null;
+ viewportChanged = (o, args) =>
+ {
+ if (_viewport.Viewport.Bottom == 0)
+ return;
+
+ _viewport.ViewportChanged -= viewportChanged;
+ OnScrollToRequested(sender, e);
+ };
+ _viewport.ViewportChanged += viewportChanged;
+ return;
+ }
+ }
+
+ double y = 0;
+ double targetHeight = 0;
+ double targetHeaderHeight = 0;
+
+ var templateReusables = new Dictionary<System.Windows.DataTemplate, FrameworkElement>();
+
+ var found = false;
+
+ if (Element.IsGroupingEnabled)
+ {
+ for (var g = 0; g < Element.TemplatedItems.Count; g++)
+ {
+ if (found)
+ break;
+
+ TemplatedItemsList<ItemsView<Cell>, Cell> til = Element.TemplatedItems.GetGroup(g);
+
+ double headerHeight = GetHeight(templateReusables, Control.GroupHeaderTemplate, til);
+ y += headerHeight;
+
+ for (var i = 0; i < til.Count; i++)
+ {
+ Cell cell = til[i];
+
+ double contentHeight = GetHeight(templateReusables, Control.ItemTemplate, cell);
+
+ if ((ReferenceEquals(til.BindingContext, e.Group) || e.Group == null) && ReferenceEquals(cell.BindingContext, e.Item))
+ {
+ targetHeaderHeight = headerHeight;
+ targetHeight = contentHeight;
+ found = true;
+ break;
+ }
+
+ y += contentHeight;
+ }
+ }
+ }
+ else
+ {
+ for (var i = 0; i < Element.TemplatedItems.Count; i++)
+ {
+ Cell cell = Element.TemplatedItems[i];
+
+ double height = GetHeight(templateReusables, Control.ItemTemplate, cell);
+
+ if (ReferenceEquals(cell.BindingContext, e.Item))
+ {
+ found = true;
+ targetHeight = height;
+ break;
+ }
+
+ y += height;
+ }
+ }
+
+ if (!found)
+ return;
+
+ ScrollToPosition position = e.Position;
+ if (position == ScrollToPosition.MakeVisible)
+ {
+ if (y >= _viewport.Viewport.Top && y <= _viewport.Viewport.Bottom)
+ return;
+ if (y > _viewport.Viewport.Bottom)
+ position = ScrollToPosition.End;
+ else
+ position = ScrollToPosition.Start;
+ }
+
+ if (position == ScrollToPosition.Start && Element.IsGroupingEnabled)
+ y = y - targetHeaderHeight;
+ else if (position == ScrollToPosition.Center)
+ y = y - (_viewport.ActualHeight / 2 + targetHeight / 2);
+ else if (position == ScrollToPosition.End)
+ y = y - _viewport.ActualHeight + targetHeight;
+
+ double startY = _viewport.Viewport.Y;
+ double distance = y - startY;
+
+ if (e.ShouldAnimate)
+ {
+ var animation = new Animation(v => { _viewport.SetViewportOrigin(new System.Windows.Point(0, startY + distance * v)); });
+
+ animation.Commit(_animatable, "ScrollTo", length: 500, easing: Easing.CubicInOut);
+ }
+ else
+ _viewport.SetViewportOrigin(new System.Windows.Point(0, y));
+ }
+
+ void RestorePreviousSelectedVisual()
+ {
+ foreach (Tuple<FrameworkElement, SLBinding, Brush> highlight in _previousHighlights)
+ {
+ if (highlight.Item2 != null)
+ highlight.Item1.SetForeground(highlight.Item2);
+ else
+ highlight.Item1.SetForeground(highlight.Item3);
+ }
+
+ _previousHighlights.Clear();
+ }
+
+ void SetSelectedVisual(FrameworkElement element)
+ {
+ IEnumerable<FrameworkElement> highlightMes = FindHighlight(element);
+ foreach (FrameworkElement toHighlight in highlightMes)
+ {
+ Brush brush = null;
+ SLBinding binding = toHighlight.GetForegroundBinding();
+ if (binding == null)
+ brush = toHighlight.GetForeground();
+
+ _previousHighlights.Add(new Tuple<FrameworkElement, SLBinding, Brush>(toHighlight, binding, brush));
+ toHighlight.SetForeground((Brush)System.Windows.Application.Current.Resources["PhoneAccentBrush"]);
+ }
+ }
+
+ void UpdateFooter()
+ {
+ Control.ListFooter = ((IListViewController)Element).FooterElement;
+ }
+
+ void UpdateHeader()
+ {
+ Control.ListHeader = ((IListViewController)Element).HeaderElement;
+ }
+
+ void UpdateIsRefreshing()
+ {
+ if (Element.IsRefreshing)
+ {
+ _progressBar.Visibility = Visibility.Visible;
+ _progressBar.IsIndeterminate = true;
+ }
+ else
+ {
+ _progressBar.IsIndeterminate = false;
+ _progressBar.Visibility = _listBox.IsInPullToRefresh && Element.IsPullToRefreshEnabled && ((IListViewController)Element).RefreshAllowed ? Visibility.Visible : Visibility.Collapsed;
+ }
+ }
+
+ void UpdateJumpList()
+ {
+ if (_listBox.IsGroupingEnabled && Element.GroupShortNameBinding == null)
+ _listBox.JumpListStyle = null;
+ else
+ _listBox.JumpListStyle = (System.Windows.Style)System.Windows.Application.Current.Resources["HeaderJumpStyle"];
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WP8/MD5.cs b/Xamarin.Forms.Platform.WP8/MD5.cs
new file mode 100644
index 00000000..2c0fc206
--- /dev/null
+++ b/Xamarin.Forms.Platform.WP8/MD5.cs
@@ -0,0 +1,12 @@
+using System.Security.Cryptography;
+
+namespace Xamarin.Forms.Platform.WinPhone
+{
+ internal abstract class MD5 : HashAlgorithm
+ {
+ public MD5()
+ {
+ HashSizeValue = 128;
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WP8/MD5CryptoServiceProvider.cs b/Xamarin.Forms.Platform.WP8/MD5CryptoServiceProvider.cs
new file mode 100644
index 00000000..aa5eba7f
--- /dev/null
+++ b/Xamarin.Forms.Platform.WP8/MD5CryptoServiceProvider.cs
@@ -0,0 +1,444 @@
+using System;
+
+namespace Xamarin.Forms.Platform.WinPhone
+{
+ internal sealed class MD5CryptoServiceProvider : MD5
+ {
+ const int BlockSizeBytes = 64;
+
+ static readonly uint[] K =
+ {
+ 0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501, 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, 0x6b901122,
+ 0xfd987193, 0xa679438e, 0x49b40821, 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8, 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, 0xa9e3e905,
+ 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a, 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70, 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05, 0xd9d4d039,
+ 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665, 0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1, 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, 0xf7537e82,
+ 0xbd3af235, 0x2ad7d2bb, 0xeb86d391
+ };
+
+ uint[] _buff;
+ ulong _count;
+ uint[] _h;
+ byte[] _processingBuffer; // Used to start data when passed less than a block worth.
+ int _processingBufferCount; // Counts how much data we have stored that still needs processed.
+
+ public MD5CryptoServiceProvider()
+ {
+ _h = new uint[4];
+ _buff = new uint[16];
+ _processingBuffer = new byte[BlockSizeBytes];
+
+ Initialize();
+ }
+
+ public override void Initialize()
+ {
+ _count = 0;
+ _processingBufferCount = 0;
+
+ _h[0] = 0x67452301;
+ _h[1] = 0xefcdab89;
+ _h[2] = 0x98badcfe;
+ _h[3] = 0x10325476;
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ if (_processingBuffer != null)
+ {
+ Array.Clear(_processingBuffer, 0, _processingBuffer.Length);
+ _processingBuffer = null;
+ }
+ if (_h != null)
+ {
+ Array.Clear(_h, 0, _h.Length);
+ _h = null;
+ }
+ if (_buff != null)
+ {
+ Array.Clear(_buff, 0, _buff.Length);
+ _buff = null;
+ }
+ }
+
+ protected override void HashCore(byte[] rgb, int ibStart, int cbSize)
+ {
+ int i;
+ State = 1;
+
+ if (_processingBufferCount != 0)
+ {
+ if (cbSize < BlockSizeBytes - _processingBufferCount)
+ {
+ Buffer.BlockCopy(rgb, ibStart, _processingBuffer, _processingBufferCount, cbSize);
+ _processingBufferCount += cbSize;
+ return;
+ }
+ i = BlockSizeBytes - _processingBufferCount;
+ Buffer.BlockCopy(rgb, ibStart, _processingBuffer, _processingBufferCount, i);
+ ProcessBlock(_processingBuffer, 0);
+ _processingBufferCount = 0;
+ ibStart += i;
+ cbSize -= i;
+ }
+
+ for (i = 0; i < cbSize - cbSize % BlockSizeBytes; i += BlockSizeBytes)
+ ProcessBlock(rgb, ibStart + i);
+
+ if (cbSize % BlockSizeBytes != 0)
+ {
+ Buffer.BlockCopy(rgb, cbSize - cbSize % BlockSizeBytes + ibStart, _processingBuffer, 0, cbSize % BlockSizeBytes);
+ _processingBufferCount = cbSize % BlockSizeBytes;
+ }
+ }
+
+ protected override byte[] HashFinal()
+ {
+ var hash = new byte[16];
+ int i, j;
+
+ ProcessFinalBlock(_processingBuffer, 0, _processingBufferCount);
+
+ for (i = 0; i < 4; i++)
+ {
+ for (j = 0; j < 4; j++)
+ hash[i * 4 + j] = (byte)(_h[i] >> j * 8);
+ }
+
+ return hash;
+ }
+
+ internal void AddLength(ulong length, byte[] buffer, int position)
+ {
+ buffer[position++] = (byte)length;
+ buffer[position++] = (byte)(length >> 8);
+ buffer[position++] = (byte)(length >> 16);
+ buffer[position++] = (byte)(length >> 24);
+ buffer[position++] = (byte)(length >> 32);
+ buffer[position++] = (byte)(length >> 40);
+ buffer[position++] = (byte)(length >> 48);
+ buffer[position] = (byte)(length >> 56);
+ }
+
+ void ProcessBlock(byte[] inputBuffer, int inputOffset)
+ {
+ uint a, b, c, d;
+ int i;
+
+ _count += BlockSizeBytes;
+
+ for (i = 0; i < 16; i++)
+ {
+ _buff[i] = inputBuffer[inputOffset + 4 * i] | ((uint)inputBuffer[inputOffset + 4 * i + 1] << 8) | ((uint)inputBuffer[inputOffset + 4 * i + 2] << 16) |
+ ((uint)inputBuffer[inputOffset + 4 * i + 3] << 24);
+ }
+
+ a = _h[0];
+ b = _h[1];
+ c = _h[2];
+ d = _h[3];
+
+ // This function was unrolled because it seems to be doubling our performance with current compiler/VM.
+ // Possibly roll up if this changes.
+
+ // ---- Round 1 --------
+
+ a += (((c ^ d) & b) ^ d) + K[0] + _buff[0];
+ a = (a << 7) | (a >> 25);
+ a += b;
+
+ d += (((b ^ c) & a) ^ c) + K[1] + _buff[1];
+ d = (d << 12) | (d >> 20);
+ d += a;
+
+ c += (((a ^ b) & d) ^ b) + K[2] + _buff[2];
+ c = (c << 17) | (c >> 15);
+ c += d;
+
+ b += (((d ^ a) & c) ^ a) + K[3] + _buff[3];
+ b = (b << 22) | (b >> 10);
+ b += c;
+
+ a += (((c ^ d) & b) ^ d) + K[4] + _buff[4];
+ a = (a << 7) | (a >> 25);
+ a += b;
+
+ d += (((b ^ c) & a) ^ c) + K[5] + _buff[5];
+ d = (d << 12) | (d >> 20);
+ d += a;
+
+ c += (((a ^ b) & d) ^ b) + K[6] + _buff[6];
+ c = (c << 17) | (c >> 15);
+ c += d;
+
+ b += (((d ^ a) & c) ^ a) + K[7] + _buff[7];
+ b = (b << 22) | (b >> 10);
+ b += c;
+
+ a += (((c ^ d) & b) ^ d) + K[8] + _buff[8];
+ a = (a << 7) | (a >> 25);
+ a += b;
+
+ d += (((b ^ c) & a) ^ c) + K[9] + _buff[9];
+ d = (d << 12) | (d >> 20);
+ d += a;
+
+ c += (((a ^ b) & d) ^ b) + K[10] + _buff[10];
+ c = (c << 17) | (c >> 15);
+ c += d;
+
+ b += (((d ^ a) & c) ^ a) + K[11] + _buff[11];
+ b = (b << 22) | (b >> 10);
+ b += c;
+
+ a += (((c ^ d) & b) ^ d) + K[12] + _buff[12];
+ a = (a << 7) | (a >> 25);
+ a += b;
+
+ d += (((b ^ c) & a) ^ c) + K[13] + _buff[13];
+ d = (d << 12) | (d >> 20);
+ d += a;
+
+ c += (((a ^ b) & d) ^ b) + K[14] + _buff[14];
+ c = (c << 17) | (c >> 15);
+ c += d;
+
+ b += (((d ^ a) & c) ^ a) + K[15] + _buff[15];
+ b = (b << 22) | (b >> 10);
+ b += c;
+
+ // ---- Round 2 --------
+
+ a += (((b ^ c) & d) ^ c) + K[16] + _buff[1];
+ a = (a << 5) | (a >> 27);
+ a += b;
+
+ d += (((a ^ b) & c) ^ b) + K[17] + _buff[6];
+ d = (d << 9) | (d >> 23);
+ d += a;
+
+ c += (((d ^ a) & b) ^ a) + K[18] + _buff[11];
+ c = (c << 14) | (c >> 18);
+ c += d;
+
+ b += (((c ^ d) & a) ^ d) + K[19] + _buff[0];
+ b = (b << 20) | (b >> 12);
+ b += c;
+
+ a += (((b ^ c) & d) ^ c) + K[20] + _buff[5];
+ a = (a << 5) | (a >> 27);
+ a += b;
+
+ d += (((a ^ b) & c) ^ b) + K[21] + _buff[10];
+ d = (d << 9) | (d >> 23);
+ d += a;
+
+ c += (((d ^ a) & b) ^ a) + K[22] + _buff[15];
+ c = (c << 14) | (c >> 18);
+ c += d;
+
+ b += (((c ^ d) & a) ^ d) + K[23] + _buff[4];
+ b = (b << 20) | (b >> 12);
+ b += c;
+
+ a += (((b ^ c) & d) ^ c) + K[24] + _buff[9];
+ a = (a << 5) | (a >> 27);
+ a += b;
+
+ d += (((a ^ b) & c) ^ b) + K[25] + _buff[14];
+ d = (d << 9) | (d >> 23);
+ d += a;
+
+ c += (((d ^ a) & b) ^ a) + K[26] + _buff[3];
+ c = (c << 14) | (c >> 18);
+ c += d;
+
+ b += (((c ^ d) & a) ^ d) + K[27] + _buff[8];
+ b = (b << 20) | (b >> 12);
+ b += c;
+
+ a += (((b ^ c) & d) ^ c) + K[28] + _buff[13];
+ a = (a << 5) | (a >> 27);
+ a += b;
+
+ d += (((a ^ b) & c) ^ b) + K[29] + _buff[2];
+ d = (d << 9) | (d >> 23);
+ d += a;
+
+ c += (((d ^ a) & b) ^ a) + K[30] + _buff[7];
+ c = (c << 14) | (c >> 18);
+ c += d;
+
+ b += (((c ^ d) & a) ^ d) + K[31] + _buff[12];
+ b = (b << 20) | (b >> 12);
+ b += c;
+
+ // ---- Round 3 --------
+
+ a += (b ^ c ^ d) + K[32] + _buff[5];
+ a = (a << 4) | (a >> 28);
+ a += b;
+
+ d += (a ^ b ^ c) + K[33] + _buff[8];
+ d = (d << 11) | (d >> 21);
+ d += a;
+
+ c += (d ^ a ^ b) + K[34] + _buff[11];
+ c = (c << 16) | (c >> 16);
+ c += d;
+
+ b += (c ^ d ^ a) + K[35] + _buff[14];
+ b = (b << 23) | (b >> 9);
+ b += c;
+
+ a += (b ^ c ^ d) + K[36] + _buff[1];
+ a = (a << 4) | (a >> 28);
+ a += b;
+
+ d += (a ^ b ^ c) + K[37] + _buff[4];
+ d = (d << 11) | (d >> 21);
+ d += a;
+
+ c += (d ^ a ^ b) + K[38] + _buff[7];
+ c = (c << 16) | (c >> 16);
+ c += d;
+
+ b += (c ^ d ^ a) + K[39] + _buff[10];
+ b = (b << 23) | (b >> 9);
+ b += c;
+
+ a += (b ^ c ^ d) + K[40] + _buff[13];
+ a = (a << 4) | (a >> 28);
+ a += b;
+
+ d += (a ^ b ^ c) + K[41] + _buff[0];
+ d = (d << 11) | (d >> 21);
+ d += a;
+
+ c += (d ^ a ^ b) + K[42] + _buff[3];
+ c = (c << 16) | (c >> 16);
+ c += d;
+
+ b += (c ^ d ^ a) + K[43] + _buff[6];
+ b = (b << 23) | (b >> 9);
+ b += c;
+
+ a += (b ^ c ^ d) + K[44] + _buff[9];
+ a = (a << 4) | (a >> 28);
+ a += b;
+
+ d += (a ^ b ^ c) + K[45] + _buff[12];
+ d = (d << 11) | (d >> 21);
+ d += a;
+
+ c += (d ^ a ^ b) + K[46] + _buff[15];
+ c = (c << 16) | (c >> 16);
+ c += d;
+
+ b += (c ^ d ^ a) + K[47] + _buff[2];
+ b = (b << 23) | (b >> 9);
+ b += c;
+
+ // ---- Round 4 --------
+
+ a += ((~d | b) ^ c) + K[48] + _buff[0];
+ a = (a << 6) | (a >> 26);
+ a += b;
+
+ d += ((~c | a) ^ b) + K[49] + _buff[7];
+ d = (d << 10) | (d >> 22);
+ d += a;
+
+ c += ((~b | d) ^ a) + K[50] + _buff[14];
+ c = (c << 15) | (c >> 17);
+ c += d;
+
+ b += ((~a | c) ^ d) + K[51] + _buff[5];
+ b = (b << 21) | (b >> 11);
+ b += c;
+
+ a += ((~d | b) ^ c) + K[52] + _buff[12];
+ a = (a << 6) | (a >> 26);
+ a += b;
+
+ d += ((~c | a) ^ b) + K[53] + _buff[3];
+ d = (d << 10) | (d >> 22);
+ d += a;
+
+ c += ((~b | d) ^ a) + K[54] + _buff[10];
+ c = (c << 15) | (c >> 17);
+ c += d;
+
+ b += ((~a | c) ^ d) + K[55] + _buff[1];
+ b = (b << 21) | (b >> 11);
+ b += c;
+
+ a += ((~d | b) ^ c) + K[56] + _buff[8];
+ a = (a << 6) | (a >> 26);
+ a += b;
+
+ d += ((~c | a) ^ b) + K[57] + _buff[15];
+ d = (d << 10) | (d >> 22);
+ d += a;
+
+ c += ((~b | d) ^ a) + K[58] + _buff[6];
+ c = (c << 15) | (c >> 17);
+ c += d;
+
+ b += ((~a | c) ^ d) + K[59] + _buff[13];
+ b = (b << 21) | (b >> 11);
+ b += c;
+
+ a += ((~d | b) ^ c) + K[60] + _buff[4];
+ a = (a << 6) | (a >> 26);
+ a += b;
+
+ d += ((~c | a) ^ b) + K[61] + _buff[11];
+ d = (d << 10) | (d >> 22);
+ d += a;
+
+ c += ((~b | d) ^ a) + K[62] + _buff[2];
+ c = (c << 15) | (c >> 17);
+ c += d;
+
+ b += ((~a | c) ^ d) + K[63] + _buff[9];
+ b = (b << 21) | (b >> 11);
+ b += c;
+
+ _h[0] += a;
+ _h[1] += b;
+ _h[2] += c;
+ _h[3] += d;
+ }
+
+ void ProcessFinalBlock(byte[] inputBuffer, int inputOffset, int inputCount)
+ {
+ ulong total = _count + (ulong)inputCount;
+ var paddingSize = (int)(56 - total % BlockSizeBytes);
+
+ if (paddingSize < 1)
+ paddingSize += BlockSizeBytes;
+
+ var fooBuffer = new byte[inputCount + paddingSize + 8];
+
+ for (var i = 0; i < inputCount; i++)
+ fooBuffer[i] = inputBuffer[i + inputOffset];
+
+ fooBuffer[inputCount] = 0x80;
+ for (int i = inputCount + 1; i < inputCount + paddingSize; i++)
+ fooBuffer[i] = 0x00;
+
+ // I deal in bytes. The algorithm deals in bits.
+ ulong size = total << 3;
+ AddLength(size, fooBuffer, inputCount + paddingSize);
+ ProcessBlock(fooBuffer, 0);
+
+ if (inputCount + paddingSize + 8 == 128)
+ ProcessBlock(fooBuffer, 64);
+ }
+
+ ~MD5CryptoServiceProvider()
+ {
+ Dispose(false);
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WP8/MasterDetailRenderer.cs b/Xamarin.Forms.Platform.WP8/MasterDetailRenderer.cs
new file mode 100644
index 00000000..43796d92
--- /dev/null
+++ b/Xamarin.Forms.Platform.WP8/MasterDetailRenderer.cs
@@ -0,0 +1,183 @@
+using System.ComponentModel;
+using System.Windows;
+using System.Windows.Controls;
+using Microsoft.Phone.Controls;
+
+namespace Xamarin.Forms.Platform.WinPhone
+{
+ public class MasterDetailRenderer : VisualElementRenderer<MasterDetailPage, FrameworkElement>
+ {
+ readonly SlideTransition _inTransition = new SlideTransition { Mode = SlideTransitionMode.SlideUpFadeIn };
+ readonly SlideTransition _outTransition = new SlideTransition { Mode = SlideTransitionMode.SlideDownFadeOut };
+ readonly Border _popup = new Border();
+ IVisualElementRenderer _detailRenderer;
+ IVisualElementRenderer _masterRenderer;
+
+ ITransition _toggleTransition;
+
+ public MasterDetailRenderer()
+ {
+ AutoPackage = false;
+ }
+
+ public bool Visible { get; private set; }
+
+ protected override System.Windows.Size ArrangeOverride(System.Windows.Size finalSize)
+ {
+ UpdateSizes(finalSize.Width, finalSize.Height);
+ return base.ArrangeOverride(finalSize);
+ }
+
+ protected override void OnElementChanged(ElementChangedEventArgs<MasterDetailPage> e)
+ {
+ base.OnElementChanged(e);
+
+ if (e.OldElement != null)
+ e.OldElement.BackButtonPressed -= HandleBackButtonPressed;
+
+ if (e.NewElement != null)
+ e.NewElement.BackButtonPressed += HandleBackButtonPressed;
+
+ LoadDetail();
+ LoadMaster();
+
+ UpdateSizes(ActualWidth, ActualHeight);
+
+ Loaded += (sender, args) =>
+ {
+ if (Element.IsPresented)
+ Toggle();
+ Element.SendAppearing();
+ };
+ Unloaded += (sender, args) =>
+ {
+ Element.SendDisappearing();
+ if (Visible)
+ {
+ var platform = (Platform)Element.Platform;
+ Canvas container = platform.GetCanvas();
+
+ container.Children.Remove(_popup);
+ }
+ };
+ }
+
+ protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ base.OnElementPropertyChanged(sender, e);
+
+ if (e.PropertyName == "Detail")
+ {
+ LoadDetail();
+ UpdateSizes(ActualWidth, ActualHeight);
+ }
+ else if (e.PropertyName == "Master")
+ {
+ LoadMaster();
+ UpdateSizes(ActualWidth, ActualHeight);
+ }
+ else if (e.PropertyName == MasterDetailPage.IsPresentedProperty.PropertyName)
+ {
+ if (Visible == Element.IsPresented)
+ return;
+ Toggle();
+ }
+ }
+
+ internal void Toggle()
+ {
+ var platform = Element.Platform as Platform;
+ Canvas container = platform.GetCanvas();
+
+ if (_toggleTransition != null)
+ return;
+
+ if (Visible)
+ {
+ _toggleTransition = _outTransition.GetTransition(_popup);
+ _toggleTransition.Begin();
+ _toggleTransition.Completed += (sender, args) =>
+ {
+ _toggleTransition.Stop();
+ container.Children.Remove(_popup);
+ _toggleTransition = null;
+ };
+ }
+ else
+ {
+ _popup.Child = _masterRenderer.ContainerElement;
+ container.Children.Add(_popup);
+
+ _toggleTransition = _inTransition.GetTransition(_popup);
+ _toggleTransition.Begin();
+
+ _toggleTransition.Completed += (sender, args) =>
+ {
+ _toggleTransition.Stop();
+ _toggleTransition = null;
+ };
+ }
+
+ Visible = !Visible;
+
+ ((IElementController)Element).SetValueFromRenderer(MasterDetailPage.IsPresentedProperty, Visible);
+ }
+
+ void HandleBackButtonPressed(object sender, BackButtonPressedEventArgs e)
+ {
+ if (Visible)
+ {
+ Toggle();
+ e.Handled = true;
+ }
+ }
+
+ void LoadDetail()
+ {
+ if (_detailRenderer != null)
+ Children.Remove(_detailRenderer.ContainerElement);
+
+ Page detail = Element.Detail;
+ if (Platform.GetRenderer(detail) == null)
+ Platform.SetRenderer(detail, Platform.CreateRenderer(detail));
+
+ _detailRenderer = Platform.GetRenderer(detail);
+
+ Children.Clear();
+ if (_detailRenderer != null)
+ Children.Add(_detailRenderer.ContainerElement);
+ }
+
+ void LoadMaster()
+ {
+ if (_masterRenderer != null && _popup != null)
+ _popup.Child = null;
+
+ Page master = Element.Master;
+ if (Platform.GetRenderer(master) == null)
+ Platform.SetRenderer(master, Platform.CreateRenderer(master));
+
+ _masterRenderer = Platform.GetRenderer(master);
+ var control = _masterRenderer as Panel;
+ if (control != null && master.BackgroundColor == Color.Default)
+ control.Background = Color.Black.ToBrush();
+ }
+
+ void UpdateSizes(double width, double height)
+ {
+ if (width <= 0 || height <= 0)
+ return;
+
+ var platform = Element.Platform as Platform;
+ Size screenSize = platform.Size;
+ Element.MasterBounds = new Rectangle(0, 0, screenSize.Width - 20, screenSize.Height - 20);
+ Element.DetailBounds = new Rectangle(0, 0, width, height);
+
+ _popup.Width = width - 20;
+ _popup.Height = height - 20;
+
+ Canvas.SetLeft(_popup, 10);
+ Canvas.SetTop(_popup, 10);
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WP8/NativeViewWrapper.cs b/Xamarin.Forms.Platform.WP8/NativeViewWrapper.cs
new file mode 100644
index 00000000..a28d0c9a
--- /dev/null
+++ b/Xamarin.Forms.Platform.WP8/NativeViewWrapper.cs
@@ -0,0 +1,24 @@
+using System.Windows;
+
+namespace Xamarin.Forms.Platform.WinPhone
+{
+ 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.WP8/NativeViewWrapperRenderer.cs b/Xamarin.Forms.Platform.WP8/NativeViewWrapperRenderer.cs
new file mode 100644
index 00000000..e28dec94
--- /dev/null
+++ b/Xamarin.Forms.Platform.WP8/NativeViewWrapperRenderer.cs
@@ -0,0 +1,62 @@
+using System.Windows;
+
+namespace Xamarin.Forms.Platform.WinPhone
+{
+ 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 System.Windows.Size ArrangeOverride(System.Windows.Size finalSize)
+ {
+ if (Element?.ArrangeOverrideDelegate == null)
+ return base.ArrangeOverride(finalSize);
+
+ // The user has specified a different implementation of ArrangeOverride
+ System.Windows.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 System.Windows.Size MeasureOverride()
+ {
+ return MeasureOverride(new System.Windows.Size());
+ }
+
+ protected override System.Windows.Size MeasureOverride(System.Windows.Size availableSize)
+ {
+ if (Element?.MeasureOverrideDelegate == null)
+ return base.MeasureOverride(availableSize);
+
+ // The user has specified a different implementation of MeasureOverride
+ System.Windows.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.WP8/NavigationMenuRenderer.cs b/Xamarin.Forms.Platform.WP8/NavigationMenuRenderer.cs
new file mode 100644
index 00000000..c5a983d3
--- /dev/null
+++ b/Xamarin.Forms.Platform.WP8/NavigationMenuRenderer.cs
@@ -0,0 +1,85 @@
+using System;
+using System.ComponentModel;
+using System.Windows.Media.Imaging;
+using Microsoft.Phone.Controls;
+
+namespace Xamarin.Forms.Platform.WinPhone
+{
+ internal class NavigationMenuRenderer : ViewRenderer<NavigationMenu, System.Windows.Controls.Grid>
+ {
+ const int Spacing = 12;
+
+ protected override void OnElementChanged(ElementChangedEventArgs<NavigationMenu> e)
+ {
+ base.OnElementChanged(e);
+
+ var grid = new System.Windows.Controls.Grid();
+ grid.ColumnDefinitions.Add(new System.Windows.Controls.ColumnDefinition { Width = System.Windows.GridLength.Auto });
+ grid.ColumnDefinitions.Add(new System.Windows.Controls.ColumnDefinition { Width = System.Windows.GridLength.Auto });
+
+ UpdateItems(grid);
+ SetNativeControl(grid);
+ }
+
+ protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ base.OnElementPropertyChanged(sender, e);
+
+ switch (e.PropertyName)
+ {
+ case "Targets":
+ UpdateItems(Control);
+ break;
+ }
+ }
+
+ TileSize GetSize()
+ {
+ return RenderSize.Width >= 210 * 2 + Spacing ? TileSize.Medium : TileSize.Default;
+ }
+
+ void UpdateItems(System.Windows.Controls.Grid grid)
+ {
+ grid.Children.Clear();
+
+ grid.RowDefinitions.Clear();
+
+ var x = 0;
+ var y = 0;
+ foreach (Page target in Element.Targets)
+ {
+ if (x > 1)
+ {
+ x = 0;
+ y++;
+ }
+
+ if (x == 0)
+ grid.RowDefinitions.Add(new System.Windows.Controls.RowDefinition());
+
+ var hubTile = new HubTile { Title = target.Title, Source = new BitmapImage(new Uri(target.Icon, UriKind.Relative)), Margin = new System.Windows.Thickness(0, 0, Spacing, Spacing) };
+
+ if (target.BackgroundColor != Color.Default)
+ hubTile.Background = target.BackgroundColor.ToBrush();
+
+ Page tmp = target;
+ hubTile.Tap += (sender, args) => Element.SendTargetSelected(tmp);
+
+ hubTile.SetValue(System.Windows.Controls.Grid.RowProperty, y);
+ hubTile.SetValue(System.Windows.Controls.Grid.ColumnProperty, x);
+ hubTile.Size = GetSize();
+
+ var weakRef = new WeakReference(hubTile);
+ SizeChanged += (sender, args) =>
+ {
+ if (weakRef.IsAlive)
+ ((HubTile)weakRef.Target).Size = GetSize();
+ ((IVisualElementController)Element).NativeSizeChanged();
+ };
+
+ x++;
+ grid.Children.Add(hubTile);
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WP8/NavigationPageRenderer.cs b/Xamarin.Forms.Platform.WP8/NavigationPageRenderer.cs
new file mode 100644
index 00000000..6f290107
--- /dev/null
+++ b/Xamarin.Forms.Platform.WP8/NavigationPageRenderer.cs
@@ -0,0 +1,209 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Diagnostics;
+using System.Linq;
+using System.Windows;
+using System.Windows.Controls;
+
+namespace Xamarin.Forms.Platform.WinPhone
+{
+ public class NavigationPageRenderer : VisualElementRenderer<NavigationPage, FrameworkElement>
+ {
+ Page _currentRoot;
+ bool _isRemoving;
+
+ public NavigationPageRenderer()
+ {
+ AutoPackage = false;
+ }
+
+ protected override void OnElementChanged(ElementChangedEventArgs<NavigationPage> e)
+ {
+ base.OnElementChanged(e);
+
+ Debug.WriteLine("Warning, Windows Phone backend does not support NavigationPage, falling back to global navigation.");
+
+ Action init = () =>
+ {
+ Element.PushRequested += PageOnPushed;
+ Element.PopRequested += PageOnPopped;
+ Element.PopToRootRequested += PageOnPoppedToRoot;
+ Element.RemovePageRequested += RemovePageRequested;
+ Element.InsertPageBeforeRequested += ElementOnInsertPageBeforeRequested;
+ Element.PropertyChanged += OnElementPropertyChanged;
+
+ var platform = (Platform)Element.Platform;
+ Element.ContainerArea = new Rectangle(new Point(0, 0), platform.Size);
+
+ platform.SizeChanged += (sender, args) => Element.ContainerArea = new Rectangle(new Point(0, 0), platform.Size);
+
+ List<Page> stack = GetStack();
+ if (stack.Count > 0)
+ UpdateRootPage(stack);
+ else
+ return;
+
+ Device.BeginInvokeOnMainThread(() =>
+ {
+ for (var i = 0; i < stack.Count; i++)
+ PageOnPushed(this, new NavigationRequestedEventArgs(stack[i], false, i != 0));
+ });
+ };
+
+ if (Element.Platform == null)
+ Element.PlatformSet += (sender, args) => init();
+ else
+ init();
+
+ Loaded += (sender, args) => Element.SendAppearing();
+ Unloaded += OnUnloaded;
+ }
+
+ void ElementOnInsertPageBeforeRequested(object sender, NavigationRequestedEventArgs eventArgs)
+ {
+ if (Element.Platform == null)
+ return;
+ var platform = Element.Platform as Platform;
+ if (platform != null)
+ ((INavigation)platform).InsertPageBefore(eventArgs.Page, eventArgs.BeforePage);
+
+ List<Page> stack = GetStack();
+ stack.Insert(stack.IndexOf(eventArgs.BeforePage), eventArgs.Page);
+
+ UpdateRootPage(stack);
+ }
+
+ List<Page> GetStack()
+ {
+ int count = Element.InternalChildren.Count;
+ var stack = new List<Page>(count);
+ for (var i = 0; i < count; i++)
+ stack.Add((Page)Element.InternalChildren[i]);
+
+ return stack;
+ }
+
+ void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ if (e.PropertyName != "Parent" || Element.RealParent != null)
+ return;
+
+ var platform = Element.Platform as Platform;
+
+ if (platform == null)
+ return;
+
+ for (var i = 0; i < Element.LogicalChildren.Count; i++)
+ {
+ var page = Element.LogicalChildren[i] as Page;
+ if (page != null)
+ platform.RemovePage(page, false);
+ }
+ }
+
+ void OnUnloaded(object sender, RoutedEventArgs args)
+ {
+ Element.SendDisappearing();
+ }
+
+ void PageOnPopped(object sender, NavigationRequestedEventArgs eventArg)
+ {
+ if (Element.Platform == null)
+ return;
+ var platform = Element.Platform as Platform;
+ if (platform != null)
+ eventArg.Task = platform.Pop(Element, eventArg.Animated).ContinueWith((t, o) => true, null);
+ }
+
+ void PageOnPoppedToRoot(object sender, NavigationRequestedEventArgs eventArgs)
+ {
+ if (Element.Platform == null)
+ return;
+ var platform = Element.Platform as Platform;
+ if (platform != null)
+ eventArgs.Task = platform.PopToRoot(Element, eventArgs.Animated).ContinueWith((t, o) => true, null);
+ }
+
+ void PageOnPushed(object sender, NavigationRequestedEventArgs e)
+ {
+ if (Element.Platform == null)
+ return;
+ var platform = Element.Platform as Platform;
+ if (platform != null)
+ {
+ if (e.Page == Element.StackCopy.LastOrDefault())
+ e.Page.IgnoresContainerArea = true;
+ e.Task = platform.PushCore(e.Page, Element, e.Animated, e.Realize).ContinueWith((t, o) => true, null);
+ }
+ }
+
+ void RemovePageRequested(object sender, NavigationRequestedEventArgs eventArgs)
+ {
+ if (Element.Platform == null)
+ return;
+ var platform = Element.Platform as Platform;
+ if (platform != null)
+ ((INavigation)platform).RemovePage(eventArgs.Page);
+
+ List<Page> stack = GetStack();
+ stack.Remove(eventArgs.Page);
+ _isRemoving = true;
+ UpdateRootPage(stack);
+ _isRemoving = false;
+ }
+
+ void UpdateRootPage(IReadOnlyList<Page> stack)
+ {
+ Page first = stack.FirstOrDefault();
+ if (first == _currentRoot)
+ return;
+
+ if (Children.Count > 0)
+ {
+ var renderer = Children[0] as IVisualElementRenderer;
+ if (renderer != null)
+ {
+ Children.RemoveAt(0);
+
+ var page = renderer.Element as Page;
+ if (page != null)
+ page.IgnoresContainerArea = false;
+
+ if (!stack.Contains(renderer.Element))
+ Platform.SetRenderer(renderer.Element, null);
+ }
+ }
+
+ _currentRoot = first;
+
+ if (first == null)
+ return;
+
+ first.IgnoresContainerArea = true;
+
+ IVisualElementRenderer firstRenderer = Platform.GetRenderer(first);
+ if (firstRenderer == null)
+ {
+ firstRenderer = Platform.CreateRenderer(first);
+ Platform.SetRenderer(first, firstRenderer);
+ }
+ var uiElement = (UIElement)firstRenderer;
+ var platform = Element.Platform as Platform;
+ Canvas canvas = platform?.GetCanvas();
+
+ //We could be swapping the visible page,
+ //so let's make sure we remove it
+ if (canvas.Children.Contains(uiElement))
+ canvas.Children.Remove(uiElement);
+ Children.Add(uiElement);
+
+ // we removed the previous root page, and the new root page is the one being presented
+ // at this time there's only 1 page now on the stack (the navigationpage with root)
+ // we need to update the platform to set this root page as the visible again
+ bool updateRoot = Element.CurrentPage == first && _isRemoving;
+ if (updateRoot)
+ platform.SetCurrent(Element, false);
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WP8/PageRenderer.cs b/Xamarin.Forms.Platform.WP8/PageRenderer.cs
new file mode 100644
index 00000000..6ba89ab5
--- /dev/null
+++ b/Xamarin.Forms.Platform.WP8/PageRenderer.cs
@@ -0,0 +1,18 @@
+using System.Windows.Controls;
+
+namespace Xamarin.Forms.Platform.WinPhone
+{
+ public class PageRenderer : VisualElementRenderer<Page, Panel>
+ {
+ protected override void OnElementChanged(ElementChangedEventArgs<Page> e)
+ {
+ // Set prior to calling base
+ Tracker = new BackgroundTracker<Panel>(BackgroundProperty) { Model = Element, Element = this };
+
+ base.OnElementChanged(e);
+
+ Loaded += (sender, args) => Element.SendAppearing();
+ Unloaded += (sender, args) => Element.SendDisappearing();
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WP8/PageToRendererConverter.cs b/Xamarin.Forms.Platform.WP8/PageToRendererConverter.cs
new file mode 100644
index 00000000..2c809c58
--- /dev/null
+++ b/Xamarin.Forms.Platform.WP8/PageToRendererConverter.cs
@@ -0,0 +1,24 @@
+using System;
+using System.Globalization;
+
+namespace Xamarin.Forms.Platform.WinPhone
+{
+ public class PageToRendererConverter : System.Windows.Data.IValueConverter
+ {
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ var page = value as Page;
+ if (page == null)
+ return null;
+
+ IVisualElementRenderer renderer = Platform.CreateRenderer(page);
+ Platform.SetRenderer(page, renderer);
+ return renderer;
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ throw new NotImplementedException();
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WP8/PickerRenderer.cs b/Xamarin.Forms.Platform.WP8/PickerRenderer.cs
new file mode 100644
index 00000000..f9cd2d81
--- /dev/null
+++ b/Xamarin.Forms.Platform.WP8/PickerRenderer.cs
@@ -0,0 +1,256 @@
+using System;
+using System.Collections.Specialized;
+using System.ComponentModel;
+using System.Reflection;
+using System.Windows;
+using System.Windows.Controls;
+using Microsoft.Phone.Controls;
+
+namespace Xamarin.Forms.Platform.WinPhone
+{
+ public class PickerRenderer : ViewRenderer<Picker, FrameworkElement>
+ {
+ bool _isChanging;
+
+ FormsListPicker _listPicker;
+
+ protected override void OnElementChanged(ElementChangedEventArgs<Picker> e)
+ {
+ _listPicker = new FormsListPicker();
+
+ UpdateAlignment();
+ UpdateIsEnabled();
+
+ base.OnElementChanged(e);
+
+ if (e.OldElement != null)
+ ((ObservableList<string>)Element.Items).CollectionChanged -= ItemsCollectionChanged;
+
+ ((ObservableList<string>)Element.Items).CollectionChanged += ItemsCollectionChanged;
+
+ _listPicker.ItemTemplate = (System.Windows.DataTemplate)System.Windows.Application.Current.Resources["PickerItemTemplate"];
+ _listPicker.FullModeItemTemplate = (System.Windows.DataTemplate)System.Windows.Application.Current.Resources["PickerFullItemTemplate"];
+ _listPicker.ExpansionMode = ExpansionMode.FullScreenOnly;
+ _listPicker.Items.Add(new ItemViewModel(" ") { MaxHeight = 0 });
+
+ _listPicker.ListPickerModeChanged += ListPickerModeChanged;
+
+ var grid = new System.Windows.Controls.Grid { Children = { _listPicker }, MaxWidth = Device.Info.PixelScreenSize.Width };
+ SetNativeControl(grid);
+
+ UpdatePicker();
+ _listPicker.SelectionChanged += PickerSelectionChanged;
+ }
+
+ protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ base.OnElementPropertyChanged(sender, e);
+ if (e.PropertyName == Picker.TitleProperty.PropertyName)
+ _listPicker.FullModeHeader = Element.Title;
+
+ if (e.PropertyName == VisualElement.IsEnabledProperty.PropertyName)
+ UpdateIsEnabled();
+
+ if (e.PropertyName == Picker.SelectedIndexProperty.PropertyName)
+ {
+ if (Element.SelectedIndex >= 0 && Element.SelectedIndex < Element.Items.Count)
+ _listPicker.SelectedIndex = Element.SelectedIndex + 1;
+ }
+
+ if (e.PropertyName == View.HorizontalOptionsProperty.PropertyName)
+ UpdateAlignment();
+ }
+
+ protected override void OnGotFocus(object sender, RoutedEventArgs args)
+ {
+ // Do nothing. ListPickerModeChanged is handling the IsFocusProperty setter
+ // Required because FrameworkElement.GotFocus and FrameworkElement.LostFocus () are fired by ListPicker.Open ()
+ }
+
+ protected override void OnLostFocus(object sender, RoutedEventArgs args)
+ {
+ // Do nothing. ListPickerModeChanged is handling the IsFocusProperty setter
+ // Required because FrameworkElement.GotFocus and FrameworkElement.LostFocus () are fired by ListPicker.Open ()
+ }
+
+ protected override void UpdateNativeWidget()
+ {
+ base.UpdateNativeWidget();
+ UpdateIsEnabled();
+ }
+
+ internal override void OnModelFocusChangeRequested(object sender, VisualElement.FocusRequestArgs args)
+ {
+ if (Control == null)
+ return;
+
+ if (args.Focus)
+ args.Result = OpenPickerPage();
+ else
+ {
+ args.Result = ClosePickerPage();
+ UnfocusControl(_listPicker);
+ }
+ }
+
+ bool ClosePickerPage()
+ {
+ FieldInfo pickerPageField = typeof(ListPicker).GetField("_listPickerPage", BindingFlags.NonPublic | BindingFlags.Instance);
+ var pickerPage = pickerPageField.GetValue(Control) as ListPickerPage;
+ typeof(ListPickerPage).InvokeMember("ClosePickerPage", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.InvokeMethod, Type.DefaultBinder, pickerPage, null);
+
+ return true;
+ }
+
+ void ItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
+ {
+ UpdateItems();
+ }
+
+ void ListPickerModeChanged(object sender, DependencyPropertyChangedEventArgs e)
+ {
+ if (e.OldValue == null || e.NewValue == null)
+ return;
+
+ var oldVal = (ListPickerMode)e.OldValue;
+ var newVal = (ListPickerMode)e.NewValue;
+
+ if (oldVal == ListPickerMode.Normal && newVal == ListPickerMode.Full)
+ {
+ // Picker Page is now showing
+ ((IElementController)Element).SetValueFromRenderer(VisualElement.IsFocusedPropertyKey, true);
+ }
+ else if (oldVal == ListPickerMode.Full && newVal == ListPickerMode.Normal)
+ {
+ // PickerPage is now dismissed
+ ((IElementController)Element).SetValueFromRenderer(VisualElement.IsFocusedPropertyKey, false);
+ }
+ }
+
+ bool OpenPickerPage()
+ {
+ bool result = _listPicker.Open();
+
+ if (result)
+ return true;
+
+ return false;
+ }
+
+ void PickerSelectionChanged(object sender, SelectionChangedEventArgs e)
+ {
+ if (_isChanging)
+ return;
+
+ var picker = (ListPicker)sender;
+
+ // initializing picker
+ if (picker.SelectedIndex == -1)
+ return;
+
+ int elementSelectedIndex = picker.SelectedIndex - 1;
+ ((IElementController)Element).SetValueFromRenderer(Picker.SelectedIndexProperty, elementSelectedIndex);
+ }
+
+ void UpdateAlignment()
+ {
+ if (Element.HorizontalOptions.Alignment != LayoutAlignment.Fill)
+ _listPicker.HorizontalAlignment = HorizontalAlignment.Left;
+ }
+
+ void UpdateIsEnabled()
+ {
+ if (_listPicker != null)
+ _listPicker.IsEnabled = Element.IsEnabled;
+ }
+
+ void UpdateItems()
+ {
+ // supress notification of non-user generated events (e.g. adding\syncing list values)
+ _isChanging = true;
+ FormsListPicker picker = _listPicker;
+ // add/remove slots from control to match element
+ while (picker.Items.Count < Element.Items.Count + 1)
+ picker.Items.Add(new ItemViewModel(string.Empty));
+
+ while (picker.Items.Count > Element.Items.Count + 1)
+ picker.Items.RemoveAt(picker.Items.Count - 1);
+
+ // update all control values to match element values
+ for (var i = 0; i < Element.Items.Count; i++)
+ {
+ var item = (ItemViewModel)picker.Items[i + 1];
+ if (item.Data == Element.Items[i])
+ continue;
+
+ item.Data = Element.Items[i];
+ }
+
+ picker.SelectedIndex = Element.SelectedIndex + 1;
+
+ _isChanging = false;
+ }
+
+ void UpdatePicker()
+ {
+ _listPicker.FullModeHeader = Element.Title;
+ UpdateItems();
+ _listPicker.SelectedIndex = Element.SelectedIndex + 1;
+ }
+
+ class ItemViewModel : INotifyPropertyChanged
+ {
+ string _data;
+ int _maxHeight;
+ float _opacity;
+
+ public ItemViewModel(string item)
+ {
+ _opacity = 1;
+ _data = item;
+ _maxHeight = int.MaxValue;
+ }
+
+ public string Data
+ {
+ get { return _data; }
+ set
+ {
+ if (value == _data)
+ return;
+
+ _data = value;
+ PropertyChanged(this, new PropertyChangedEventArgs("Data"));
+ }
+ }
+
+ public int MaxHeight
+ {
+ get { return _maxHeight; }
+ set
+ {
+ if (value == _maxHeight)
+ return;
+
+ _maxHeight = value;
+ PropertyChanged(this, new PropertyChangedEventArgs("MaxHeight"));
+ }
+ }
+
+ public float Opacity
+ {
+ get { return _opacity; }
+ set
+ {
+ if (value == _opacity)
+ return;
+
+ _opacity = value;
+ PropertyChanged(this, new PropertyChangedEventArgs("Opacity"));
+ }
+ }
+
+ public event PropertyChangedEventHandler PropertyChanged = delegate { };
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WP8/Platform.cs b/Xamarin.Forms.Platform.WP8/Platform.cs
new file mode 100644
index 00000000..363c6b84
--- /dev/null
+++ b/Xamarin.Forms.Platform.WP8/Platform.cs
@@ -0,0 +1,628 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Linq;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using Microsoft.Phone.Controls;
+using Microsoft.Phone.Shell;
+
+namespace Xamarin.Forms.Platform.WinPhone
+{
+ // from mono
+ public class Platform : BindableObject, IPlatform, INavigation
+ {
+ internal static readonly BindableProperty RendererProperty = BindableProperty.CreateAttached("Renderer", typeof(IVisualElementRenderer), typeof(Platform), default(IVisualElementRenderer));
+
+ readonly TurnstileTransition _backwardInTransition = new TurnstileTransition { Mode = TurnstileTransitionMode.BackwardIn };
+
+ readonly TurnstileTransition _backwardOutTransition = new TurnstileTransition { Mode = TurnstileTransitionMode.BackwardOut };
+
+ readonly TurnstileTransition _forwardInTransition = new TurnstileTransition { Mode = TurnstileTransitionMode.ForwardIn };
+
+ readonly TurnstileTransition _forwardOutTransition = new TurnstileTransition { Mode = TurnstileTransitionMode.ForwardOut };
+
+ readonly NavigationModel _navModel = new NavigationModel();
+
+ readonly PhoneApplicationPage _page;
+
+ readonly Canvas _renderer;
+ readonly ToolbarTracker _tracker = new ToolbarTracker();
+
+ Page _currentDisplayedPage;
+ CustomMessageBox _visibleMessageBox;
+
+ internal Platform(PhoneApplicationPage page)
+ {
+ _tracker.SeparateMasterDetail = true;
+
+ page.BackKeyPress += OnBackKeyPress;
+ _page = page;
+
+ _renderer = new Canvas();
+ _renderer.SizeChanged += RendererSizeChanged;
+
+ _tracker.CollectionChanged += (sender, args) => UpdateToolbarItems();
+
+ ProgressIndicator indicator;
+ SystemTray.SetProgressIndicator(page, indicator = new ProgressIndicator { IsVisible = false, IsIndeterminate = true });
+
+ var busyCount = 0;
+ MessagingCenter.Subscribe(this, Page.BusySetSignalName, (Page sender, bool enabled) =>
+ {
+ busyCount = Math.Max(0, enabled ? busyCount + 1 : busyCount - 1);
+ indicator.IsVisible = busyCount > 0;
+ });
+
+ MessagingCenter.Subscribe(this, Page.AlertSignalName, (Page sender, AlertArguments arguments) =>
+ {
+ var messageBox = new CustomMessageBox { Title = arguments.Title, Message = arguments.Message };
+ if (arguments.Accept != null)
+ messageBox.LeftButtonContent = arguments.Accept;
+ messageBox.RightButtonContent = arguments.Cancel;
+ messageBox.Show();
+ _visibleMessageBox = messageBox;
+ messageBox.Dismissed += (o, args) =>
+ {
+ arguments.SetResult(args.Result == CustomMessageBoxResult.LeftButton);
+ _visibleMessageBox = null;
+ };
+ });
+
+ MessagingCenter.Subscribe(this, Page.ActionSheetSignalName, (Page sender, ActionSheetArguments arguments) =>
+ {
+ var messageBox = new CustomMessageBox { Title = arguments.Title };
+
+ var listBox = new ListBox { FontSize = 36, Margin = new System.Windows.Thickness(12) };
+ var itemSource = new List<string>();
+
+ if (!string.IsNullOrWhiteSpace(arguments.Destruction))
+ itemSource.Add(arguments.Destruction);
+ itemSource.AddRange(arguments.Buttons);
+ if (!string.IsNullOrWhiteSpace(arguments.Cancel))
+ itemSource.Add(arguments.Cancel);
+
+ listBox.ItemsSource = itemSource.Select(s => new TextBlock { Text = s, Margin = new System.Windows.Thickness(0, 12, 0, 12) });
+ messageBox.Content = listBox;
+
+ listBox.SelectionChanged += (o, args) => messageBox.Dismiss();
+ messageBox.Dismissed += (o, args) =>
+ {
+ string result = listBox.SelectedItem != null ? ((TextBlock)listBox.SelectedItem).Text : null;
+ arguments.SetResult(result);
+ _visibleMessageBox = null;
+ };
+
+ messageBox.Show();
+ _visibleMessageBox = messageBox;
+ });
+ }
+
+ internal Size Size
+ {
+ get { return new Size(_renderer.ActualWidth, _renderer.ActualHeight); }
+ }
+
+ Page Page { get; set; }
+
+ void INavigation.InsertPageBefore(Page page, Page before)
+ {
+ _navModel.InsertPageBefore(page, before);
+ }
+
+ IReadOnlyList<Page> INavigation.ModalStack
+ {
+ get { return _navModel.Roots.ToList(); }
+ }
+
+ IReadOnlyList<Page> INavigation.NavigationStack
+ {
+ get { return _navModel.Tree.Last(); }
+ }
+
+ Task<Page> INavigation.PopAsync()
+ {
+ return ((INavigation)this).PopAsync(true);
+ }
+
+ Task<Page> INavigation.PopAsync(bool animated)
+ {
+ return Pop(Page, animated);
+ }
+
+ Task<Page> INavigation.PopModalAsync()
+ {
+ return ((INavigation)this).PopModalAsync(true);
+ }
+
+ Task<Page> INavigation.PopModalAsync(bool animated)
+ {
+ var tcs = new TaskCompletionSource<Page>();
+ Page result = _navModel.PopModal();
+
+ IReadOnlyList<Page> last = _navModel.Tree.Last();
+ IEnumerable<Page> stack = last;
+ if (last.Count > 1)
+ stack = stack.Skip(1);
+
+ Page navRoot = stack.First();
+ Page current = _navModel.CurrentPage;
+ if (current == navRoot)
+ current = _navModel.Roots.Last(); // Navigation page itself, since nav root has a host
+
+ SetCurrent(current, animated, true, () => tcs.SetResult(result));
+ return tcs.Task;
+ }
+
+ Task INavigation.PopToRootAsync()
+ {
+ return ((INavigation)this).PopToRootAsync(true);
+ }
+
+ async Task INavigation.PopToRootAsync(bool animated)
+ {
+ await PopToRoot(Page, animated);
+ }
+
+ Task INavigation.PushAsync(Page root)
+ {
+ return ((INavigation)this).PushAsync(root, true);
+ }
+
+ Task INavigation.PushAsync(Page root, bool animated)
+ {
+ return Push(root, Page, animated);
+ }
+
+ Task INavigation.PushModalAsync(Page modal)
+ {
+ return ((INavigation)this).PushModalAsync(modal, true);
+ }
+
+ Task INavigation.PushModalAsync(Page modal, bool animated)
+ {
+ var tcs = new TaskCompletionSource<object>();
+ _navModel.PushModal(modal);
+ SetCurrent(_navModel.CurrentPage, animated, completedCallback: () => tcs.SetResult(null));
+ return tcs.Task;
+ }
+
+ void INavigation.RemovePage(Page page)
+ {
+ RemovePage(page, true);
+ }
+
+ SizeRequest IPlatform.GetNativeSize(VisualElement view, 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 && GetRenderer(view) != null)
+ {
+ IVisualElementRenderer element = GetRenderer(view);
+ return element.GetDesiredSize(widthConstraint, heightConstraint);
+ }
+
+ return new SizeRequest();
+ }
+
+ public static IVisualElementRenderer CreateRenderer(VisualElement element)
+ {
+ IVisualElementRenderer result = Registrar.Registered.GetHandler<IVisualElementRenderer>(element.GetType()) ?? new ViewRenderer();
+ result.SetElement(element);
+ return result;
+ }
+
+ public static IVisualElementRenderer GetRenderer(VisualElement self)
+ {
+ return (IVisualElementRenderer)self.GetValue(RendererProperty);
+ }
+
+ public static void SetRenderer(VisualElement self, IVisualElementRenderer renderer)
+ {
+ self.SetValue(RendererProperty, renderer);
+ self.IsPlatformEnabled = renderer != null;
+ }
+
+ internal Canvas GetCanvas()
+ {
+ return _renderer;
+ }
+
+ internal async Task<Page> Pop(Page ancestor, bool animated)
+ {
+ Page result = _navModel.Pop(ancestor);
+
+ Page navRoot = _navModel.Tree.Last().Skip(1).First();
+ Page current = _navModel.CurrentPage;
+
+ // The following code is a terrible horrible ugly hack that we are kind of stuck with for the time being
+ // Effectively what can happen is a TabbedPage with many navigation page children needs to have all those children in the
+ // nav stack. If you have multiple each of those roots needs to be skipped over.
+
+ // In general the check for the NavigationPage will always hit if the check for the Skip(1) hits, but since that check
+ // was always there it is left behind to ensure compatibility with previous behavior.
+ bool replaceWithRoot = current == navRoot;
+ var parent = current.Parent as NavigationPage;
+ if (parent != null)
+ {
+ if (parent.InternalChildren[0] == current)
+ replaceWithRoot = true;
+ }
+
+ if (replaceWithRoot)
+ current = _navModel.Roots.Last(); // Navigation page itself, since nav root has a host
+
+ await SetCurrent(current, animated, true);
+ return result;
+ }
+
+ internal async Task PopToRoot(Page ancestor, bool animated)
+ {
+ _navModel.PopToRoot(ancestor);
+ await SetCurrent(_navModel.CurrentPage, animated, true);
+ }
+
+ internal async Task PushCore(Page root, Page ancester, bool animated, bool realize = true)
+ {
+ _navModel.Push(root, ancester);
+ if (realize)
+ await SetCurrent(_navModel.CurrentPage, animated);
+
+ if (root.NavigationProxy.Inner == null)
+ root.NavigationProxy.Inner = this;
+ }
+
+ internal async void RemovePage(Page page, bool popCurrent)
+ {
+ if (popCurrent && _navModel.CurrentPage == page)
+ await ((INavigation)this).PopAsync();
+ else
+ _navModel.RemovePage(page);
+ }
+
+ internal Task SetCurrent(Page page, bool animated, bool popping = false, Action completedCallback = null)
+ {
+ var tcs = new TaskCompletionSource<bool>();
+ if (page == _currentDisplayedPage)
+ {
+ tcs.SetResult(true);
+ return tcs.Task;
+ }
+
+ if (!animated)
+ tcs.SetResult(true);
+
+ page.Platform = this;
+
+ if (GetRenderer(page) == null)
+ SetRenderer(page, CreateRenderer(page));
+
+ page.Layout(new Rectangle(0, 0, _renderer.ActualWidth, _renderer.ActualHeight));
+ IVisualElementRenderer pageRenderer = GetRenderer(page);
+ if (pageRenderer != null)
+ {
+ ((FrameworkElement)pageRenderer.ContainerElement).Width = _renderer.ActualWidth;
+ ((FrameworkElement)pageRenderer.ContainerElement).Height = _renderer.ActualHeight;
+ }
+
+ Page current = _currentDisplayedPage;
+ UIElement currentElement = null;
+ if (current != null)
+ currentElement = (UIElement)GetRenderer(current);
+
+ if (popping)
+ {
+ ITransition transitionOut = null;
+ if (current != null)
+ {
+ if (animated)
+ transitionOut = _backwardOutTransition.GetTransition(currentElement);
+ else
+ _renderer.Children.Remove(currentElement);
+ }
+
+ var pageElement = (UIElement)GetRenderer(page);
+
+ if (animated)
+ {
+ transitionOut.Completed += (s, e) =>
+ {
+ transitionOut.Stop();
+ _renderer.Children.Remove(currentElement);
+ UpdateToolbarTracker();
+
+ _renderer.Children.Add(pageElement);
+
+ ITransition transitionIn = _backwardInTransition.GetTransition(pageElement);
+ transitionIn.Completed += (si, ei) =>
+ {
+ transitionIn.Stop();
+ if (completedCallback != null)
+ completedCallback();
+
+ tcs.SetResult(true);
+ };
+ transitionIn.Begin();
+ };
+
+ transitionOut.Begin();
+ }
+ else
+ {
+ UpdateToolbarTracker();
+ _renderer.Children.Add(pageElement);
+ if (completedCallback != null)
+ completedCallback();
+ }
+ }
+ else
+ {
+ ITransition transitionOut = null;
+ if (current != null)
+ {
+ if (animated)
+ transitionOut = _forwardOutTransition.GetTransition(currentElement);
+ else
+ _renderer.Children.Remove(currentElement);
+ }
+
+ if (animated)
+ {
+ if (transitionOut != null)
+ {
+ transitionOut.Completed += (o, e) =>
+ {
+ _renderer.Children.Remove(currentElement);
+ transitionOut.Stop();
+
+ UpdateToolbarTracker();
+
+ var element = (UIElement)GetRenderer(page);
+ _renderer.Children.Add(element);
+ ITransition transitionIn = _forwardInTransition.GetTransition(element);
+ transitionIn.Completed += (s, ie) =>
+ {
+ transitionIn.Stop();
+ if (completedCallback != null)
+ completedCallback();
+ tcs.SetResult(true);
+ };
+ transitionIn.Begin();
+ };
+
+ transitionOut.Begin();
+ }
+ else
+ {
+ UpdateToolbarTracker();
+
+ _renderer.Children.Add((UIElement)GetRenderer(page));
+ ITransition transitionIn = _forwardInTransition.GetTransition((UIElement)GetRenderer(page));
+ transitionIn.Completed += (s, e) =>
+ {
+ transitionIn.Stop();
+ if (completedCallback != null)
+ completedCallback();
+
+ tcs.SetResult(true);
+ };
+ transitionIn.Begin();
+ }
+ }
+ else
+ {
+ _renderer.Children.Add((UIElement)GetRenderer(page));
+ UpdateToolbarTracker();
+ if (completedCallback != null)
+ completedCallback();
+ }
+ }
+
+ _currentDisplayedPage = page;
+
+ return tcs.Task;
+ }
+
+ internal void SetPage(Page newRoot)
+ {
+ if (newRoot == null)
+ return;
+
+ Page = newRoot;
+ _navModel.Clear();
+ _navModel.PushModal(newRoot);
+ SetCurrent(newRoot, false, true);
+
+ ((Application)newRoot.RealParent).NavigationProxy.Inner = this;
+ }
+
+ internal event EventHandler SizeChanged;
+
+ void OnBackKeyPress(object sender, CancelEventArgs e)
+ {
+ if (_visibleMessageBox != null)
+ {
+ _visibleMessageBox.Dismiss();
+ e.Cancel = true;
+ return;
+ }
+
+ Page lastRoot = _navModel.Roots.Last();
+
+ bool handled = lastRoot.SendBackButtonPressed();
+
+ e.Cancel = handled;
+ }
+
+ Task Push(Page root, Page ancester, bool animated)
+ {
+ return PushCore(root, ancester, animated);
+ }
+
+ void RendererSizeChanged(object sender, SizeChangedEventArgs e)
+ {
+ UpdateFormSizes();
+ EventHandler handler = SizeChanged;
+ if (handler != null)
+ handler(this, EventArgs.Empty);
+ }
+
+ void UpdateFormSizes()
+ {
+ foreach (Page f in _navModel.Roots)
+ {
+ f.Layout(new Rectangle(0, 0, _renderer.ActualWidth, _renderer.ActualHeight));
+ IVisualElementRenderer pageRenderer = f.GetRenderer();
+ if (pageRenderer != null)
+ {
+ ((FrameworkElement)pageRenderer.ContainerElement).Width = _renderer.ActualWidth;
+ ((FrameworkElement)pageRenderer.ContainerElement).Height = _renderer.ActualHeight;
+ }
+ }
+ }
+
+ void UpdateToolbarItems()
+ {
+ if (_page.ApplicationBar == null)
+ _page.ApplicationBar = new ApplicationBar();
+
+ ToolbarItem[] items = _tracker.ToolbarItems.ToArray();
+ MasterDetailPage masterDetail = _tracker.Target.Descendants().Prepend(_tracker.Target).OfType<MasterDetailPage>().FirstOrDefault();
+
+ TaggedAppBarButton oldMasterDetailButton = _page.ApplicationBar.Buttons.OfType<TaggedAppBarButton>().FirstOrDefault(b => b.Tag is MasterDetailPage && b.Tag != masterDetail);
+
+ if (oldMasterDetailButton != null)
+ _page.ApplicationBar.Buttons.Remove(oldMasterDetailButton);
+
+ if (masterDetail != null)
+ {
+ if (masterDetail.ShouldShowToolbarButton())
+ {
+ if (_page.ApplicationBar.Buttons.OfType<TaggedAppBarButton>().All(b => b.Tag != masterDetail))
+ {
+ var button = new TaggedAppBarButton
+ {
+ IconUri = new Uri(masterDetail.Master.Icon ?? "ApplicationIcon.jpg", UriKind.Relative),
+ Text = masterDetail.Master.Title,
+ IsEnabled = true,
+ Tag = masterDetail
+ };
+ button.Click += (sender, args) =>
+ {
+ var masterDetailRenderer = GetRenderer(masterDetail) as MasterDetailRenderer;
+
+ if (masterDetailRenderer != null)
+ masterDetailRenderer.Toggle();
+ };
+ _page.ApplicationBar.Buttons.Add(button);
+ }
+ }
+ }
+
+ var buttonsToAdd = new List<TaggedAppBarButton>();
+ foreach (ToolbarItem item in items.Where(i => i.Order != ToolbarItemOrder.Secondary))
+ {
+ if (_page.ApplicationBar.Buttons.OfType<TaggedAppBarButton>().Any(b => b.Tag == item))
+ continue;
+
+ var button = new TaggedAppBarButton
+ {
+ IconUri = new Uri(item.Icon ?? "ApplicationIcon.jpg", UriKind.Relative),
+ Text = !string.IsNullOrWhiteSpace(item.Name) ? item.Text : (string)item.Icon ?? "ApplicationIcon.jpg",
+ IsEnabled = item.IsEnabled,
+ Tag = item
+ };
+ button.Click += (sender, args) => item.Activate();
+ buttonsToAdd.Add(button);
+ }
+
+ var menuItemsToAdd = new List<TaggedAppBarMenuItem>();
+ foreach (ToolbarItem item in items.Where(i => i.Order == ToolbarItemOrder.Secondary))
+ {
+ if (_page.ApplicationBar.MenuItems.OfType<TaggedAppBarMenuItem>().Any(b => b.Tag == item))
+ continue;
+
+ var button = new TaggedAppBarMenuItem { Text = !string.IsNullOrWhiteSpace(item.Name) ? item.Text : (string)item.Icon ?? "MenuItem", IsEnabled = true, Tag = item };
+ button.Click += (sender, args) => item.Activate();
+ menuItemsToAdd.Add(button);
+ }
+
+ TaggedAppBarButton[] deadButtons = _page.ApplicationBar.Buttons.OfType<TaggedAppBarButton>().Where(b => b.Tag is ToolbarItem && !items.Contains(b.Tag)).ToArray();
+
+ TaggedAppBarMenuItem[] deadMenuItems = _page.ApplicationBar.MenuItems.OfType<TaggedAppBarMenuItem>().Where(b => b.Tag is ToolbarItem && !items.Contains(b.Tag)).ToArray();
+
+ // we must remove the dead buttons before adding the new ones so we dont accidentally go over the limit during the tranistion
+ foreach (TaggedAppBarButton deadButton in deadButtons)
+ {
+ deadButton.Dispose();
+ _page.ApplicationBar.Buttons.Remove(deadButton);
+ }
+
+ foreach (TaggedAppBarMenuItem deadMenuItem in deadMenuItems)
+ _page.ApplicationBar.MenuItems.Remove(deadMenuItem);
+
+ // fixme, insert in order
+ foreach (TaggedAppBarButton newButton in buttonsToAdd)
+ _page.ApplicationBar.Buttons.Add(newButton);
+
+ foreach (TaggedAppBarMenuItem newMenuItem in menuItemsToAdd)
+ _page.ApplicationBar.MenuItems.Add(newMenuItem);
+
+ _page.ApplicationBar.IsVisible = _page.ApplicationBar.Buttons.Count > 0 || _page.ApplicationBar.MenuItems.Count > 0;
+ }
+
+ void UpdateToolbarTracker()
+ {
+ if (_navModel.Roots.Last() != null)
+ _tracker.Target = _navModel.Roots.Last();
+ }
+
+ class TaggedAppBarButton : ApplicationBarIconButton, IDisposable
+ {
+ bool _disposed;
+ object _tag;
+
+ public object Tag
+ {
+ get { return _tag; }
+ set
+ {
+ if (_tag == null && value is ToolbarItem)
+ (value as ToolbarItem).PropertyChanged += TaggedAppBarButton_PropertyChanged;
+ _tag = value;
+ }
+ }
+
+ public void Dispose()
+ {
+ if (_disposed)
+ return;
+ _disposed = true;
+
+ if (Tag != null && Tag is ToolbarItem)
+ (Tag as ToolbarItem).PropertyChanged -= TaggedAppBarButton_PropertyChanged;
+ }
+
+ void TaggedAppBarButton_PropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ var item = Tag as ToolbarItem;
+ if (item == null)
+ return;
+
+ if (e.PropertyName == MenuItem.IsEnabledProperty.PropertyName)
+ IsEnabled = item.IsEnabled;
+ else if (e.PropertyName == MenuItem.TextProperty.PropertyName)
+ Text = !string.IsNullOrWhiteSpace(item.Name) ? item.Text : (string)item.Icon ?? "ApplicationIcon.jpg";
+ else if (e.PropertyName == MenuItem.IconProperty.PropertyName)
+ IconUri = new Uri(item.Icon ?? "ApplicationIcon.jpg", UriKind.Relative);
+ }
+ }
+
+ class TaggedAppBarMenuItem : ApplicationBarMenuItem
+ {
+ public object Tag { get; set; }
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WP8/PlatformEffect.cs b/Xamarin.Forms.Platform.WP8/PlatformEffect.cs
new file mode 100644
index 00000000..3ee0f416
--- /dev/null
+++ b/Xamarin.Forms.Platform.WP8/PlatformEffect.cs
@@ -0,0 +1,8 @@
+using System.Windows;
+
+namespace Xamarin.Forms.Platform.WinPhone
+{
+ public abstract class PlatformEffect : PlatformEffect<FrameworkElement, FrameworkElement>
+ {
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WP8/ProgressBarRenderer.cs b/Xamarin.Forms.Platform.WP8/ProgressBarRenderer.cs
new file mode 100644
index 00000000..b027d674
--- /dev/null
+++ b/Xamarin.Forms.Platform.WP8/ProgressBarRenderer.cs
@@ -0,0 +1,35 @@
+using System.ComponentModel;
+using System.Windows;
+
+namespace Xamarin.Forms.Platform.WinPhone
+{
+ public class ProgressBarRenderer : ViewRenderer<ProgressBar, System.Windows.Controls.ProgressBar>
+ {
+ protected override void OnElementChanged(ElementChangedEventArgs<ProgressBar> e)
+ {
+ base.OnElementChanged(e);
+
+ var progressBar = new System.Windows.Controls.ProgressBar { Minimum = 0, Maximum = 1, Value = Element.Progress };
+ progressBar.ValueChanged += ProgressBarOnValueChanged;
+
+ SetNativeControl(progressBar);
+ }
+
+ protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ base.OnElementPropertyChanged(sender, e);
+
+ switch (e.PropertyName)
+ {
+ case "Progress":
+ Control.Value = Element.Progress;
+ break;
+ }
+ }
+
+ void ProgressBarOnValueChanged(object sender, RoutedPropertyChangedEventArgs<double> routedPropertyChangedEventArgs)
+ {
+ Element?.InvalidateMeasure(InvalidationTrigger.MeasureChanged);
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WP8/Properties/AssemblyInfo.cs b/Xamarin.Forms.Platform.WP8/Properties/AssemblyInfo.cs
new file mode 100644
index 00000000..f1be5df1
--- /dev/null
+++ b/Xamarin.Forms.Platform.WP8/Properties/AssemblyInfo.cs
@@ -0,0 +1,78 @@
+using System.Reflection;
+using System.Resources;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using Xamarin.Forms;
+using Xamarin.Forms.Platform.WinPhone;
+using TableView = Xamarin.Forms.TableView;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+
+[assembly: AssemblyTitle("Xamarin.Forms.Platform.WP8")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+
+[assembly: Guid("50e5d817-4ac1-46c0-8e20-260fb6ad16af")]
+[assembly: NeutralResourcesLanguage("en-US")]
+
+// View subclasses
+
+[assembly: ExportRenderer(typeof(BoxView), typeof(BoxViewRenderer))]
+[assembly: ExportRenderer(typeof(Entry), typeof(EntryRenderer))]
+[assembly: ExportRenderer(typeof(Editor), typeof(EditorRenderer))]
+[assembly: ExportRenderer(typeof(Label), typeof(LabelRenderer))]
+[assembly: ExportRenderer(typeof(Image), typeof(ImageRenderer))]
+[assembly: ExportRenderer(typeof(Button), typeof(ButtonRenderer))]
+[assembly: ExportRenderer(typeof(Slider), typeof(SliderRenderer))]
+[assembly: ExportRenderer(typeof(WebView), typeof(WebViewRenderer))]
+[assembly: ExportRenderer(typeof(SearchBar), typeof(SearchBarRenderer))]
+[assembly: ExportRenderer(typeof(Switch), typeof(SwitchRenderer))]
+[assembly: ExportRenderer(typeof(DatePicker), typeof(DatePickerRenderer))]
+[assembly: ExportRenderer(typeof(TimePicker), typeof(TimePickerRenderer))]
+[assembly: ExportRenderer(typeof(Picker), typeof(PickerRenderer))]
+[assembly: ExportRenderer(typeof(Stepper), typeof(StepperRenderer))]
+[assembly: ExportRenderer(typeof(ProgressBar), typeof(ProgressBarRenderer))]
+[assembly: ExportRenderer(typeof(ScrollView), typeof(ScrollViewRenderer))]
+[assembly: ExportRenderer(typeof(ActivityIndicator), typeof(ActivityIndicatorRenderer))]
+[assembly: ExportRenderer(typeof(Frame), typeof(FrameRenderer))]
+[assembly: ExportRenderer(typeof(NavigationMenu), typeof(NavigationMenuRenderer))]
+[assembly: ExportRenderer(typeof(ListView), typeof(ListViewRenderer))]
+[assembly: ExportRenderer(typeof(CarouselView), typeof(CarouselViewRenderer))]
+[assembly: ExportRenderer(typeof(TableView), typeof(TableViewRenderer))]
+[assembly: ExportRenderer(typeof(NativeViewWrapper), typeof(NativeViewWrapperRenderer))]
+
+// Form and subclasses
+
+[assembly: ExportRenderer(typeof(TabbedPage), typeof(TabbedPageRenderer))]
+[assembly: ExportRenderer(typeof(NavigationPage), typeof(NavigationPageRenderer))]
+[assembly: ExportRenderer(typeof(CarouselPage), typeof(CarouselPageRenderer))]
+[assembly: ExportRenderer(typeof(Page), typeof(PageRenderer))]
+[assembly: ExportRenderer(typeof(MasterDetailPage), typeof(MasterDetailRenderer))]
+
+//ImageSources
+
+[assembly: ExportImageSourceHandler(typeof(FileImageSource), typeof(FileImageSourceHandler))]
+[assembly: ExportImageSourceHandler(typeof(StreamImageSource), typeof(StreamImagesourceHandler))]
+[assembly: ExportImageSourceHandler(typeof(UriImageSource), typeof(ImageLoaderSourceHandler))]
+
+// 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))]
+[assembly: Dependency(typeof(Deserializer))]
+[assembly: Dependency(typeof(ResourcesProvider))]
+[assembly: InternalsVisibleTo("Xamarin.Forms.Core.WP8.UnitTests")] \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WP8/RendererFactory.cs b/Xamarin.Forms.Platform.WP8/RendererFactory.cs
new file mode 100644
index 00000000..33896df3
--- /dev/null
+++ b/Xamarin.Forms.Platform.WP8/RendererFactory.cs
@@ -0,0 +1,13 @@
+using System;
+
+namespace Xamarin.Forms.Platform.WinPhone
+{
+ public static class RendererFactory
+ {
+ [Obsolete("Use Platform.CreateRenderer")]
+ public static IVisualElementRenderer GetRenderer(VisualElement view)
+ {
+ return Platform.CreateRenderer(view);
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WP8/ResourcesProvider.cs b/Xamarin.Forms.Platform.WP8/ResourcesProvider.cs
new file mode 100644
index 00000000..3eaf2a1f
--- /dev/null
+++ b/Xamarin.Forms.Platform.WP8/ResourcesProvider.cs
@@ -0,0 +1,59 @@
+using System.Windows.Controls;
+
+namespace Xamarin.Forms.Platform.WinPhone
+{
+ internal class ResourcesProvider : ISystemResourcesProvider
+ {
+ ResourceDictionary _dictionary;
+
+ public IResourceDictionary GetSystemResources()
+ {
+ _dictionary = new ResourceDictionary();
+
+ UpdateStyles();
+
+ return _dictionary;
+ }
+
+ Style GetListItemDetailTextStyle()
+ {
+ var result = new Style(typeof(Label));
+
+ result.Setters.Add(new Setter { Property = Label.FontSizeProperty, Value = 32 });
+
+ return result;
+ }
+
+ Style GetListItemTextStyle()
+ {
+ var result = new Style(typeof(Label));
+
+ result.Setters.Add(new Setter { Property = Label.FontSizeProperty, Value = 48 });
+
+ return result;
+ }
+
+ Style GetStyle(System.Windows.Style style, TextBlock hackbox)
+ {
+ hackbox.Style = style;
+
+ var result = new Style(typeof(Label));
+ result.Setters.Add(new Setter { Property = Label.FontFamilyProperty, Value = hackbox.FontFamily });
+
+ result.Setters.Add(new Setter { Property = Label.FontSizeProperty, Value = hackbox.FontSize });
+
+ return result;
+ }
+
+ void UpdateStyles()
+ {
+ var textBlock = new TextBlock();
+ _dictionary[Device.Styles.TitleStyleKey] = GetStyle((System.Windows.Style)System.Windows.Application.Current.Resources["PhoneTextTitle1Style"], textBlock);
+ _dictionary[Device.Styles.SubtitleStyleKey] = GetStyle((System.Windows.Style)System.Windows.Application.Current.Resources["PhoneTextTitle2Style"], textBlock);
+ _dictionary[Device.Styles.BodyStyleKey] = GetStyle((System.Windows.Style)System.Windows.Application.Current.Resources["PhoneTextNormalStyle"], textBlock);
+ _dictionary[Device.Styles.CaptionStyleKey] = GetStyle((System.Windows.Style)System.Windows.Application.Current.Resources["PhoneTextSmallStyle"], textBlock);
+ _dictionary[Device.Styles.ListItemTextStyleKey] = GetListItemTextStyle();
+ _dictionary[Device.Styles.ListItemDetailTextStyleKey] = GetListItemDetailTextStyle();
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WP8/ScrollViewRenderer.cs b/Xamarin.Forms.Platform.WP8/ScrollViewRenderer.cs
new file mode 100644
index 00000000..6e19d8c4
--- /dev/null
+++ b/Xamarin.Forms.Platform.WP8/ScrollViewRenderer.cs
@@ -0,0 +1,194 @@
+using System;
+using System.ComponentModel;
+using System.Windows;
+using System.Windows.Controls;
+
+namespace Xamarin.Forms.Platform.WinPhone
+{
+ public class ScrollViewRenderer : ViewRenderer<ScrollView, ScrollViewer>
+ {
+ Animatable _animatable;
+
+ 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 System.Windows.Size ArrangeOverride(System.Windows.Size finalSize)
+ {
+ if (Element == null)
+ return finalSize;
+
+ Element.IsInNativeLayout = true;
+
+ if (Control != null)
+ {
+ Control.Measure(finalSize);
+ Control.Arrange(new Rect(0, 0, finalSize.Width, finalSize.Height));
+ }
+
+ Element.IsInNativeLayout = false;
+
+ return finalSize;
+ }
+
+ protected override System.Windows.Size MeasureOverride(System.Windows.Size availableSize)
+ {
+ if (Element == null)
+ return new System.Windows.Size(0, 0);
+
+ double width = Math.Max(0, Element.Width);
+ double height = Math.Max(0, Element.Height);
+ return new System.Windows.Size(width, height);
+ }
+
+ 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 { ManipulationMode = ManipulationMode.Control });
+ Control.LayoutUpdated += (sender, args) => { UpdateScrollPosition(); };
+ }
+ ((IScrollViewController)e.NewElement).ScrollToRequested += OnScrollToRequested;
+ }
+
+ SizeChanged += (sender, args) =>
+ {
+ Control.Width = ActualWidth;
+ Control.Height = ActualHeight;
+ };
+
+ 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();
+ }
+
+ static double GetDistance(double start, double position, double v)
+ {
+ return start + (position - start) * v;
+ }
+
+ void LoadContent()
+ {
+ var lastContent = Control.Content as FrameworkElement;
+ if (lastContent != null)
+ lastContent.Margin = new System.Windows.Thickness(); //undo any damage we may have done to this renderer
+
+ View view = Element.Content;
+
+ if (view != null)
+ Platform.SetRenderer(view, Platform.CreateRenderer(view));
+
+ Control.Content = view != null ? Platform.GetRenderer(view) : null;
+
+ UpdateMargins();
+ }
+
+ void OnScrollToRequested(object sender, ScrollToRequestedEventArgs e)
+ {
+ if (_animatable == null && e.ShouldAnimate)
+ _animatable = new Animatable();
+
+ ScrollToPosition position = e.Position;
+ double x = e.ScrollX;
+ double y = e.ScrollY;
+
+ if (e.Mode == ScrollToMode.Element)
+ {
+ Point itemPosition = Controller.GetScrollPositionForElement(e.Element as VisualElement, e.Position);
+ x = itemPosition.X;
+ y = itemPosition.Y;
+ }
+
+ if (Control.VerticalOffset == y && Control.HorizontalOffset == x)
+ return;
+
+ if (e.ShouldAnimate)
+ {
+ var animation = new Animation(v => { UpdateScrollOffset(GetDistance(Control.ViewportWidth, x, v), GetDistance(Control.ViewportHeight, y, v)); });
+
+ animation.Commit(_animatable, "ScrollTo", length: 500, easing: Easing.CubicInOut, finished: (v, d) =>
+ {
+ UpdateScrollOffset(x, y);
+ Controller.SendScrollFinished();
+ });
+ }
+ else
+ {
+ UpdateScrollOffset(x, y);
+ Controller.SendScrollFinished();
+ }
+ }
+
+ void UpdateMargins()
+ {
+ var element = Control.Content as FrameworkElement;
+ if (element == null)
+ return;
+
+ if (Element.Orientation == ScrollOrientation.Horizontal)
+ {
+ // need to add left/right margins
+ element.Margin = new System.Windows.Thickness(Element.Padding.Left, 0, Element.Padding.Right, 0);
+ }
+ else
+ {
+ // need to add top/bottom margins
+ element.Margin = new System.Windows.Thickness(0, Element.Padding.Top, 0, Element.Padding.Bottom);
+ }
+ }
+
+ void UpdateOrientation()
+ {
+ if (Element.Orientation == ScrollOrientation.Horizontal)
+ Control.HorizontalScrollBarVisibility = ScrollBarVisibility.Auto;
+ else
+ Control.HorizontalScrollBarVisibility = ScrollBarVisibility.Disabled;
+ }
+
+ void UpdateScrollOffset(double x, double y)
+ {
+ if (Element.Orientation == ScrollOrientation.Horizontal)
+ Control.ScrollToHorizontalOffset(x);
+ else
+ Control.ScrollToVerticalOffset(y);
+ }
+
+ void UpdateScrollPosition()
+ {
+ if (Element != null)
+ Controller.SetScrolledPosition(Control.HorizontalOffset, Control.VerticalOffset);
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WP8/SearchBarRenderer.cs b/Xamarin.Forms.Platform.WP8/SearchBarRenderer.cs
new file mode 100644
index 00000000..b27851a8
--- /dev/null
+++ b/Xamarin.Forms.Platform.WP8/SearchBarRenderer.cs
@@ -0,0 +1,159 @@
+using System.ComponentModel;
+using System.Windows.Input;
+using System.Windows.Media;
+
+namespace Xamarin.Forms.Platform.WinPhone
+{
+ public class SearchBarRenderer : ViewRenderer<SearchBar, FormsPhoneTextBox>
+ {
+ const string DefaultPlaceholder = "Search";
+ Brush _defaultPlaceholderColorBrush;
+
+ Brush _defaultTextColorBrush;
+
+ bool _fontApplied;
+
+ protected override void OnElementChanged(ElementChangedEventArgs<SearchBar> e)
+ {
+ base.OnElementChanged(e);
+
+ var scope = new InputScope();
+ var name = new InputScopeName();
+ name.NameValue = InputScopeNameValue.Search;
+ scope.Names.Add(name);
+
+ var phoneTextBox = new FormsPhoneTextBox { InputScope = scope };
+
+ phoneTextBox.KeyUp += PhoneTextBoxOnKeyUp;
+
+ phoneTextBox.TextChanged += PhoneTextBoxOnTextChanged;
+
+ SetNativeControl(phoneTextBox);
+
+ UpdateText();
+ UpdatePlaceholder();
+ UpdateAlignment();
+ UpdateFont();
+ UpdatePlaceholderColor();
+ UpdateTextColor();
+ }
+
+ protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ base.OnElementPropertyChanged(sender, e);
+
+ if (e.PropertyName == SearchBar.TextProperty.PropertyName)
+ UpdateText();
+ else if (e.PropertyName == SearchBar.PlaceholderProperty.PropertyName)
+ UpdatePlaceholder();
+ else if (e.PropertyName == SearchBar.FontAttributesProperty.PropertyName)
+ UpdateFont();
+ else if (e.PropertyName == SearchBar.FontFamilyProperty.PropertyName)
+ UpdateFont();
+ else if (e.PropertyName == SearchBar.FontSizeProperty.PropertyName)
+ UpdateFont();
+ else if (e.PropertyName == SearchBar.HorizontalTextAlignmentProperty.PropertyName)
+ UpdateAlignment();
+ else if (e.PropertyName == SearchBar.PlaceholderColorProperty.PropertyName)
+ UpdatePlaceholderColor();
+ else if (e.PropertyName == SearchBar.TextColorProperty.PropertyName)
+ UpdateTextColor();
+ }
+
+ protected override void UpdateBackgroundColor()
+ {
+ Control.Background = Element.BackgroundColor == Color.Default ? (Brush)System.Windows.Application.Current.Resources["PhoneTextBoxBrush"] : Element.BackgroundColor.ToBrush();
+ }
+
+ void PhoneTextBoxOnKeyUp(object sender, KeyEventArgs keyEventArgs)
+ {
+ if (keyEventArgs.Key == Key.Enter)
+ Element.OnSearchButtonPressed();
+ }
+
+ void PhoneTextBoxOnTextChanged(object sender, System.Windows.Controls.TextChangedEventArgs textChangedEventArgs)
+ {
+ ((IElementController)Element).SetValueFromRenderer(SearchBar.TextProperty, Control.Text);
+ }
+
+ void UpdateAlignment()
+ {
+ Control.TextAlignment = Element.HorizontalTextAlignment.ToNativeTextAlignment();
+ }
+
+ void UpdateFont()
+ {
+ if (Control == null)
+ return;
+
+ SearchBar searchbar = Element;
+
+ if (searchbar == null)
+ return;
+
+ bool searchbarIsDefault = searchbar.FontFamily == null && searchbar.FontSize == Device.GetNamedSize(NamedSize.Default, typeof(SearchBar), true) && searchbar.FontAttributes == FontAttributes.None;
+
+ if (searchbarIsDefault && !_fontApplied)
+ return;
+
+ if (searchbarIsDefault)
+ {
+ Control.ClearValue(System.Windows.Controls.Control.FontStyleProperty);
+ Control.ClearValue(System.Windows.Controls.Control.FontSizeProperty);
+ Control.ClearValue(System.Windows.Controls.Control.FontFamilyProperty);
+ Control.ClearValue(System.Windows.Controls.Control.FontWeightProperty);
+ Control.ClearValue(System.Windows.Controls.Control.FontStretchProperty);
+ }
+ else
+ Control.ApplyFont(searchbar);
+
+ _fontApplied = true;
+ }
+
+ void UpdatePlaceholder()
+ {
+ Control.Hint = Element.Placeholder ?? DefaultPlaceholder;
+ }
+
+ void UpdatePlaceholderColor()
+ {
+ Color placeholderColor = Element.PlaceholderColor;
+
+ if (placeholderColor.IsDefault)
+ {
+ if (_defaultPlaceholderColorBrush == null)
+ return;
+
+ Control.PlaceholderForegroundBrush = _defaultPlaceholderColorBrush;
+ }
+
+ if (_defaultPlaceholderColorBrush == null)
+ _defaultPlaceholderColorBrush = Control.PlaceholderForegroundBrush;
+
+ Control.PlaceholderForegroundBrush = placeholderColor.ToBrush();
+ }
+
+ void UpdateText()
+ {
+ Control.Text = Element.Text ?? "";
+ }
+
+ void UpdateTextColor()
+ {
+ Color textColor = Element.TextColor;
+
+ if (textColor.IsDefault)
+ {
+ if (_defaultTextColorBrush == null)
+ return;
+
+ Control.Foreground = _defaultTextColorBrush;
+ }
+
+ if (_defaultTextColorBrush == null)
+ _defaultTextColorBrush = Control.Foreground;
+
+ Control.Foreground = textColor.ToBrush();
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WP8/ServiceReferences.ClientConfig b/Xamarin.Forms.Platform.WP8/ServiceReferences.ClientConfig
new file mode 100644
index 00000000..26e884eb
--- /dev/null
+++ b/Xamarin.Forms.Platform.WP8/ServiceReferences.ClientConfig
@@ -0,0 +1,26 @@
+<configuration>
+ <system.serviceModel>
+ <bindings>
+ <basicHttpBinding>
+ <binding name="BasicHttpBinding_IGeocodeService" maxBufferSize="2147483647"
+ maxReceivedMessageSize="2147483647">
+ <security mode="None" />
+ </binding>
+ </basicHttpBinding>
+ <customBinding>
+ <binding name="CustomBinding_IGeocodeService">
+ <binaryMessageEncoding />
+ <httpTransport maxReceivedMessageSize="2147483647" maxBufferSize="2147483647" />
+ </binding>
+ </customBinding>
+ </bindings>
+ <client>
+ <endpoint address="http://dev.virtualearth.net/webservices/v1/geocodeservice/GeocodeService.svc"
+ binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_IGeocodeService"
+ contract="GeoService.IGeocodeService" name="BasicHttpBinding_IGeocodeService" />
+ <endpoint address="http://dev.virtualearth.net/webservices/v1/geocodeservice/GeocodeService.svc/binaryHttp"
+ binding="customBinding" bindingConfiguration="CustomBinding_IGeocodeService"
+ contract="GeoService.IGeocodeService" name="CustomBinding_IGeocodeService" />
+ </client>
+ </system.serviceModel>
+</configuration>
diff --git a/Xamarin.Forms.Platform.WP8/SliderRenderer.cs b/Xamarin.Forms.Platform.WP8/SliderRenderer.cs
new file mode 100644
index 00000000..40cd767d
--- /dev/null
+++ b/Xamarin.Forms.Platform.WP8/SliderRenderer.cs
@@ -0,0 +1,43 @@
+using System.ComponentModel;
+using System.Windows;
+
+namespace Xamarin.Forms.Platform.WinPhone
+{
+ public class SliderRenderer : ViewRenderer<Slider, System.Windows.Controls.Slider>
+ {
+ protected override void OnElementChanged(ElementChangedEventArgs<Slider> e)
+ {
+ base.OnElementChanged(e);
+
+ var wSlider = new System.Windows.Controls.Slider { Minimum = Element.Minimum, Maximum = Element.Maximum, Value = Element.Value };
+
+ SetNativeControl(wSlider);
+
+ wSlider.ValueChanged += HandleValueChanged;
+ }
+
+ protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ base.OnElementPropertyChanged(sender, e);
+
+ switch (e.PropertyName)
+ {
+ case "Minimum":
+ Control.Minimum = Element.Minimum;
+ break;
+ case "Maximum":
+ Control.Maximum = Element.Maximum;
+ break;
+ case "Value":
+ if (Control.Value != Element.Value)
+ Control.Value = Element.Value;
+ break;
+ }
+ }
+
+ void HandleValueChanged(object sender, RoutedPropertyChangedEventArgs<double> routedPropertyChangedEventArgs)
+ {
+ ((IElementController)Element).SetValueFromRenderer(Slider.ValueProperty, Control.Value);
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WP8/StepperRenderer.cs b/Xamarin.Forms.Platform.WP8/StepperRenderer.cs
new file mode 100644
index 00000000..b459305f
--- /dev/null
+++ b/Xamarin.Forms.Platform.WP8/StepperRenderer.cs
@@ -0,0 +1,68 @@
+using System;
+using System.ComponentModel;
+using System.Windows;
+using System.Windows.Controls;
+using WButton = System.Windows.Controls.Button;
+
+namespace Xamarin.Forms.Platform.WinPhone
+{
+ public class StepperRenderer : ViewRenderer<Stepper, Border>
+ {
+ readonly StackPanel _panel = new StackPanel();
+ WButton _downButton;
+ WButton _upButton;
+
+ protected override void OnElementChanged(ElementChangedEventArgs<Stepper> e)
+ {
+ base.OnElementChanged(e);
+
+ var border = new Border();
+ border.Child = _panel;
+ _panel.HorizontalAlignment = HorizontalAlignment.Right;
+ _panel.Orientation = Orientation.Horizontal;
+
+ _upButton = new WButton { Content = "+", Width = 100 };
+ _downButton = new WButton { Content = "-", Width = 100 };
+
+ _panel.Children.Add(_downButton);
+ _panel.Children.Add(_upButton);
+
+ SetNativeControl(border);
+
+ _upButton.Click += UpButtonOnClick;
+ _downButton.Click += DownButtonOnClick;
+
+ UpdateButtons();
+ }
+
+ protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ base.OnElementPropertyChanged(sender, e);
+
+ switch (e.PropertyName)
+ {
+ case "Minimum":
+ case "Maximum":
+ case "Value":
+ UpdateButtons();
+ break;
+ }
+ }
+
+ void DownButtonOnClick(object sender, RoutedEventArgs routedEventArgs)
+ {
+ ((IElementController)Element).SetValueFromRenderer(Stepper.ValueProperty, Math.Max(Element.Minimum, Element.Value - Element.Increment));
+ }
+
+ void UpButtonOnClick(object sender, RoutedEventArgs routedEventArgs)
+ {
+ ((IElementController)Element).SetValueFromRenderer(Stepper.ValueProperty, Math.Min(Element.Maximum, Element.Value + Element.Increment));
+ }
+
+ void UpdateButtons()
+ {
+ _upButton.IsEnabled = Element.Value < Element.Maximum;
+ _downButton.IsEnabled = Element.Value > Element.Minimum;
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WP8/SwitchRenderer.cs b/Xamarin.Forms.Platform.WP8/SwitchRenderer.cs
new file mode 100644
index 00000000..e64fe227
--- /dev/null
+++ b/Xamarin.Forms.Platform.WP8/SwitchRenderer.cs
@@ -0,0 +1,50 @@
+using System.ComponentModel;
+using System.Windows;
+using System.Windows.Controls;
+using Microsoft.Phone.Controls.Primitives;
+
+namespace Xamarin.Forms.Platform.WinPhone
+{
+ public class SwitchRenderer : ViewRenderer<Switch, Border>
+ {
+ readonly ToggleSwitchButton _toggleSwitch = new ToggleSwitchButton();
+
+ protected override void OnElementChanged(ElementChangedEventArgs<Switch> e)
+ {
+ base.OnElementChanged(e);
+
+ var container = new Border { Child = _toggleSwitch };
+ _toggleSwitch.IsChecked = Element.IsToggled;
+ _toggleSwitch.Checked += (sender, args) => ((IElementController)Element).SetValueFromRenderer(Switch.IsToggledProperty, true);
+ _toggleSwitch.Unchecked += (sender, args) => ((IElementController)Element).SetValueFromRenderer(Switch.IsToggledProperty, false);
+ _toggleSwitch.HorizontalAlignment = HorizontalAlignment.Right;
+
+ SetNativeControl(container);
+ }
+
+ protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ base.OnElementPropertyChanged(sender, e);
+
+ if (e.PropertyName == Switch.IsToggledProperty.PropertyName)
+ {
+ if (_toggleSwitch.IsChecked != Element.IsToggled)
+ _toggleSwitch.IsChecked = Element.IsToggled;
+ }
+ else if (e.PropertyName == VisualElement.IsEnabledProperty.PropertyName)
+ UpdateSwitchIsEnabled();
+ }
+
+ protected override void UpdateNativeWidget()
+ {
+ base.UpdateNativeWidget();
+
+ UpdateSwitchIsEnabled();
+ }
+
+ void UpdateSwitchIsEnabled()
+ {
+ _toggleSwitch.IsEnabled = Element.IsEnabled;
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WP8/TabbedPageRenderer.cs b/Xamarin.Forms.Platform.WP8/TabbedPageRenderer.cs
new file mode 100644
index 00000000..9192fc6f
--- /dev/null
+++ b/Xamarin.Forms.Platform.WP8/TabbedPageRenderer.cs
@@ -0,0 +1,105 @@
+using System;
+using System.ComponentModel;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Media;
+using Microsoft.Phone.Controls;
+
+namespace Xamarin.Forms.Platform.WinPhone
+{
+ public class TabbedPagePresenter : System.Windows.Controls.ContentPresenter
+ {
+ public override void OnApplyTemplate()
+ {
+ base.OnApplyTemplate();
+
+ DependencyObject parent = VisualTreeHelper.GetParent(this);
+ while (parent != null && !(parent is PivotItem))
+ parent = VisualTreeHelper.GetParent(parent);
+
+ var pivotItem = parent as PivotItem;
+ if (pivotItem == null)
+ throw new Exception("No parent PivotItem found for tab");
+
+ pivotItem.SizeChanged += (s, e) =>
+ {
+ if (pivotItem.ActualWidth > 0 && pivotItem.ActualHeight > 0)
+ {
+ var tab = (Page)DataContext;
+ ((TabbedPage)tab.RealParent).ContainerArea = new Rectangle(0, 0, pivotItem.ActualWidth, pivotItem.ActualHeight);
+ }
+ };
+ }
+ }
+
+ public class TabbedPageRenderer : Pivot, IVisualElementRenderer
+ {
+ TabbedPage _page;
+ BackgroundTracker<Control> _tracker;
+
+ public TabbedPageRenderer()
+ {
+ SetBinding(TitleProperty, new System.Windows.Data.Binding("Title"));
+ SetBinding(ItemsSourceProperty, new System.Windows.Data.Binding("Children"));
+ HeaderTemplate = (System.Windows.DataTemplate)System.Windows.Application.Current.Resources["TabbedPageHeader"];
+ ItemTemplate = (System.Windows.DataTemplate)System.Windows.Application.Current.Resources["TabbedPage"];
+
+ SelectionChanged += OnSelectionChanged;
+ }
+
+ public UIElement ContainerElement
+ {
+ get { return this; }
+ }
+
+ public VisualElement Element
+ {
+ get { return _page; }
+ }
+
+ public event EventHandler<VisualElementChangedEventArgs> ElementChanged;
+
+ public SizeRequest GetDesiredSize(double widthConstraint, double heightConstraint)
+ {
+ return new SizeRequest(new Size(widthConstraint, heightConstraint));
+ }
+
+ public void SetElement(VisualElement element)
+ {
+ TabbedPage oldElement = _page;
+ _page = (TabbedPage)element;
+ _tracker = new BackgroundTracker<Control>(BackgroundProperty) { Model = _page, Element = this };
+
+ DataContext = element;
+
+ _page.PropertyChanged += OnPropertyChanged;
+
+ Loaded += (sender, args) => _page.SendAppearing();
+ Unloaded += (sender, args) => _page.SendDisappearing();
+
+ OnElementChanged(new VisualElementChangedEventArgs(_page, element));
+ }
+
+ protected virtual void OnElementChanged(VisualElementChangedEventArgs e)
+ {
+ EventHandler<VisualElementChangedEventArgs> changed = ElementChanged;
+ if (changed != null)
+ changed(this, e);
+ }
+
+ void OnPropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ if (e.PropertyName == "CurrentPage")
+ {
+ Page current = _page.CurrentPage;
+ if (current != null)
+ SelectedItem = current;
+ }
+ }
+
+ void OnSelectionChanged(object sender, SelectionChangedEventArgs e)
+ {
+ _page.CurrentPage = (Page)SelectedItem;
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WP8/TableView.xaml b/Xamarin.Forms.Platform.WP8/TableView.xaml
new file mode 100644
index 00000000..89c9cddb
--- /dev/null
+++ b/Xamarin.Forms.Platform.WP8/TableView.xaml
@@ -0,0 +1,31 @@
+<Grid x:Class="Xamarin.Forms.Platform.WinPhone.TableView"
+ 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"
+ >
+
+ <Grid.Resources>
+ <ResourceDictionary>
+ <ResourceDictionary.MergedDictionaries>
+ <ResourceDictionary Source="WPResources.xaml" />
+ </ResourceDictionary.MergedDictionaries>
+ </ResourceDictionary>
+ </Grid.Resources>
+
+ <Grid.RowDefinitions>
+ <RowDefinition Height="Auto" />
+ <RowDefinition />
+ </Grid.RowDefinitions>
+
+ <TextBlock Grid.Row="0" Text="{Binding Title,Converter={StaticResource UpperConverter}}" Style="{StaticResource PhoneTextSmallTitleStyle}" />
+ <ListBox Grid.Row="1" ItemsSource="{Binding}" ItemTemplate="{StaticResource TableGroup}">
+ <ListBox.ItemContainerStyle>
+ <Style TargetType="ListBoxItem">
+ <Setter Property="HorizontalContentAlignment" Value="Stretch" />
+ </Style>
+ </ListBox.ItemContainerStyle>
+ </ListBox>
+</Grid>
+
diff --git a/Xamarin.Forms.Platform.WP8/TableView.xaml.cs b/Xamarin.Forms.Platform.WP8/TableView.xaml.cs
new file mode 100644
index 00000000..dc3d1dcc
--- /dev/null
+++ b/Xamarin.Forms.Platform.WP8/TableView.xaml.cs
@@ -0,0 +1,10 @@
+namespace Xamarin.Forms.Platform.WinPhone
+{
+ public partial class TableView : System.Windows.Controls.Grid
+ {
+ public TableView()
+ {
+ InitializeComponent();
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WP8/TableViewRenderer.cs b/Xamarin.Forms.Platform.WP8/TableViewRenderer.cs
new file mode 100644
index 00000000..84f205eb
--- /dev/null
+++ b/Xamarin.Forms.Platform.WP8/TableViewRenderer.cs
@@ -0,0 +1,91 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Windows;
+using System.Windows.Input;
+using System.Windows.Media;
+using Microsoft.Phone.Shell;
+
+namespace Xamarin.Forms.Platform.WinPhone
+{
+ public class TableViewRenderer : ViewRenderer<Xamarin.Forms.TableView, TableView>
+ {
+ TableView _view;
+
+ 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<Xamarin.Forms.TableView> e)
+ {
+ base.OnElementChanged(e);
+
+ Element.ModelChanged += OnModelChanged;
+
+ _view = new TableView { DataContext = Element.Root };
+ _view.Tap += OnTapTable;
+ _view.Hold += OnLongPressTable;
+ SetNativeControl(_view);
+ }
+
+ bool FindIndices(GestureEventArgs e, out int sectionIndex, out int cellIndex)
+ {
+ sectionIndex = 0;
+ cellIndex = 0;
+
+ TableSection section = null;
+ Cell cell = null;
+
+ System.Windows.Point pos = e.GetPosition(System.Windows.Application.Current.RootVisual);
+ if (Device.Info.CurrentOrientation.IsLandscape())
+ {
+ double x = pos.Y;
+ double y = System.Windows.Application.Current.RootVisual.RenderSize.Width - pos.X + (SystemTray.IsVisible ? 72 : 0);
+ pos = new System.Windows.Point(x, y);
+ }
+ IEnumerable<UIElement> elements = VisualTreeHelper.FindElementsInHostCoordinates(pos, System.Windows.Application.Current.RootVisual);
+ foreach (FrameworkElement element in elements.OfType<FrameworkElement>())
+ {
+ if (cell == null)
+ cell = element.DataContext as Cell;
+ else if (section == null)
+ section = element.DataContext as TableSection;
+ else
+ break;
+ }
+
+ if (cell == null || section == null)
+ return false;
+
+ sectionIndex = Element.Root.IndexOf(section);
+ cellIndex = section.IndexOf(cell);
+ return true;
+ }
+
+ void OnLongPressTable(object sender, GestureEventArgs e)
+ {
+ int sectionIndex, cellIndex;
+ if (!FindIndices(e, out sectionIndex, out cellIndex))
+ return;
+
+ Element.Model.RowLongPressed(sectionIndex, cellIndex);
+ }
+
+ void OnModelChanged(object sender, EventArgs eventArgs)
+ {
+ _view.DataContext = Element.Root;
+ }
+
+ void OnTapTable(object sender, GestureEventArgs e)
+ {
+ int sectionIndex, cellIndex;
+ if (!FindIndices(e, out sectionIndex, out cellIndex))
+ return;
+
+ Element.Model.RowSelected(sectionIndex, cellIndex);
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WP8/TextAlignmentToHorizontalAlignmentConverter.cs b/Xamarin.Forms.Platform.WP8/TextAlignmentToHorizontalAlignmentConverter.cs
new file mode 100644
index 00000000..7f8aeef6
--- /dev/null
+++ b/Xamarin.Forms.Platform.WP8/TextAlignmentToHorizontalAlignmentConverter.cs
@@ -0,0 +1,43 @@
+using System;
+using System.Globalization;
+using System.Windows;
+
+namespace Xamarin.Forms.Platform.WinPhone
+{
+ public sealed class TextAlignmentToHorizontalAlignmentConverter : System.Windows.Data.IValueConverter
+ {
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ var alignment = (System.Windows.TextAlignment)value;
+
+ switch (alignment)
+ {
+ case System.Windows.TextAlignment.Center:
+ return HorizontalAlignment.Center;
+ case System.Windows.TextAlignment.Left:
+ return HorizontalAlignment.Left;
+ case System.Windows.TextAlignment.Right:
+ return HorizontalAlignment.Right;
+ default:
+ return HorizontalAlignment.Left;
+ }
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ var alignment = (HorizontalAlignment)value;
+
+ switch (alignment)
+ {
+ case HorizontalAlignment.Left:
+ return System.Windows.TextAlignment.Left;
+ case HorizontalAlignment.Center:
+ return System.Windows.TextAlignment.Center;
+ case HorizontalAlignment.Right:
+ return System.Windows.TextAlignment.Right;
+ default:
+ return System.Windows.TextAlignment.Left;
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WP8/TextCellRenderer.cs b/Xamarin.Forms.Platform.WP8/TextCellRenderer.cs
new file mode 100644
index 00000000..b3c2453d
--- /dev/null
+++ b/Xamarin.Forms.Platform.WP8/TextCellRenderer.cs
@@ -0,0 +1,88 @@
+using System;
+using System.Windows.Input;
+using Microsoft.Phone.Controls;
+
+namespace Xamarin.Forms.Platform.WinPhone
+{
+ public class TextCellRenderer : ICellRenderer
+ {
+ public virtual System.Windows.DataTemplate GetTemplate(Cell cell)
+ {
+ if (cell.RealParent is ListView)
+ {
+ if (TemplatedItemsList<ItemsView<Cell>, Cell>.GetIsGroupHeader(cell))
+ return (System.Windows.DataTemplate)System.Windows.Application.Current.Resources["ListViewHeaderTextCell"];
+
+ return (System.Windows.DataTemplate)System.Windows.Application.Current.Resources["ListViewTextCell"];
+ }
+
+ return (System.Windows.DataTemplate)System.Windows.Application.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 EntryCellPhoneTextBox : PhoneTextBox
+ {
+ public event EventHandler KeyboardReturnPressed;
+
+ protected override void OnKeyUp(KeyEventArgs e)
+ {
+ if (e.Key == Key.Enter)
+ {
+ EventHandler handler = KeyboardReturnPressed;
+ if (handler != null)
+ handler(this, EventArgs.Empty);
+ }
+ base.OnKeyUp(e);
+ }
+ }
+
+ public class EntryCellRenderer : ICellRenderer
+ {
+ public virtual System.Windows.DataTemplate GetTemplate(Cell cell)
+ {
+ return (System.Windows.DataTemplate)System.Windows.Application.Current.Resources["EntryCell"];
+ }
+ }
+
+ public class ViewCellRenderer : ICellRenderer
+ {
+ public virtual System.Windows.DataTemplate GetTemplate(Cell cell)
+ {
+ return (System.Windows.DataTemplate)System.Windows.Application.Current.Resources["ViewCell"];
+ }
+ }
+
+ public class SwitchCellRenderer : ICellRenderer
+ {
+ public virtual System.Windows.DataTemplate GetTemplate(Cell cell)
+ {
+ return (System.Windows.DataTemplate)System.Windows.Application.Current.Resources["SwitchCell"];
+ }
+ }
+
+ public class ImageCellRenderer : ICellRenderer
+ {
+ public virtual System.Windows.DataTemplate GetTemplate(Cell cell)
+ {
+ if (cell.RealParent is ListView)
+ return (System.Windows.DataTemplate)System.Windows.Application.Current.Resources["ListImageCell"];
+ return (System.Windows.DataTemplate)System.Windows.Application.Current.Resources["ImageCell"];
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WP8/TimePickerRenderer.cs b/Xamarin.Forms.Platform.WP8/TimePickerRenderer.cs
new file mode 100644
index 00000000..70841d7a
--- /dev/null
+++ b/Xamarin.Forms.Platform.WP8/TimePickerRenderer.cs
@@ -0,0 +1,60 @@
+using System;
+using System.ComponentModel;
+using System.Reflection;
+using Microsoft.Phone.Controls;
+
+namespace Xamarin.Forms.Platform.WinPhone
+{
+ public class TimePickerRenderer : ViewRenderer<TimePicker, Microsoft.Phone.Controls.TimePicker>
+ {
+ protected override void OnElementChanged(ElementChangedEventArgs<TimePicker> e)
+ {
+ base.OnElementChanged(e);
+
+ var timePicker = new Microsoft.Phone.Controls.TimePicker { Value = DateTime.Today.Add(Element.Time) };
+ timePicker.ValueChanged += TimePickerOnValueChanged;
+
+ SetNativeControl(timePicker);
+ UpdateFormatString();
+ }
+
+ protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ base.OnElementPropertyChanged(sender, e);
+
+ if (e.PropertyName == "Time")
+ Control.Value = DateTime.Today.Add(Element.Time);
+ else if (e.PropertyName == TimePicker.FormatProperty.PropertyName)
+ UpdateFormatString();
+ }
+
+ internal override void OnModelFocusChangeRequested(object sender, VisualElement.FocusRequestArgs args)
+ {
+ Microsoft.Phone.Controls.TimePicker control = Control;
+ if (control == null)
+ return;
+
+ if (args.Focus)
+ {
+ typeof(DateTimePickerBase).InvokeMember("OpenPickerPage", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.InvokeMethod, Type.DefaultBinder, control, null);
+ args.Result = true;
+ }
+ else
+ {
+ UnfocusControl(control);
+ args.Result = true;
+ }
+ }
+
+ void TimePickerOnValueChanged(object sender, DateTimeValueChangedEventArgs dateTimeValueChangedEventArgs)
+ {
+ if (Control.Value != null)
+ ((IElementController)Element).SetValueFromRenderer(TimePicker.TimeProperty, Control.Value.Value - DateTime.Today);
+ }
+
+ void UpdateFormatString()
+ {
+ Control.ValueStringFormat = "{0:" + Element.Format + "}";
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WP8/Toolkit.Content/ApplicationBar.Cancel.png b/Xamarin.Forms.Platform.WP8/Toolkit.Content/ApplicationBar.Cancel.png
new file mode 100644
index 00000000..4dd724f0
--- /dev/null
+++ b/Xamarin.Forms.Platform.WP8/Toolkit.Content/ApplicationBar.Cancel.png
Binary files differ
diff --git a/Xamarin.Forms.Platform.WP8/Toolkit.Content/ApplicationBar.Check.png b/Xamarin.Forms.Platform.WP8/Toolkit.Content/ApplicationBar.Check.png
new file mode 100644
index 00000000..7a074666
--- /dev/null
+++ b/Xamarin.Forms.Platform.WP8/Toolkit.Content/ApplicationBar.Check.png
Binary files differ
diff --git a/Xamarin.Forms.Platform.WP8/Toolkit.Content/ApplicationBar.Delete.png b/Xamarin.Forms.Platform.WP8/Toolkit.Content/ApplicationBar.Delete.png
new file mode 100644
index 00000000..95bb16da
--- /dev/null
+++ b/Xamarin.Forms.Platform.WP8/Toolkit.Content/ApplicationBar.Delete.png
Binary files differ
diff --git a/Xamarin.Forms.Platform.WP8/Toolkit.Content/ApplicationBar.Select.png b/Xamarin.Forms.Platform.WP8/Toolkit.Content/ApplicationBar.Select.png
new file mode 100644
index 00000000..995deaaa
--- /dev/null
+++ b/Xamarin.Forms.Platform.WP8/Toolkit.Content/ApplicationBar.Select.png
Binary files differ
diff --git a/Xamarin.Forms.Platform.WP8/ViewExtensions.cs b/Xamarin.Forms.Platform.WP8/ViewExtensions.cs
new file mode 100644
index 00000000..1d46b3b2
--- /dev/null
+++ b/Xamarin.Forms.Platform.WP8/ViewExtensions.cs
@@ -0,0 +1,19 @@
+using System;
+
+namespace Xamarin.Forms.Platform.WinPhone
+{
+ public static class ViewExtensions
+ {
+ [Obsolete("Use Platform.GetRenderer")]
+ public static IVisualElementRenderer GetRenderer(this VisualElement self)
+ {
+ return Platform.GetRenderer(self);
+ }
+
+ [Obsolete("Use Platform.SetRenderer")]
+ public static void SetRenderer(this VisualElement self, IVisualElementRenderer renderer)
+ {
+ Platform.SetRenderer(self, renderer);
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WP8/ViewRenderer.cs b/Xamarin.Forms.Platform.WP8/ViewRenderer.cs
new file mode 100644
index 00000000..3f0d72c5
--- /dev/null
+++ b/Xamarin.Forms.Platform.WP8/ViewRenderer.cs
@@ -0,0 +1,47 @@
+using System.ComponentModel;
+using System.Windows;
+using System.Windows.Media;
+using WSize = System.Windows.Size;
+
+namespace Xamarin.Forms.Platform.WinPhone
+{
+ public class ViewRenderer<TElement, TNativeElement> : VisualElementRenderer<TElement, TNativeElement> where TElement : View where TNativeElement : FrameworkElement
+ {
+ }
+
+ public class ViewRenderer : ViewRenderer<View, FrameworkElement>
+ {
+ protected override void OnElementChanged(ElementChangedEventArgs<View> e)
+ {
+ base.OnElementChanged(e);
+ SizeChanged += (sender, args) => UpdateClipToBounds();
+
+ UpdateBackgroundColor();
+ }
+
+ protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ base.OnElementPropertyChanged(sender, e);
+
+ if (e.PropertyName == Layout.IsClippedToBoundsProperty.PropertyName)
+ UpdateClipToBounds();
+ }
+
+ protected override void UpdateNativeWidget()
+ {
+ base.UpdateNativeWidget();
+ UpdateClipToBounds();
+ }
+
+ void UpdateClipToBounds()
+ {
+ var layout = Element as Layout;
+ if (layout != null)
+ {
+ Clip = null;
+ if (layout.IsClippedToBounds)
+ Clip = new RectangleGeometry { Rect = new Rect(0, 0, ActualWidth, ActualHeight) };
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WP8/ViewToRendererConverter.cs b/Xamarin.Forms.Platform.WP8/ViewToRendererConverter.cs
new file mode 100644
index 00000000..49b232a1
--- /dev/null
+++ b/Xamarin.Forms.Platform.WP8/ViewToRendererConverter.cs
@@ -0,0 +1,77 @@
+using System;
+using System.Globalization;
+using System.Windows;
+using System.Windows.Controls;
+
+namespace Xamarin.Forms.Platform.WinPhone
+{
+ public class ViewToRendererConverter : System.Windows.Data.IValueConverter
+ {
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ var view = value as View;
+ if (view == null)
+ return null;
+
+ return new WrapperControl(view);
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ throw new NotSupportedException();
+ }
+
+ class WrapperControl : ContentControl
+ {
+ readonly View _view;
+
+ public WrapperControl(View view)
+ {
+ _view = view;
+ _view.MeasureInvalidated += (sender, args) => InvalidateMeasure();
+
+ IVisualElementRenderer visualElementRenderer = Platform.CreateRenderer(view);
+ Platform.SetRenderer(view, visualElementRenderer);
+ Content = visualElementRenderer.ContainerElement;
+
+ // make sure we re-measure once the template is applied
+ var frameworkElement = visualElementRenderer.ContainerElement as FrameworkElement;
+ if (frameworkElement != null)
+ {
+ frameworkElement.Loaded += (sender, args) =>
+ {
+ _view.InvalidateMeasure(InvalidationTrigger.MeasureChanged);
+ InvalidateMeasure();
+ };
+ }
+ }
+
+ protected override System.Windows.Size ArrangeOverride(System.Windows.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 System.Windows.Size MeasureOverride(System.Windows.Size availableSize)
+ {
+ var content = Content as FrameworkElement;
+ content?.Measure(availableSize);
+ Size request = _view.Measure(availableSize.Width, availableSize.Height, MeasureFlags.IncludeMargins).Request;
+
+ System.Windows.Size result;
+ if (_view.HorizontalOptions.Alignment == LayoutAlignment.Fill && !double.IsInfinity(availableSize.Width) && availableSize.Width != 0)
+ result = new System.Windows.Size(availableSize.Width, request.Height);
+ else
+ result = new System.Windows.Size(request.Width, request.Height);
+
+ _view.Layout(new Rectangle(0, 0, result.Width, result.Width));
+ return result;
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WP8/VisualElementPackager.cs b/Xamarin.Forms.Platform.WP8/VisualElementPackager.cs
new file mode 100644
index 00000000..65f589fc
--- /dev/null
+++ b/Xamarin.Forms.Platform.WP8/VisualElementPackager.cs
@@ -0,0 +1,88 @@
+using System;
+using System.Windows;
+using System.Windows.Controls;
+
+namespace Xamarin.Forms.Platform.WinPhone
+{
+ public class VisualElementPackager
+ {
+ readonly Panel _panel;
+ readonly IVisualElementRenderer _renderer;
+ bool _loaded;
+
+ public VisualElementPackager(IVisualElementRenderer renderer)
+ {
+ if (renderer == null)
+ throw new ArgumentNullException("renderer");
+
+ _panel = renderer.ContainerElement as Panel;
+ if (_panel == null)
+ throw new ArgumentException("Renderer's container element must be a Panel or Panel subclass");
+
+ _renderer = renderer;
+ }
+
+ public void Load()
+ {
+ if (_loaded)
+ return;
+
+ _loaded = true;
+ _renderer.Element.ChildAdded += HandleChildAdded;
+ _renderer.Element.ChildRemoved += HandleChildRemoved;
+ _renderer.Element.ChildrenReordered += HandleChildrenReordered;
+
+ foreach (Element child in _renderer.Element.LogicalChildren)
+ HandleChildAdded(_renderer.Element, new ElementEventArgs(child));
+ }
+
+ void EnsureZIndex()
+ {
+ for (var index = 0; index < _renderer.Element.LogicalChildren.Count; index++)
+ {
+ var child = (VisualElement)_renderer.Element.LogicalChildren[index];
+ IVisualElementRenderer r = Platform.GetRenderer(child);
+ if (r == null)
+ continue;
+ // Even though this attached property is defined on Canvas, it actually works on all Panels
+ // Why? Microsoft.
+ Canvas.SetZIndex(r.ContainerElement, index + 1);
+ }
+ }
+
+ void HandleChildAdded(object sender, ElementEventArgs e)
+ {
+ var view = e.Element as VisualElement;
+
+ if (view == null)
+ return;
+
+ IVisualElementRenderer renderer;
+ Platform.SetRenderer(view, renderer = Platform.CreateRenderer(view));
+
+ _panel.Children.Add(renderer.ContainerElement);
+
+ EnsureZIndex();
+ }
+
+ void HandleChildRemoved(object sender, ElementEventArgs e)
+ {
+ var view = e.Element as VisualElement;
+
+ if (view == null)
+ return;
+
+ var renderer = Platform.GetRenderer(view) as UIElement;
+
+ if (renderer != null)
+ _panel.Children.Remove(renderer);
+
+ EnsureZIndex();
+ }
+
+ void HandleChildrenReordered(object sender, EventArgs e)
+ {
+ EnsureZIndex();
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WP8/VisualElementRenderer.cs b/Xamarin.Forms.Platform.WP8/VisualElementRenderer.cs
new file mode 100644
index 00000000..9ec274c5
--- /dev/null
+++ b/Xamarin.Forms.Platform.WP8/VisualElementRenderer.cs
@@ -0,0 +1,326 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Media;
+
+namespace Xamarin.Forms.Platform.WinPhone
+{
+ public class VisualElementRenderer<TElement, TNativeElement> : Panel, IVisualElementRenderer, IEffectControlProvider where TElement : VisualElement where TNativeElement : FrameworkElement
+ {
+ readonly List<EventHandler<VisualElementChangedEventArgs>> _elementChangedHandlers = new List<EventHandler<VisualElementChangedEventArgs>>();
+
+ Brush _initialBrush;
+
+ VisualElementTracker _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 Tracker
+ {
+ get { return _tracker; }
+ set
+ {
+ if (_tracker == value)
+ return;
+
+ if (_tracker != null)
+ {
+ _tracker.Dispose();
+ _tracker.Updated -= HandleTrackerUpdated;
+ }
+
+ _tracker = value;
+
+ if (_tracker != null)
+ _tracker.Updated += HandleTrackerUpdated;
+ }
+ }
+
+ VisualElementPackager Packager { get; set; }
+
+ void IEffectControlProvider.RegisterEffect(Effect effect)
+ {
+ var platformEffect = effect as PlatformEffect;
+ if (platformEffect != null)
+ OnRegisterEffect(platformEffect);
+ }
+
+ public UIElement ContainerElement
+ {
+ get { return this; }
+ }
+
+ VisualElement IVisualElementRenderer.Element
+ {
+ get { return Element; }
+ }
+
+ event EventHandler<VisualElementChangedEventArgs> IVisualElementRenderer.ElementChanged
+ {
+ add { _elementChangedHandlers.Add(value); }
+ remove { _elementChangedHandlers.Remove(value); }
+ }
+
+ public virtual SizeRequest GetDesiredSize(double widthConstraint, double heightConstraint)
+ {
+ if (Children.Count == 0)
+ return new SizeRequest();
+
+ var constraint = new System.Windows.Size(widthConstraint, heightConstraint);
+ var child = (FrameworkElement)Children[0];
+
+ 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 -= OnModelFocusChangeRequested;
+ }
+
+ Element.PropertyChanged += OnElementPropertyChanged;
+ Element.FocusChangeRequested += OnModelFocusChangeRequested;
+
+ if (AutoPackage && Packager == null)
+ Packager = new VisualElementPackager(this);
+
+ if (AutoTrack && Tracker == null)
+ {
+ Tracker = new VisualElementTracker<TElement, FrameworkElement> { Model = Element, Element = this };
+ }
+
+ // 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 System.Windows.Size ArrangeOverride(System.Windows.Size finalSize)
+ {
+ if (Element == null || finalSize.Width * finalSize.Height == 0)
+ return finalSize;
+
+ Element.IsInNativeLayout = true;
+
+ if (Control != null)
+ {
+ Control.Measure(finalSize);
+ 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 = child.GetRenderer();
+ 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 override System.Windows.Size MeasureOverride(System.Windows.Size availableSize)
+ {
+ if (Element == null || availableSize.Width * availableSize.Height == 0)
+ return new System.Windows.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;
+
+ try
+ {
+ renderer.ContainerElement.Measure(availableSize);
+ }
+ catch (NullReferenceException)
+ {
+ if (!IsExpectedTabbedPageMeasurementException(renderer))
+ throw;
+ }
+ }
+
+ double width = Math.Max(0, Element.Width);
+ double height = Math.Max(0, Element.Height);
+ var result = new System.Windows.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 System.Windows.Size(w, h));
+ }
+
+ Element.IsInNativeLayout = false;
+
+ return result;
+ }
+
+ protected virtual void OnElementChanged(ElementChangedEventArgs<TElement> e)
+ {
+ var args = new VisualElementChangedEventArgs(e.OldElement, e.NewElement);
+ for (var i = 0; i < _elementChangedHandlers.Count; i++)
+ _elementChangedHandlers[i](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 OnGotFocus(object sender, RoutedEventArgs args)
+ {
+ ((IElementController)Element).SetValueFromRenderer(VisualElement.IsFocusedPropertyKey, true);
+ }
+
+ protected virtual void OnLostFocus(object sender, RoutedEventArgs args)
+ {
+ ((IElementController)Element).SetValueFromRenderer(VisualElement.IsFocusedPropertyKey, false);
+ }
+
+ protected virtual void OnRegisterEffect(PlatformEffect effect)
+ {
+ effect.Container = this;
+ effect.Control = Control;
+ }
+
+ protected void SetNativeControl(TNativeElement element)
+ {
+ Control = element;
+
+ Children.Add(element);
+ Element.IsNativeStateConsistent = false;
+ element.Loaded += (sender, args) => Element.IsNativeStateConsistent = true;
+
+ element.GotFocus += OnGotFocus;
+ element.LostFocus += OnLostFocus;
+
+ _tracker.Child = element;
+
+ UpdateBackgroundColor();
+ }
+
+ protected virtual void UpdateBackgroundColor()
+ {
+ var control = Control as Control;
+ if (_initialBrush == null)
+ _initialBrush = control == null ? Background : control.Background;
+ if (control != null)
+ control.Background = Element.BackgroundColor != Color.Default ? Element.BackgroundColor.ToBrush() : _initialBrush;
+ else
+ Background = Element.BackgroundColor != Color.Default ? Element.BackgroundColor.ToBrush() : _initialBrush;
+ }
+
+ protected virtual void UpdateNativeWidget()
+ {
+ UpdateEnabled();
+ }
+
+ internal virtual void OnModelFocusChangeRequested(object sender, VisualElement.FocusRequestArgs args)
+ {
+ var control = Control as Control;
+ if (control == null)
+ return;
+
+ if (args.Focus)
+ args.Result = control.Focus();
+ else
+ {
+ UnfocusControl(control);
+ args.Result = true;
+ }
+ }
+
+ internal void UnfocusControl(Control control)
+ {
+ if (control == null || !control.IsEnabled)
+ return;
+ control.IsEnabled = false;
+ control.IsEnabled = true;
+ }
+
+ void HandleTrackerUpdated(object sender, EventArgs e)
+ {
+ UpdateNativeWidget();
+ }
+
+ static bool IsExpectedTabbedPageMeasurementException(IVisualElementRenderer renderer)
+ {
+ // The TabbedPageRenderer's underlying Pivot control throws a NRE if the tabbed page
+ // does not have any toolbar items and it's measured during an animated transition
+ // from a page which does have toolbar items
+
+ // The NRE happens before TabbedPageRenderer's MeasureOverride is even called,
+ // so unfortunately we have to handle it here
+
+ var tpr = renderer.ContainerElement as TabbedPageRenderer;
+
+ var tp = tpr?.Element as TabbedPage;
+
+ return tp?.ToolbarItems.Count == 0;
+ }
+
+ void UpdateEnabled()
+ {
+ if (Control is Control)
+ (Control as Control).IsEnabled = Element.IsEnabled;
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WP8/VisualElementTracker.cs b/Xamarin.Forms.Platform.WP8/VisualElementTracker.cs
new file mode 100644
index 00000000..8211ec63
--- /dev/null
+++ b/Xamarin.Forms.Platform.WP8/VisualElementTracker.cs
@@ -0,0 +1,370 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Linq;
+using System.Windows;
+using System.Windows.Input;
+using System.Windows.Media;
+
+namespace Xamarin.Forms.Platform.WinPhone
+{
+ public abstract class VisualElementTracker : IDisposable
+ {
+ public abstract FrameworkElement Child { get; set; }
+
+ public abstract void Dispose();
+
+ public event EventHandler Updated;
+
+ protected void OnUpdated()
+ {
+ if (Updated != null)
+ Updated(this, EventArgs.Empty);
+ }
+ }
+
+ public class VisualElementTracker<TModel, TElement> : VisualElementTracker where TModel : VisualElement where TElement : FrameworkElement
+ {
+ FrameworkElement _child;
+ bool _disposed;
+ TElement _element;
+
+ bool _invalidateArrangeNeeded;
+ bool _isPanning;
+ bool _isPinching;
+
+ TModel _model;
+ bool _touchFrameReportedEventSet;
+ int _touchPoints = 1;
+
+ public override FrameworkElement Child
+ {
+ get { return _child; }
+ set
+ {
+ if (_child == value)
+ return;
+ _child = value;
+ UpdateNativeControl();
+ }
+ }
+
+ public TElement Element
+ {
+ get { return _element; }
+ set
+ {
+ if (_element == value)
+ return;
+
+ if (_element != null)
+ {
+ _element.Tap -= ElementOnTap;
+ _element.DoubleTap -= ElementOnDoubleTap;
+ _element.ManipulationDelta -= OnManipulationDelta;
+ _element.ManipulationCompleted -= OnManipulationCompleted;
+ }
+
+ _element = value;
+
+ if (_element != null)
+ {
+ _element.Tap += ElementOnTap;
+ _element.DoubleTap += ElementOnDoubleTap;
+ _element.ManipulationDelta += OnManipulationDelta;
+ _element.ManipulationCompleted += OnManipulationCompleted;
+ }
+
+ UpdateNativeControl();
+ }
+ }
+
+ public TModel Model
+ {
+ get { return _model; }
+ set
+ {
+ if (_model == value)
+ return;
+
+ if (_model != null)
+ {
+ _model.BatchCommitted -= HandleRedrawNeeded;
+ _model.PropertyChanged -= HandlePropertyChanged;
+ }
+
+ _model = value;
+
+ if (_model != null)
+ {
+ _model.BatchCommitted += HandleRedrawNeeded;
+ _model.PropertyChanged += HandlePropertyChanged;
+ }
+
+ UpdateNativeControl();
+ }
+ }
+
+ public override void Dispose()
+ {
+ if (_disposed)
+ return;
+ _disposed = true;
+
+ if (_element != null)
+ {
+ _element.Tap -= ElementOnTap;
+ _element.DoubleTap -= ElementOnDoubleTap;
+ _element.ManipulationDelta -= OnManipulationDelta;
+ _element.ManipulationCompleted -= OnManipulationCompleted;
+ }
+
+ if (_model != null)
+ {
+ _model.BatchCommitted -= HandleRedrawNeeded;
+ _model.PropertyChanged -= HandlePropertyChanged;
+ }
+
+ Child = null;
+ Model = null;
+ Element = null;
+ }
+
+ protected virtual void HandlePropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ if (Model.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(Model, Element);
+ else if (e.PropertyName == VisualElement.ScaleProperty.PropertyName)
+ UpdateScaleAndRotation(Model, Element);
+ 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(Model, Element);
+ else if (e.PropertyName == VisualElement.IsVisibleProperty.PropertyName)
+ UpdateVisibility(Model, Element);
+ else if (e.PropertyName == VisualElement.OpacityProperty.PropertyName)
+ UpdateOpacity(Model, Element);
+ else if (e.PropertyName == VisualElement.InputTransparentProperty.PropertyName)
+ UpdateInputTransparent(Model, Element);
+ }
+
+ protected virtual void UpdateNativeControl()
+ {
+ if (Model == null || Element == null)
+ return;
+
+ UpdateOpacity(_model, _element);
+ UpdateScaleAndRotation(_model, _element);
+ UpdateInputTransparent(_model, _element);
+
+ if (_invalidateArrangeNeeded)
+ MaybeInvalidate();
+ _invalidateArrangeNeeded = false;
+
+ UpdateTouchFrameReportedEvent(_model);
+
+ OnUpdated();
+ }
+
+ void ElementOnDoubleTap(object sender, GestureEventArgs gestureEventArgs)
+ {
+ var view = Model as View;
+ if (view == null)
+ return;
+
+ foreach (TapGestureRecognizer gestureRecognizer in
+ view.GestureRecognizers.OfType<TapGestureRecognizer>().Where(g => g.NumberOfTapsRequired == 2))
+ {
+ gestureRecognizer.SendTapped(view);
+ gestureEventArgs.Handled = true;
+ }
+ }
+
+ void ElementOnTap(object sender, GestureEventArgs gestureEventArgs)
+ {
+ var view = Model as View;
+ if (view == null)
+ return;
+
+ foreach (TapGestureRecognizer gestureRecognizer in
+ view.GestureRecognizers.OfType<TapGestureRecognizer>().Where(g => g.NumberOfTapsRequired == 1))
+ {
+ gestureRecognizer.SendTapped(view);
+ gestureEventArgs.Handled = true;
+ }
+ }
+
+ void HandlePan(ManipulationDeltaEventArgs e, View view)
+ {
+ foreach (PanGestureRecognizer recognizer in
+ view.GestureRecognizers.GetGesturesFor<PanGestureRecognizer>().Where(g => g.TouchPoints == _touchPoints))
+ {
+ if (!_isPanning)
+ ((IPanGestureController)recognizer).SendPanStarted(view, Application.Current.PanGestureId);
+
+ double totalX = 0;
+ double totalY = 0;
+
+ // Translation and CumulativeManipulation will be 0 if we have more than one touch point because it thinks we're pinching,
+ // so we'll just go ahead and use the center point of the pinch gesture to figure out how much we're panning.
+ if (_touchPoints > 1 && e.PinchManipulation != null)
+ {
+ totalX = e.PinchManipulation.Current.Center.X - e.PinchManipulation.Original.Center.X;
+ totalY = e.PinchManipulation.Current.Center.Y - e.PinchManipulation.Original.Center.Y;
+ }
+ else
+ {
+ totalX = e.DeltaManipulation.Translation.X + e.CumulativeManipulation.Translation.X;
+ totalY = e.DeltaManipulation.Translation.Y + e.CumulativeManipulation.Translation.Y;
+ }
+
+ ((IPanGestureController)recognizer).SendPan(view, totalX, totalY, Application.Current.PanGestureId);
+ _isPanning = true;
+ }
+ }
+
+ void HandlePinch(ManipulationDeltaEventArgs e, View view)
+ {
+ if (e.PinchManipulation == null)
+ return;
+
+ IEnumerable<PinchGestureRecognizer> pinchGestures = view.GestureRecognizers.GetGesturesFor<PinchGestureRecognizer>();
+ System.Windows.Point translationPoint = e.ManipulationContainer.TransformToVisual(Element).Transform(e.PinchManipulation.Current.Center);
+ var scaleOriginPoint = new Point(translationPoint.X / view.Width, translationPoint.Y / view.Height);
+ foreach (var recognizer in pinchGestures)
+ {
+ if (!_isPinching)
+ ((IPinchGestureController)recognizer).SendPinchStarted(view, scaleOriginPoint);
+ ((IPinchGestureController)recognizer).SendPinch(view, e.PinchManipulation.DeltaScale, scaleOriginPoint);
+ }
+ _isPinching = true;
+ }
+
+ void HandleRedrawNeeded(object sender, EventArgs e)
+ {
+ UpdateNativeControl();
+ }
+
+ void MaybeInvalidate()
+ {
+ if (Model.IsInNativeLayout)
+ return;
+ var parent = (FrameworkElement)Element.Parent;
+ parent?.InvalidateMeasure();
+ Element.InvalidateMeasure();
+ }
+
+ void OnManipulationCompleted(object sender, ManipulationCompletedEventArgs e)
+ {
+ var view = Model as View;
+ if (view == null)
+ return;
+
+ IEnumerable pinchGestures = view.GestureRecognizers.GetGesturesFor<PinchGestureRecognizer>();
+ foreach (var recognizer in pinchGestures)
+ ((IPinchGestureController)recognizer).SendPinchEnded(view);
+ _isPinching = false;
+
+ IEnumerable<PanGestureRecognizer> panGestures = view.GestureRecognizers.GetGesturesFor<PanGestureRecognizer>().Where(g => g.TouchPoints == _touchPoints);
+ foreach (PanGestureRecognizer recognizer in panGestures)
+ ((IPanGestureController)recognizer).SendPanCompleted(view, Application.Current.PanGestureId);
+ Application.Current.PanGestureId++;
+ _isPanning = false;
+ }
+
+ void OnManipulationDelta(object sender, ManipulationDeltaEventArgs e)
+ {
+ var view = Model as View;
+ if (view == null)
+ return;
+
+ HandlePinch(e, view);
+
+ HandlePan(e, view);
+ }
+
+ void Touch_FrameReported(object sender, TouchFrameEventArgs e)
+ {
+ _touchPoints = e.GetTouchPoints(Child).Count;
+ }
+
+ 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;
+
+ 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 System.Windows.Point(anchorX, anchorY);
+ frameworkElement.RenderTransform = new ScaleTransform { ScaleX = scale, ScaleY = scale };
+
+ UpdateRotation(view, frameworkElement);
+ }
+
+ void UpdateTouchFrameReportedEvent(VisualElement model)
+ {
+ if (_touchFrameReportedEventSet)
+ return;
+
+ Touch.FrameReported -= Touch_FrameReported;
+ _touchFrameReportedEventSet = false;
+
+ var view = model as View;
+ if (view == null)
+ return;
+
+ if (!view.GestureRecognizers.GetGesturesFor<PanGestureRecognizer>().Any(g => g.TouchPoints > 1))
+ return;
+
+ Touch.FrameReported += Touch_FrameReported;
+ _touchFrameReportedEventSet = true;
+ }
+
+ static void UpdateVisibility(VisualElement view, FrameworkElement frameworkElement)
+ {
+ frameworkElement.Visibility = view.IsVisible ? Visibility.Visible : Visibility.Collapsed;
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WP8/WP8PlatformServices.cs b/Xamarin.Forms.Platform.WP8/WP8PlatformServices.cs
new file mode 100644
index 00000000..c20071f4
--- /dev/null
+++ b/Xamarin.Forms.Platform.WP8/WP8PlatformServices.cs
@@ -0,0 +1,232 @@
+using System;
+using System.IO;
+using System.IO.IsolatedStorage;
+using System.Net;
+using System.Reflection;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Threading;
+using Windows.System;
+using Xamarin.Forms.Platform.WinPhone;
+
+namespace Xamarin.Forms
+{
+ internal class WP8PlatformServices : IPlatformServices
+ {
+ static readonly MD5CryptoServiceProvider Checksum = new MD5CryptoServiceProvider();
+
+ public void BeginInvokeOnMainThread(Action action)
+ {
+ Deployment.Current.Dispatcher.BeginInvoke(action);
+ }
+
+ public ITimer CreateTimer(Action<object> callback)
+ {
+ return new _Timer(new Timer(o => callback(o)));
+ }
+
+ public ITimer CreateTimer(Action<object> callback, object state, int dueTime, int period)
+ {
+ return new _Timer(new Timer(o => callback(o), state, dueTime, period));
+ }
+
+ public ITimer CreateTimer(Action<object> callback, object state, long dueTime, long period)
+ {
+ return new _Timer(new Timer(o => callback(o), state, dueTime, period));
+ }
+
+ public ITimer CreateTimer(Action<object> callback, object state, TimeSpan dueTime, TimeSpan period)
+ {
+ return new _Timer(new Timer(o => callback(o), state, dueTime, period));
+ }
+
+ public ITimer CreateTimer(Action<object> callback, object state, uint dueTime, uint period)
+ {
+ return new _Timer(new Timer(o => callback(o), state, dueTime, period));
+ }
+
+ public Assembly[] GetAssemblies()
+ {
+ return AppDomain.CurrentDomain.GetAssemblies();
+ }
+
+ public string GetMD5Hash(string input)
+ {
+ byte[] bytes = Checksum.ComputeHash(Encoding.UTF8.GetBytes(input));
+ var ret = new char[32];
+ for (var i = 0; i < 16; i++)
+ {
+ ret[i * 2] = (char)Hex(bytes[i] >> 4);
+ ret[i * 2 + 1] = (char)Hex(bytes[i] & 0xf);
+ }
+ return new string(ret);
+ }
+
+ public double GetNamedSize(NamedSize size, Type targetElementType, bool useOldSizes)
+ {
+ switch (size)
+ {
+ case NamedSize.Default:
+ if (typeof(Label).IsAssignableFrom(targetElementType))
+ return (double)System.Windows.Application.Current.Resources["PhoneFontSizeNormal"];
+ return (double)System.Windows.Application.Current.Resources["PhoneFontSizeMedium"];
+ case NamedSize.Micro:
+ return (double)System.Windows.Application.Current.Resources["PhoneFontSizeSmall"] - 3;
+ case NamedSize.Small:
+ return (double)System.Windows.Application.Current.Resources["PhoneFontSizeSmall"];
+ case NamedSize.Medium:
+ if (useOldSizes)
+ goto case NamedSize.Default;
+ return (double)System.Windows.Application.Current.Resources["PhoneFontSizeMedium"];
+ case NamedSize.Large:
+ return (double)System.Windows.Application.Current.Resources["PhoneFontSizeLarge"];
+ default:
+ throw new ArgumentOutOfRangeException("size");
+ }
+ }
+
+ public Task<Stream> GetStreamAsync(Uri uri, CancellationToken cancellationToken)
+ {
+ var tcs = new TaskCompletionSource<Stream>();
+
+ try
+ {
+ HttpWebRequest request = WebRequest.CreateHttp(uri);
+ request.AllowReadStreamBuffering = true;
+ request.BeginGetResponse(ar =>
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ tcs.SetCanceled();
+ return;
+ }
+
+ try
+ {
+ Stream stream = request.EndGetResponse(ar).GetResponseStream();
+ tcs.TrySetResult(stream);
+ }
+ catch (Exception ex)
+ {
+ tcs.TrySetException(ex);
+ }
+ }, null);
+ }
+ catch (Exception ex)
+ {
+ tcs.TrySetException(ex);
+ }
+
+ return tcs.Task;
+ }
+
+ public IIsolatedStorageFile GetUserStoreForApplication()
+ {
+ return new _IsolatedStorageFile(IsolatedStorageFile.GetUserStoreForApplication());
+ }
+
+ public bool IsInvokeRequired
+ {
+ get { return !Deployment.Current.Dispatcher.CheckAccess(); }
+ }
+
+ public void OpenUriAction(Uri uri)
+ {
+ Launcher.LaunchUriAsync(uri);
+ }
+
+ 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();
+ };
+ }
+
+ static int Hex(int v)
+ {
+ if (v < 10)
+ return '0' + v;
+ return 'a' + v - 10;
+ }
+
+ public class _Timer : ITimer
+ {
+ readonly Timer _timer;
+
+ public _Timer(Timer timer)
+ {
+ _timer = timer;
+ }
+
+ public void Change(int dueTime, int period)
+ {
+ _timer.Change(dueTime, period);
+ }
+
+ public void Change(long dueTime, long period)
+ {
+ _timer.Change(dueTime, period);
+ }
+
+ public void Change(TimeSpan dueTime, TimeSpan period)
+ {
+ _timer.Change(dueTime, period);
+ }
+
+ public void Change(uint dueTime, uint period)
+ {
+ _timer.Change(dueTime, period);
+ }
+ }
+
+ public class _IsolatedStorageFile : IIsolatedStorageFile
+ {
+ readonly IsolatedStorageFile _isolatedStorageFile;
+
+ public _IsolatedStorageFile(IsolatedStorageFile isolatedStorageFile)
+ {
+ _isolatedStorageFile = isolatedStorageFile;
+ }
+
+ public Task CreateDirectoryAsync(string path)
+ {
+ _isolatedStorageFile.CreateDirectory(path);
+ return Task.FromResult(true);
+ }
+
+ public Task<bool> GetDirectoryExistsAsync(string path)
+ {
+ return Task.FromResult(_isolatedStorageFile.DirectoryExists(path));
+ }
+
+ public Task<bool> GetFileExistsAsync(string path)
+ {
+ return Task.FromResult(_isolatedStorageFile.FileExists(path));
+ }
+
+ public Task<DateTimeOffset> GetLastWriteTimeAsync(string path)
+ {
+ return Task.FromResult(_isolatedStorageFile.GetLastWriteTime(path));
+ }
+
+ public Task<Stream> OpenFileAsync(string path, FileMode mode, FileAccess access)
+ {
+ Stream stream = _isolatedStorageFile.OpenFile(path, (System.IO.FileMode)mode, (System.IO.FileAccess)access);
+ return Task.FromResult(stream);
+ }
+
+ public Task<Stream> OpenFileAsync(string path, FileMode mode, FileAccess access, FileShare share)
+ {
+ Stream stream = _isolatedStorageFile.OpenFile(path, (System.IO.FileMode)mode, (System.IO.FileAccess)access, (System.IO.FileShare)share);
+ return Task.FromResult(stream);
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WP8/WPResources.xaml b/Xamarin.Forms.Platform.WP8/WPResources.xaml
new file mode 100644
index 00000000..6a234e60
--- /dev/null
+++ b/Xamarin.Forms.Platform.WP8/WPResources.xaml
@@ -0,0 +1,542 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ xmlns:toolkit="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone.Controls.Toolkit"
+ xmlns:forms="clr-namespace:Xamarin.Forms.Platform.WinPhone"
+ xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
+ xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity">
+
+ <forms:CaseConverter x:Key="UpperConverter" ConvertToUpper="True" />
+ <forms:CaseConverter x:Key="LowerConverter" ConvertToUpper="False" />
+ <forms:ColorConverter x:Key="ColorConverter" />
+ <forms:HeightConverter x:Key="HeightConverter" />
+ <forms:HorizontalTextAlignmentConverter x:Key="HorizontalTextAlignmentConverter" />
+ <forms:KeyboardConverter x:Key="KeyboardConverter"/>
+ <forms:ImageConverter x:Key="ImageConverter" />
+ <forms:ViewToRendererConverter x:Key="ViewToRenderer" />
+ <forms:PageToRendererConverter x:Key="PageToRenderer" />
+ <forms:CollapseWhenEmptyConverter x:Key="CollapseWhenEmpty" />
+ <forms:EntryCellRendererCompleted x:Key="EntryCellRendererCompleted" />
+
+ <Style x:Key="PhoneTextSmallTitleStyle" BasedOn="{StaticResource PhoneTextNormalStyle}" TargetType="TextBlock">
+ <Style.Setters>
+ <Setter Property="FontWeight" Value="Bold" />
+ </Style.Setters>
+ </Style>
+
+ <DataTemplate x:Key="CellTemplate">
+ <Border Background="Transparent">
+ <forms:CellControl Cell="{Binding}" HorizontalContentAlignment="Stretch" />
+ </Border>
+ </DataTemplate>
+
+ <DataTemplate x:Key="TableGroup">
+ <StackPanel Margin="0,20,0,0">
+ <TextBlock Text="{Binding Title,Converter={StaticResource LowerConverter}}" Style="{StaticResource PhoneTextGroupHeaderStyle}" />
+ <ItemsControl ItemsSource="{Binding}" ItemTemplate="{StaticResource CellTemplate}" />
+ </StackPanel>
+ </DataTemplate>
+
+ <DataTemplate x:Key="ViewCell">
+ <ContentPresenter Height="{Binding RenderHeight, Converter={StaticResource HeightConverter}}" Content="{Binding View,Converter={StaticResource ViewToRenderer}}" />
+ </DataTemplate>
+
+ <DataTemplate x:Key="View">
+ <ContentPresenter Content="{Binding Path=.,Converter={StaticResource ViewToRenderer}}" />
+ </DataTemplate>
+
+ <Style x:Key="HeaderJumpStyle" TargetType="phone:LongListSelector">
+ <Setter Property="GridCellSize" Value="113,113" />
+ <Setter Property="LayoutMode" Value="Grid" />
+ <Setter Property="ItemTemplate">
+ <Setter.Value>
+ <DataTemplate>
+ <Border Background="{StaticResource PhoneAccentBrush}" Margin="6">
+ <TextBlock Text="{Binding ShortName, Converter={StaticResource LowerConverter}}" Foreground="{StaticResource PhoneForegroundBrush}" FontSize="48" Padding="6" FontFamily="{StaticResource PhoneFontFamilySemiLight}" HorizontalAlignment="Left" VerticalAlignment="Bottom" />
+ </Border>
+ </DataTemplate>
+ </Setter.Value>
+ </Setter>
+ </Style>
+
+ <DataTemplate x:Key="ListViewHeader">
+ <forms:CellControl Cell="{Binding HeaderContent}" HorizontalContentAlignment="Left" ShowContextActions="False" />
+ </DataTemplate>
+
+ <DataTemplate x:Key="ListViewHeaderTextCell">
+ <Border Background="{StaticResource PhoneAccentBrush}" Height="{Binding RenderHeight, Converter={StaticResource HeightConverter}}" Margin="5" MinWidth="62" MinHeight="62">
+ <StackPanel Margin="6">
+ <StackPanel.Resources>
+ <Style TargetType="TextBlock">
+ <Setter Property="FontFamily" Value="{StaticResource PhoneFontFamilySemiLight}" />
+ </Style>
+ </StackPanel.Resources>
+
+ <TextBlock FontSize="48"
+ Text="{Binding Text, Converter={StaticResource LowerConverter}}"
+ Visibility="{Binding Text,RelativeSource={RelativeSource Self}, Converter={StaticResource CollapseWhenEmpty}}"
+ Foreground="{Binding TextColor,Converter={StaticResource ColorConverter},ConverterParameter=PhoneForegroundBrush}" />
+
+ <TextBlock FontSize="32"
+ Text="{Binding Detail}"
+ Visibility="{Binding Text,RelativeSource={RelativeSource Self}, Converter={StaticResource CollapseWhenEmpty}}"
+ Foreground="{Binding DetailColor,Converter={StaticResource ColorConverter},ConverterParameter=PhoneAccentBrush}" />
+ </StackPanel>
+ </Border>
+ </DataTemplate>
+
+ <DataTemplate x:Key="TextCell">
+ <Button Name="button" Margin="0,10,0,0" Height="{Binding RenderHeight, Converter={StaticResource HeightConverter}}" IsEnabled="{Binding IsEnabled}">
+ <Button.Template>
+ <ControlTemplate>
+ <Grid Background="{Binding Background,RelativeSource={RelativeSource TemplatedParent}}">
+ <ContentPresenter />
+ </Grid>
+ </ControlTemplate>
+ </Button.Template>
+ <Button.Content>
+ <StackPanel>
+ <TextBlock
+ Style="{StaticResource PhoneTextExtraLargeStyle}"
+ Text="{Binding Text,Converter={StaticResource LowerConverter}}"
+ Foreground="{Binding TextColor,Converter={StaticResource ColorConverter},ConverterParameter=PhoneForegroundBrush}"
+ Visibility="{Binding Text,RelativeSource={RelativeSource Self}, Converter={StaticResource CollapseWhenEmpty}}"
+ toolkit:TiltEffect.IsTiltEnabled="True"
+ />
+
+ <TextBlock
+ Style="{StaticResource PhoneTextAccentStyle}"
+ Text="{Binding Detail}"
+ Foreground="{Binding DetailColor,Converter={StaticResource ColorConverter},ConverterParameter=PhoneAccentBrush}"
+ Visibility="{Binding Text,RelativeSource={RelativeSource Self}, Converter={StaticResource CollapseWhenEmpty}}"
+ toolkit:TiltEffect.IsTiltEnabled="True"
+ />
+ </StackPanel>
+ </Button.Content>
+ </Button>
+ </DataTemplate>
+
+ <DataTemplate x:Key="ImageCell">
+ <Button Name="button" Margin="0,10,0,0" Height="{Binding RenderHeight, Converter={StaticResource HeightConverter}}" IsEnabled="{Binding IsEnabled}">
+ <Button.Template>
+ <ControlTemplate>
+ <Grid Background="{Binding Background,RelativeSource={RelativeSource TemplatedParent}}">
+ <ContentPresenter />
+ </Grid>
+ </ControlTemplate>
+ </Button.Template>
+ <Button.Content>
+ <Grid Margin="10, 0, 0, 0">
+ <Grid.ColumnDefinitions>
+ <ColumnDefinition Width="80"/>
+ <ColumnDefinition Width="*" />
+ </Grid.ColumnDefinitions>
+
+ <Image
+ Grid.Column="0"
+ DataContext="{Binding ImageSource, Converter={StaticResource ImageConverter}}"
+ Source="{Binding Value}"
+ VerticalAlignment="Center"
+ />
+
+ <StackPanel Grid.Column="1">
+ <TextBlock
+ Style="{StaticResource PhoneTextExtraLargeStyle}"
+ Text="{Binding Text,Converter={StaticResource LowerConverter}}"
+ Foreground="{Binding TextColor,Converter={StaticResource ColorConverter},ConverterParameter=PhoneForegroundBrush}"
+ Visibility="{Binding Text,RelativeSource={RelativeSource Self}, Converter={StaticResource CollapseWhenEmpty}}"
+ toolkit:TiltEffect.IsTiltEnabled="True"
+ />
+
+ <TextBlock
+ Style="{StaticResource PhoneTextAccentStyle}"
+ Text="{Binding Detail}"
+ Foreground="{Binding DetailColor,Converter={StaticResource ColorConverter},ConverterParameter=PhoneAccentBrush}"
+ Visibility="{Binding Text,RelativeSource={RelativeSource Self}, Converter={StaticResource CollapseWhenEmpty}}"
+ toolkit:TiltEffect.IsTiltEnabled="True"
+ />
+ </StackPanel>
+ </Grid>
+ </Button.Content>
+ </Button>
+ </DataTemplate>
+
+ <DataTemplate x:Key="ListImageCell">
+ <Button Margin="0,10,0,0" Height="{Binding RenderHeight, Converter={StaticResource HeightConverter}}" IsEnabled="{Binding IsEnabled}">
+ <Button.Template>
+ <ControlTemplate>
+ <Grid Background="{Binding Background,RelativeSource={RelativeSource TemplatedParent}}">
+ <ContentPresenter />
+ </Grid>
+ </ControlTemplate>
+ </Button.Template>
+ <Button.Content>
+ <Grid Margin="10, 0, 0, 0">
+ <Grid.ColumnDefinitions>
+ <ColumnDefinition Width="80"/>
+ <ColumnDefinition Width="*" />
+ </Grid.ColumnDefinitions>
+
+ <Image
+ Grid.Column="0"
+ DataContext="{Binding ImageSource, Converter={StaticResource ImageConverter}}"
+ Source="{Binding Value}"
+ VerticalAlignment="Center"
+ />
+
+ <StackPanel Grid.Column="1">
+ <TextBlock
+ Style="{StaticResource PhoneTextLargeStyle}"
+ Text="{Binding Text}"
+ Foreground="{Binding TextColor,Converter={StaticResource ColorConverter},ConverterParameter=PhoneForegroundBrush}"
+ Visibility="{Binding Text,RelativeSource={RelativeSource Self}, Converter={StaticResource CollapseWhenEmpty}}"
+ toolkit:TiltEffect.IsTiltEnabled="True"
+ forms:ListViewRenderer.HighlightWhenSelected="True"
+ />
+
+ <TextBlock
+ Style="{StaticResource PhoneTextAccentStyle}"
+ Text="{Binding Detail}"
+ Foreground="{Binding DetailColor,Converter={StaticResource ColorConverter},ConverterParameter=PhoneAccentBrush}"
+ Visibility="{Binding Text,RelativeSource={RelativeSource Self}, Converter={StaticResource CollapseWhenEmpty}}"
+ toolkit:TiltEffect.IsTiltEnabled="True"
+ forms:ListViewRenderer.HighlightWhenSelected="True"
+ />
+ </StackPanel>
+ </Grid>
+ </Button.Content>
+ </Button>
+ </DataTemplate>
+
+ <DataTemplate x:Key="ListViewTextCell">
+ <Button Margin="0,10,0,0" Height="{Binding RenderHeight, Converter={StaticResource HeightConverter}}" IsEnabled="{Binding IsEnabled}">
+ <Button.Template>
+ <ControlTemplate>
+ <Grid Background="{Binding Background,RelativeSource={RelativeSource TemplatedParent}}">
+ <ContentPresenter />
+ </Grid>
+ </ControlTemplate>
+ </Button.Template>
+ <Button.Content>
+ <StackPanel>
+ <TextBlock
+ Style="{StaticResource PhoneTextLargeStyle}"
+ Text="{Binding Text}"
+ Foreground="{Binding TextColor,Converter={StaticResource ColorConverter},ConverterParameter=PhoneForegroundBrush}"
+ Visibility="{Binding Text,RelativeSource={RelativeSource Self}, Converter={StaticResource CollapseWhenEmpty}}"
+ toolkit:TiltEffect.IsTiltEnabled="True"
+ forms:ListViewRenderer.HighlightWhenSelected="True"
+ />
+
+ <TextBlock
+ Style="{StaticResource PhoneTextAccentStyle}"
+ Text="{Binding Detail}"
+ Foreground="{Binding DetailColor,Converter={StaticResource ColorConverter},ConverterParameter=PhoneAccentBrush}"
+ Visibility="{Binding Text,RelativeSource={RelativeSource Self}, Converter={StaticResource CollapseWhenEmpty}}"
+ toolkit:TiltEffect.IsTiltEnabled="True"
+ forms:ListViewRenderer.HighlightWhenSelected="True"
+ />
+ </StackPanel>
+ </Button.Content>
+ </Button>
+ </DataTemplate>
+
+ <DataTemplate x:Key="EntryCell">
+ <Grid HorizontalAlignment="Stretch">
+ <Grid.ColumnDefinitions>
+ <ColumnDefinition Width="Auto"/>
+ <ColumnDefinition Width="*"/>
+ </Grid.ColumnDefinitions>
+
+ <Grid.Children>
+ <TextBlock forms:ListViewRenderer.HighlightWhenSelected="True" Style="{StaticResource PhoneTextLargeStyle}"
+ Text="{Binding Label}"
+ Foreground="{Binding LabelColor,Converter={StaticResource ColorConverter},ConverterParameter=PhoneForegroundBrush}"
+ Visibility="{Binding Text,RelativeSource={RelativeSource Self}, Converter={StaticResource CollapseWhenEmpty}}"
+ VerticalAlignment="Center"
+ Grid.Column="0"
+ />
+ <forms:EntryCellPhoneTextBox
+ IsEnabled="{Binding IsEnabled}"
+ Hint="{Binding Placeholder}"
+ Text="{Binding Text, Mode=TwoWay}"
+ InputScope="{Binding Keyboard, Converter={StaticResource KeyboardConverter}}"
+ VerticalAlignment="Center"
+ TextAlignment="{Binding HorizontalTextAlignment, Converter={StaticResource HorizontalTextAlignmentConverter}}"
+ Grid.Column="1"
+ >
+ <i:Interaction.Triggers>
+ <i:EventTrigger EventName="KeyboardReturnPressed">
+ <i:InvokeCommandAction Command="{StaticResource EntryCellRendererCompleted}" CommandParameter="{Binding}" />
+ </i:EventTrigger>
+ </i:Interaction.Triggers>
+ </forms:EntryCellPhoneTextBox>
+ </Grid.Children>
+ </Grid>
+ </DataTemplate>
+
+ <DataTemplate x:Key="SwitchCell">
+ <toolkit:ToggleSwitch Content="{Binding Text}" Height="{Binding RenderHeight, Converter={StaticResource HeightConverter}}"
+ IsChecked="{Binding On, Mode=TwoWay}" IsEnabled="{Binding IsEnabled}" FontFamily="{StaticResource PhoneFontFamilyNormal}" />
+ </DataTemplate>
+
+ <DataTemplate x:Key="TabbedPage">
+ <forms:TabbedPagePresenter Content="{Binding Converter={StaticResource PageToRenderer}}" />
+ </DataTemplate>
+
+ <DataTemplate x:Key="TabbedPageHeader">
+ <TextBlock Text="{Binding Title, Converter={StaticResource LowerConverter}}" />
+ </DataTemplate>
+
+ <DataTemplate x:Key="CarouselPage">
+ <forms:CarouselPagePresenter Content="{Binding Converter={StaticResource PageToRenderer}}" />
+ </DataTemplate>
+
+ <DataTemplate x:Key="PickerItemTemplate">
+ <StackPanel>
+ <TextBlock Text="{Binding Data}" Opacity="{Binding Opacity}"/>
+ </StackPanel>
+ </DataTemplate>
+
+ <DataTemplate x:Key="PickerFullItemTemplate">
+ <StackPanel MaxHeight="{Binding MaxHeight}">
+ <TextBlock Text="{Binding Data}" FontSize="43" />
+ </StackPanel>
+ </DataTemplate>
+
+ <!-- Overriding the PhoneTextBox Style so we can handle Placeholder alignment -->
+ <forms:TextAlignmentToHorizontalAlignmentConverter x:Key="AlignmentConverter" />
+ <Style TargetType="forms:FormsPhoneTextBox">
+ <Setter Property="FontFamily" Value="{StaticResource PhoneFontFamilyNormal}" />
+ <Setter Property="FontSize" Value="{StaticResource PhoneFontSizeMediumLarge}" />
+ <Setter Property="Background" Value="{StaticResource PhoneTextBoxBrush}" />
+ <Setter Property="Foreground" Value="{StaticResource PhoneTextBoxForegroundBrush}" />
+ <Setter Property="BorderBrush" Value="{StaticResource PhoneTextBoxBrush}" />
+ <Setter Property="SelectionBackground" Value="{StaticResource PhoneAccentBrush}" />
+ <Setter Property="SelectionForeground" Value="{StaticResource PhoneTextBoxSelectionForegroundBrush}" />
+ <Setter Property="BorderThickness" Value="{StaticResource PhoneBorderThickness}" />
+ <Setter Property="Padding" Value="{StaticResource PhoneBorderThickness}" />
+ <Setter Property="PlaceholderForegroundBrush" Value="{StaticResource PhoneTextBoxReadOnlyBrush}" />
+
+ <Setter Property="Template">
+ <Setter.Value>
+
+ <ControlTemplate TargetType="forms:FormsPhoneTextBox">
+
+ <Grid Background="Transparent" x:Name="RootGrid">
+ <VisualStateManager.VisualStateGroups>
+ <VisualStateGroup x:Name="CommonStates">
+ <VisualState x:Name="Normal" />
+ <VisualState x:Name="Disabled">
+ <Storyboard>
+ <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Visibility"
+ Storyboard.TargetName="HintBorder">
+ <DiscreteObjectKeyFrame KeyTime="0">
+ <DiscreteObjectKeyFrame.Value>
+ <Visibility>Collapsed</Visibility>
+ </DiscreteObjectKeyFrame.Value>
+ </DiscreteObjectKeyFrame>
+ </ObjectAnimationUsingKeyFrames>
+ <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Visibility"
+ Storyboard.TargetName="TextBorder">
+ <DiscreteObjectKeyFrame KeyTime="0">
+ <DiscreteObjectKeyFrame.Value>
+ <Visibility>Visible</Visibility>
+ </DiscreteObjectKeyFrame.Value>
+ </DiscreteObjectKeyFrame>
+ </ObjectAnimationUsingKeyFrames>
+ <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="BorderBrush"
+ Storyboard.TargetName="TextBorder">
+ <DiscreteObjectKeyFrame KeyTime="0" Value="Transparent" />
+ </ObjectAnimationUsingKeyFrames>
+ <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Margin"
+ Storyboard.TargetName="TextBorder">
+ <DiscreteObjectKeyFrame KeyTime="0" Value="0" />
+ </ObjectAnimationUsingKeyFrames>
+ <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="HorizontalAlignment"
+ Storyboard.TargetName="Text">
+ <DiscreteObjectKeyFrame KeyTime="0">
+ <DiscreteObjectKeyFrame.Value>
+ <HorizontalAlignment>Stretch</HorizontalAlignment>
+ </DiscreteObjectKeyFrame.Value>
+ </DiscreteObjectKeyFrame>
+ </ObjectAnimationUsingKeyFrames>
+ </Storyboard>
+ </VisualState>
+ </VisualStateGroup>
+ <VisualStateGroup x:Name="FocusStates">
+ <VisualState x:Name="Focused">
+ <Storyboard>
+ <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Background"
+ Storyboard.TargetName="HintBorder">
+ <DiscreteObjectKeyFrame KeyTime="0"
+ Value="{StaticResource PhoneTextBoxEditBackgroundBrush}" />
+ </ObjectAnimationUsingKeyFrames>
+ <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="BorderBrush"
+ Storyboard.TargetName="HintBorder">
+ <DiscreteObjectKeyFrame KeyTime="0"
+ Value="{StaticResource PhoneTextBoxEditBorderBrush}" />
+ </ObjectAnimationUsingKeyFrames>
+ </Storyboard>
+ </VisualState>
+ <VisualState x:Name="Unfocused" />
+ </VisualStateGroup>
+ <VisualStateGroup x:Name="LengthIndicatorStates">
+ <VisualState x:Name="LengthIndicatorVisible">
+ <Storyboard>
+ <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Visibility"
+ Storyboard.TargetName="LengthIndicator">
+ <DiscreteObjectKeyFrame KeyTime="0:0:0">
+ <DiscreteObjectKeyFrame.Value>
+ <Visibility>Visible</Visibility>
+ </DiscreteObjectKeyFrame.Value>
+ </DiscreteObjectKeyFrame>
+ </ObjectAnimationUsingKeyFrames>
+
+ <ObjectAnimationUsingKeyFrames Storyboard.TargetName="RootGrid"
+ Storyboard.TargetProperty="Margin">
+ <DiscreteObjectKeyFrame KeyTime="0:0:0"
+ Value="0, 0, 0, 27" />
+ </ObjectAnimationUsingKeyFrames>
+
+ <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Opacity"
+ Storyboard.TargetName="LengthIndicator">
+ <DiscreteObjectKeyFrame KeyTime="0:0:0" Value="0.6" />
+ </ObjectAnimationUsingKeyFrames>
+
+ <DoubleAnimation Storyboard.TargetName="LengthIndicator"
+ Storyboard.TargetProperty="(UIElement.RenderTransform).(TranslateTransform.Y)"
+ To="32"
+ Duration="0:0:0.350">
+ <DoubleAnimation.EasingFunction>
+ <ExponentialEase Exponent="6" />
+ </DoubleAnimation.EasingFunction>
+ </DoubleAnimation>
+ </Storyboard>
+ </VisualState>
+ <VisualState x:Name="LengthIndicatorHidden">
+ <Storyboard>
+ <DoubleAnimation Storyboard.TargetName="LengthIndicator"
+ Storyboard.TargetProperty="(UIElement.RenderTransform).(TranslateTransform.Y)"
+ To="0"
+ Duration="0:0:0.350">
+ <DoubleAnimation.EasingFunction>
+ <ExponentialEase Exponent="6" />
+ </DoubleAnimation.EasingFunction>
+ </DoubleAnimation>
+
+ <ObjectAnimationUsingKeyFrames Storyboard.TargetName="RootGrid"
+ Storyboard.TargetProperty="Margin">
+ <DiscreteObjectKeyFrame KeyTime="0:0:0"
+ Value="0, 0, 0, 0" />
+ </ObjectAnimationUsingKeyFrames>
+
+ <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Opacity"
+ Storyboard.TargetName="LengthIndicator">
+ <DiscreteObjectKeyFrame KeyTime="0:0:0.350" Value="0" />
+ </ObjectAnimationUsingKeyFrames>
+
+ <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Visibility"
+ Storyboard.TargetName="LengthIndicator">
+ <DiscreteObjectKeyFrame KeyTime="0:0:0.350">
+ <DiscreteObjectKeyFrame.Value>
+ <Visibility>Collapsed</Visibility>
+ </DiscreteObjectKeyFrame.Value>
+ </DiscreteObjectKeyFrame>
+ </ObjectAnimationUsingKeyFrames>
+ </Storyboard>
+ </VisualState>
+ </VisualStateGroup>
+ </VisualStateManager.VisualStateGroups>
+
+ <Border x:Name="LengthIndicatorBorder">
+ <TextBlock Foreground="{StaticResource PhoneContrastBackgroundBrush}"
+ HorizontalAlignment="Right"
+ TextAlignment="Right"
+ VerticalAlignment="Bottom"
+ Margin="{StaticResource PhoneMargin}"
+ FontSize="{StaticResource PhoneFontSizeNormal}"
+ Opacity="0"
+ x:Name="LengthIndicator">
+ <TextBlock.RenderTransform>
+ <TranslateTransform />
+ </TextBlock.RenderTransform>
+ </TextBlock>
+ </Border>
+
+ <Border x:Name="HintBorder"
+ BorderBrush="{TemplateBinding BorderBrush}"
+ BorderThickness="{TemplateBinding BorderThickness}"
+ Background="{TemplateBinding Background}"
+ Margin="{StaticResource PhoneTouchTargetOverhang}">
+ <Grid>
+ <ContentControl x:Name="HintContent"
+ Style="{TemplateBinding HintStyle}"
+ Content="{TemplateBinding Hint}"
+ Foreground="{TemplateBinding PlaceholderForegroundBrush}"
+ Background="Transparent"
+ HorizontalAlignment="{Binding TextAlignment,
+ RelativeSource={RelativeSource Mode=TemplatedParent},
+ Converter={StaticResource AlignmentConverter}}"
+ VerticalAlignment="Center"
+ Margin="3,0,3,0"
+ Visibility="{TemplateBinding ActualHintVisibility}" />
+ <ContentControl x:Name="ContentElement"
+ BorderThickness="0"
+ HorizontalContentAlignment="Stretch"
+ Margin="{StaticResource PhoneTextBoxInnerMargin}"
+ Padding="{TemplateBinding Padding}"
+ VerticalContentAlignment="Stretch" />
+ </Grid>
+ </Border>
+ <Border x:Name="TextBorder"
+ BorderBrush="{StaticResource PhoneDisabledBrush}"
+ BorderThickness="{TemplateBinding BorderThickness}"
+ Background="Transparent"
+ Margin="{StaticResource PhoneTouchTargetOverhang}"
+ Visibility="Collapsed">
+ <TextBox x:Name="Text"
+ Foreground="{StaticResource PhoneDisabledBrush}"
+ FontWeight="{TemplateBinding FontWeight}"
+ FontStyle="{TemplateBinding FontStyle}"
+ FontSize="{TemplateBinding FontSize}"
+ FontFamily="{TemplateBinding FontFamily}"
+ SelectionForeground="{TemplateBinding SelectionForeground}"
+ SelectionBackground="{TemplateBinding SelectionBackground}"
+ TextAlignment="{TemplateBinding TextAlignment}"
+ TextWrapping="{TemplateBinding TextWrapping}"
+ Text="{TemplateBinding DisabledText}"
+
+ HorizontalAlignment="Left" />
+ </Border>
+ <Border x:Name="ActionIconBorder"
+ Width="84"
+ Height="72"
+ Background="Transparent"
+ HorizontalAlignment="Right"
+ VerticalAlignment="Bottom">
+ <Image x:Name="ActionIcon"
+ Width="26"
+ Height="26"
+ Source="{TemplateBinding ActionIcon}" />
+ </Border>
+
+ <TextBlock x:Name="MeasurementTextBlock"
+ Margin="8"
+ IsHitTestVisible="False"
+ Opacity="0"
+ FontFamily="{TemplateBinding FontFamily}"
+ FontSize="{TemplateBinding FontSize}"
+ FontStretch="{TemplateBinding FontStretch}"
+ TextAlignment="{TemplateBinding TextAlignment}"
+ FontWeight="{TemplateBinding FontWeight}"
+ FontStyle="{TemplateBinding FontStyle}"
+ TextWrapping="{TemplateBinding TextWrapping}"
+ Text="{TemplateBinding Text}" />
+ </Grid>
+ </ControlTemplate>
+ </Setter.Value>
+ </Setter>
+ </Style>
+
+</ResourceDictionary>
diff --git a/Xamarin.Forms.Platform.WP8/WebViewRenderer.cs b/Xamarin.Forms.Platform.WP8/WebViewRenderer.cs
new file mode 100644
index 00000000..4b3151b0
--- /dev/null
+++ b/Xamarin.Forms.Platform.WP8/WebViewRenderer.cs
@@ -0,0 +1,166 @@
+using System;
+using System.ComponentModel;
+using System.IO;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Navigation;
+using Microsoft.Phone.Controls;
+
+namespace Xamarin.Forms.Platform.WinPhone
+{
+ public class WebViewRenderer : ViewRenderer<WebView, WebBrowser>, IWebViewRenderer
+ {
+ WebNavigationEvent _eventState;
+ bool _updating;
+
+ public async void LoadHtml(string html, string baseUrl)
+ {
+ string fileName = string.Format("formslocal_{0}.html", DateTime.Now.Ticks);
+ ;
+ await SaveToIsoStore(fileName, html);
+ Control.Navigate(new Uri(fileName, UriKind.Relative));
+ }
+
+ public void LoadUrl(string url)
+ {
+ Control.Source = new Uri(url, UriKind.RelativeOrAbsolute);
+ }
+
+ protected override void OnElementChanged(ElementChangedEventArgs<WebView> e)
+ {
+ base.OnElementChanged(e);
+
+ if (Control == null)
+ {
+ var webBrowser = new WebBrowser();
+ webBrowser.IsScriptEnabled = true;
+ webBrowser.Navigated += WebBrowserOnNavigated;
+ webBrowser.Navigating += WebBrowserOnNavigating;
+ webBrowser.NavigationFailed += WebBrowserOnNavigationFailed;
+ SetNativeControl(webBrowser);
+ }
+
+ if (e.OldElement != null)
+ {
+ e.OldElement.EvalRequested -= OnEvalRequested;
+ e.OldElement.GoBackRequested -= OnGoBackRequested;
+ e.OldElement.GoForwardRequested -= OnGoForwardRequested;
+ Control.DataContext = null;
+ }
+
+ if (e.NewElement != null)
+ {
+ e.NewElement.EvalRequested += OnEvalRequested;
+ e.NewElement.GoBackRequested += OnGoBackRequested;
+ e.NewElement.GoForwardRequested += OnGoForwardRequested;
+ Control.DataContext = e.NewElement;
+ }
+
+ Load();
+ }
+
+ protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ base.OnElementPropertyChanged(sender, e);
+
+ switch (e.PropertyName)
+ {
+ case "Source":
+ if (!_updating)
+ Load();
+ break;
+ }
+ }
+
+ void Load()
+ {
+ if (Element.Source != null)
+ Element.Source.Load(this);
+
+ UpdateCanGoBackForward();
+ }
+
+ void OnEvalRequested(object sender, EventArg<string> eventArg)
+ {
+ Control.Dispatcher.BeginInvoke(() => Control.InvokeScript("eval", 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();
+ }
+
+ async Task SaveToIsoStore(string fileName, string html)
+ {
+ IIsolatedStorageFile store = Device.PlatformServices.GetUserStoreForApplication();
+ using(Stream file = await store.OpenFileAsync(fileName, FileMode.CreateNew, FileAccess.Write).ConfigureAwait(false))
+ {
+ byte[] bytes = Encoding.UTF8.GetBytes(html);
+ await file.WriteAsync(bytes, 0, bytes.Length).ConfigureAwait(false);
+ }
+ }
+
+ 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;
+ }
+
+ void WebBrowserOnNavigated(object sender, System.Windows.Navigation.NavigationEventArgs navigationEventArgs)
+ {
+ string url = navigationEventArgs.Uri.IsAbsoluteUri ? navigationEventArgs.Uri.AbsoluteUri : navigationEventArgs.Uri.OriginalString;
+ SendNavigated(new UrlWebViewSource { Url = url }, _eventState, WebNavigationResult.Success);
+
+ UpdateCanGoBackForward();
+ }
+
+ void WebBrowserOnNavigating(object sender, NavigatingEventArgs navigatingEventArgs)
+ {
+ string url = navigatingEventArgs.Uri.IsAbsoluteUri ? navigatingEventArgs.Uri.AbsoluteUri : navigatingEventArgs.Uri.OriginalString;
+ var args = new WebNavigatingEventArgs(_eventState, new UrlWebViewSource { Url = url }, url);
+
+ Element.SendNavigating(args);
+
+ navigatingEventArgs.Cancel = args.Cancel;
+
+ // reset in this case because this is the last event we will get
+ if (args.Cancel)
+ _eventState = WebNavigationEvent.NewPage;
+ }
+
+ void WebBrowserOnNavigationFailed(object sender, NavigationFailedEventArgs navigationFailedEventArgs)
+ {
+ string url = navigationFailedEventArgs.Uri.IsAbsoluteUri ? navigationFailedEventArgs.Uri.AbsoluteUri : navigationFailedEventArgs.Uri.OriginalString;
+ SendNavigated(new UrlWebViewSource { Url = url }, _eventState, WebNavigationResult.Failure);
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.WP8/WinPhoneTicker.cs b/Xamarin.Forms.Platform.WP8/WinPhoneTicker.cs
new file mode 100644
index 00000000..94b17246
--- /dev/null
+++ b/Xamarin.Forms.Platform.WP8/WinPhoneTicker.cs
@@ -0,0 +1,26 @@
+using System;
+using System.Windows.Threading;
+
+namespace Xamarin.Forms
+{
+ internal class WinPhoneTicker : Ticker
+ {
+ readonly DispatcherTimer _timer;
+
+ public WinPhoneTicker()
+ {
+ _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.WP8/Xamarin.Forms.Platform.WP8.csproj b/Xamarin.Forms.Platform.WP8/Xamarin.Forms.Platform.WP8.csproj
new file mode 100644
index 00000000..29ebeae0
--- /dev/null
+++ b/Xamarin.Forms.Platform.WP8/Xamarin.Forms.Platform.WP8.csproj
@@ -0,0 +1,245 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <ProductVersion>10.0.20506</ProductVersion>
+ <SchemaVersion>2.0</SchemaVersion>
+ <ProjectGuid>{517B6AE0-792B-4665-9376-5CA33E539181}</ProjectGuid>
+ <ProjectTypeGuids>{C089C8C0-30E0-4E22-80C0-CE093F111A43};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}</ProjectTypeGuids>
+ <OutputType>Library</OutputType>
+ <AppDesignerFolder>Properties</AppDesignerFolder>
+ <RootNamespace>Xamarin.Forms.Platform.WinPhone</RootNamespace>
+ <AssemblyName>Xamarin.Forms.Platform.WP8</AssemblyName>
+ <TargetFrameworkIdentifier>WindowsPhone</TargetFrameworkIdentifier>
+ <TargetFrameworkVersion>v8.0</TargetFrameworkVersion>
+ <SilverlightVersion>$(TargetFrameworkVersion)</SilverlightVersion>
+ <SilverlightApplication>false</SilverlightApplication>
+ <ValidateXaml>true</ValidateXaml>
+ <MinimumVisualStudioVersion>11.0</MinimumVisualStudioVersion>
+ <ThrowErrorsInValidation>true</ThrowErrorsInValidation>
+ <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
+ <RestorePackages>true</RestorePackages>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+ <DebugSymbols>true</DebugSymbols>
+ <DebugType>full</DebugType>
+ <Optimize>false</Optimize>
+ <OutputPath>Bin\Debug</OutputPath>
+ <DefineConstants>DEBUG;TRACE;SILVERLIGHT;WINDOWS_PHONE</DefineConstants>
+ <NoStdLib>true</NoStdLib>
+ <NoConfig>true</NoConfig>
+ <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;SILVERLIGHT;WINDOWS_PHONE</DefineConstants>
+ <NoStdLib>true</NoStdLib>
+ <NoConfig>true</NoConfig>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
+ <DebugSymbols>true</DebugSymbols>
+ <DebugType>full</DebugType>
+ <Optimize>false</Optimize>
+ <OutputPath>Bin\x86\Debug</OutputPath>
+ <DefineConstants>DEBUG;TRACE;SILVERLIGHT;WINDOWS_PHONE</DefineConstants>
+ <NoStdLib>true</NoStdLib>
+ <NoConfig>true</NoConfig>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
+ <DebugType>pdbonly</DebugType>
+ <Optimize>true</Optimize>
+ <OutputPath>Bin\x86\Release</OutputPath>
+ <DefineConstants>TRACE;SILVERLIGHT;WINDOWS_PHONE</DefineConstants>
+ <NoStdLib>true</NoStdLib>
+ <NoConfig>true</NoConfig>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|ARM' ">
+ <DebugSymbols>true</DebugSymbols>
+ <DebugType>full</DebugType>
+ <Optimize>false</Optimize>
+ <OutputPath>Bin\ARM\Debug</OutputPath>
+ <DefineConstants>DEBUG;TRACE;SILVERLIGHT;WINDOWS_PHONE</DefineConstants>
+ <NoStdLib>true</NoStdLib>
+ <NoConfig>true</NoConfig>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|ARM' ">
+ <DebugType>pdbonly</DebugType>
+ <Optimize>true</Optimize>
+ <OutputPath>Bin\ARM\Release</OutputPath>
+ <DefineConstants>TRACE;SILVERLIGHT;WINDOWS_PHONE</DefineConstants>
+ <NoStdLib>true</NoStdLib>
+ <NoConfig>true</NoConfig>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Turkey|AnyCPU'">
+ <DebugSymbols>true</DebugSymbols>
+ <OutputPath>bin\Turkey\</OutputPath>
+ <DefineConstants>DEBUG;TRACE;SILVERLIGHT;WINDOWS_PHONE</DefineConstants>
+ <NoStdLib>true</NoStdLib>
+ <DebugType>full</DebugType>
+ <PlatformTarget>AnyCPU</PlatformTarget>
+ <ErrorReport>prompt</ErrorReport>
+ <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Turkey|x86'">
+ <DebugSymbols>true</DebugSymbols>
+ <OutputPath>bin\x86\Turkey\</OutputPath>
+ <DefineConstants>DEBUG;TRACE;SILVERLIGHT;WINDOWS_PHONE</DefineConstants>
+ <NoStdLib>true</NoStdLib>
+ <DebugType>full</DebugType>
+ <PlatformTarget>AnyCPU</PlatformTarget>
+ <ErrorReport>prompt</ErrorReport>
+ <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Turkey|ARM'">
+ <DebugSymbols>true</DebugSymbols>
+ <OutputPath>bin\ARM\Turkey\</OutputPath>
+ <DefineConstants>DEBUG;TRACE;SILVERLIGHT;WINDOWS_PHONE</DefineConstants>
+ <NoStdLib>true</NoStdLib>
+ <DebugType>full</DebugType>
+ <PlatformTarget>AnyCPU</PlatformTarget>
+ <ErrorReport>prompt</ErrorReport>
+ <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
+ </PropertyGroup>
+ <ItemGroup>
+ <Compile Include="..\Xamarin.Forms.Core\Properties\GlobalAssemblyInfo.cs">
+ <Link>Properties\GlobalAssemblyInfo.cs</Link>
+ </Compile>
+ <Compile Include="ActivityIndicatorRenderer.cs" />
+ <Compile Include="AlignmentExtensions.cs" />
+ <Compile Include="Animatable.cs" />
+ <Compile Include="AsyncValue.cs" />
+ <Compile Include="CellControl.cs" />
+ <Compile Include="CollapseWhenEmptyConverter.cs" />
+ <Compile Include="Deserializer.cs" />
+ <Compile Include="CustomContextMenu.cs" />
+ <Compile Include="ElementChangedEventArgs.cs" />
+ <Compile Include="ExportCellAttribute.cs" />
+ <Compile Include="ExportImageSourceHandlerAttribute.cs" />
+ <Compile Include="ExportRendererAttribute.cs" />
+ <Compile Include="Extensions.cs" />
+ <Compile Include="FontExtensions.cs" />
+ <Compile Include="Forms.cs" />
+ <Compile Include="FormsApplicationPage.cs" />
+ <Compile Include="FormsListPicker.cs" />
+ <Compile Include="FormsPhoneTextBox.cs" />
+ <Compile Include="FrameworkElementExtensions.cs" />
+ <Compile Include="LayoutExtensions.cs" />
+ <Compile Include="CarouselViewRenderer.cs" />
+ <Compile Include="MD5.cs" />
+ <Compile Include="MD5CryptoServiceProvider.cs" />
+ <Compile Include="NativeViewWrapper.cs" />
+ <Compile Include="NativeViewWrapperRenderer.cs" />
+ <Compile Include="PageToRendererConverter.cs" />
+ <Compile Include="PlatformEffect.cs" />
+ <Compile Include="Properties\AssemblyInfo.cs" />
+ <Compile Include="BoxViewRenderer.cs" />
+ <Compile Include="ButtonRenderer.cs" />
+ <Compile Include="CarouselPageRenderer.cs" />
+ <Compile Include="Converters\CaseConverter.cs" />
+ <Compile Include="Converters\ColorConverter.cs" />
+ <Compile Include="Converters\ImageConverter.cs" />
+ <Compile Include="Converters\KeyboardConverter.cs" />
+ <Compile Include="Converters\HorizontalTextAlignmentConverter.cs" />
+ <Compile Include="CellTemplateSelector.cs" />
+ <Compile Include="ConvertExtensions.cs" />
+ <Compile Include="DataTemplateSelector.cs" />
+ <Compile Include="DatePickerRenderer.cs" />
+ <Compile Include="EditorRenderer.cs" />
+ <Compile Include="EntryRenderer.cs" />
+ <Compile Include="FrameRenderer.cs" />
+ <Compile Include="ICellRenderer.cs" />
+ <Compile Include="ImageRenderer.cs" />
+ <Compile Include="IVisualElementRenderer.cs" />
+ <Compile Include="LabelRenderer.cs" />
+ <Compile Include="ListViewRenderer.cs" />
+ <Compile Include="MasterDetailRenderer.cs" />
+ <Compile Include="NavigationMenuRenderer.cs" />
+ <Compile Include="NavigationPageRenderer.cs" />
+ <Compile Include="PageRenderer.cs" />
+ <Compile Include="Platform.cs" />
+ <Compile Include="ProgressBarRenderer.cs" />
+ <Compile Include="RendererFactory.cs" />
+ <Compile Include="ResourcesProvider.cs" />
+ <Compile Include="ScrollViewRenderer.cs" />
+ <Compile Include="SearchBarRenderer.cs" />
+ <Compile Include="SliderRenderer.cs" />
+ <Compile Include="StepperRenderer.cs" />
+ <Compile Include="SwitchRenderer.cs" />
+ <Compile Include="TabbedPageRenderer.cs" />
+ <Compile Include="TableView.xaml.cs">
+ <DependentUpon>TableView.xaml</DependentUpon>
+ </Compile>
+ <Compile Include="TableViewRenderer.cs" />
+ <Compile Include="TextAlignmentToHorizontalAlignmentConverter.cs" />
+ <Compile Include="TextCellRenderer.cs" />
+ <Compile Include="TimePickerRenderer.cs" />
+ <Compile Include="ViewExtensions.cs" />
+ <Compile Include="VisualElementPackager.cs" />
+ <Compile Include="ViewRenderer.cs" />
+ <Compile Include="ViewToRendererConverter.cs" />
+ <Compile Include="VisualElementRenderer.cs" />
+ <Compile Include="VisualElementTracker.cs" />
+ <Compile Include="WebViewRenderer.cs" />
+ <Compile Include="PickerRenderer.cs" />
+ <Compile Include="WinPhoneTicker.cs" />
+ <Compile Include="WP8PlatformServices.cs" />
+ </ItemGroup>
+ <ItemGroup>
+ <Reference Include="Microsoft.Phone.Controls, Version=8.0.0.0, Culture=neutral, PublicKeyToken=24eec0d8c86cda1e, processorArchitecture=MSIL" />
+ <Reference Include="Microsoft.Phone.Controls.Toolkit">
+ <HintPath>..\packages\WPtoolkit.4.2013.08.16\lib\wp8\Microsoft.Phone.Controls.Toolkit.dll</HintPath>
+ </Reference>
+ <Reference Include="System.Windows.Interactivity, Version=3.9.5.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL" />
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="packages.config" />
+ <Content Include="ServiceReferences.ClientConfig" />
+ </ItemGroup>
+ <ItemGroup>
+ <Content Include="Toolkit.Content\ApplicationBar.Cancel.png" />
+ <Content Include="Toolkit.Content\ApplicationBar.Check.png" />
+ <Content Include="Toolkit.Content\ApplicationBar.Delete.png" />
+ <Content Include="Toolkit.Content\ApplicationBar.Select.png" />
+ </ItemGroup>
+ <ItemGroup>
+ <Page Include="TableView.xaml">
+ <Generator>MSBuild:Compile</Generator>
+ <SubType>Designer</SubType>
+ </Page>
+ <Page Include="WPResources.xaml">
+ <Generator>MSBuild:Compile</Generator>
+ <SubType>Designer</SubType>
+ </Page>
+ </ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="..\Xamarin.Forms.Core\Xamarin.Forms.Core.csproj">
+ <Project>{57B8B73D-C3B5-4C42-869E-7B2F17D354AC}</Project>
+ <Name>Xamarin.Forms.Core</Name>
+ </ProjectReference>
+ </ItemGroup>
+ <Import Project="$(MSBuildExtensionsPath)\Microsoft\$(TargetFrameworkIdentifier)\$(TargetFrameworkVersion)\Microsoft.$(TargetFrameworkIdentifier).$(TargetFrameworkVersion).Overrides.targets" />
+ <Import Project="$(MSBuildExtensionsPath)\Microsoft\$(TargetFrameworkIdentifier)\$(TargetFrameworkVersion)\Microsoft.$(TargetFrameworkIdentifier).CSharp.targets" />
+ <ProjectExtensions />
+ <Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.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
diff --git a/Xamarin.Forms.Platform.WP8/packages.config b/Xamarin.Forms.Platform.WP8/packages.config
new file mode 100644
index 00000000..d534d6e7
--- /dev/null
+++ b/Xamarin.Forms.Platform.WP8/packages.config
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<packages>
+ <package id="WPtoolkit" version="4.2013.08.16" targetFramework="wp80" />
+</packages>