[Win] Will arrange native children of custom renderers (opt-in) (#322)
authorSamantha Houts <samantha@teamredwall.com>
Tue, 27 Sep 2016 18:12:49 +0000 (11:12 -0700)
committerJason Smith <jason.smith@xamarin.com>
Tue, 27 Sep 2016 18:12:49 +0000 (11:12 -0700)
* Add repro for 42602

* [Win] Add option to arrange native children

* [Win] Don't allocate arrangedChildren unless required

Xamarin.Forms.ControlGallery.Windows/CustomRenderers.cs [new file with mode: 0644]
Xamarin.Forms.ControlGallery.Windows/Xamarin.Forms.ControlGallery.Windows.csproj
Xamarin.Forms.ControlGallery.WindowsPhone/CustomRenderers.cs [new file with mode: 0644]
Xamarin.Forms.ControlGallery.WindowsPhone/Xamarin.Forms.ControlGallery.WindowsPhone.csproj
Xamarin.Forms.ControlGallery.WindowsUniversal/CustomRenderers.cs [new file with mode: 0644]
Xamarin.Forms.ControlGallery.WindowsUniversal/Xamarin.Forms.ControlGallery.WindowsUniversal.csproj
Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Bugzilla42602.cs [new file with mode: 0644]
Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Xamarin.Forms.Controls.Issues.Shared.projitems
Xamarin.Forms.Platform.WinRT/VisualElementRenderer.cs

diff --git a/Xamarin.Forms.ControlGallery.Windows/CustomRenderers.cs b/Xamarin.Forms.ControlGallery.Windows/CustomRenderers.cs
new file mode 100644 (file)
index 0000000..391aed2
--- /dev/null
@@ -0,0 +1,57 @@
+using Windows.UI.Xaml.Controls;
+using Windows.UI.Xaml.Media;
+using Windows.UI.Xaml.Shapes;
+using Xamarin.Forms.Platform.WinRT;
+
+[assembly: ExportRenderer(typeof(Xamarin.Forms.Controls.Bugzilla42602.TextBoxView), typeof(Xamarin.Forms.ControlGallery.Windows.TextBoxViewRenderer))]
+namespace Xamarin.Forms.ControlGallery.Windows
+{
+       public class TextBoxViewRenderer : BoxViewRenderer
+       {
+               Canvas m_Canvas;
+
+               protected override void OnElementChanged(ElementChangedEventArgs<BoxView> e)
+               {
+                       base.OnElementChanged(e);
+
+                       ArrangeNativeChildren = true;
+
+                       if (m_Canvas != null)
+                               Children.Remove(m_Canvas);
+
+                       m_Canvas = new Canvas()
+                       {
+                               Width = 200,
+                               Height = 200,
+                               Background = new SolidColorBrush(global::Windows.UI.Color.FromArgb(0, 255, 255, 255)),
+                               IsHitTestVisible = false
+                       };
+
+                       Children.Add(m_Canvas);
+
+                       //ellipse
+                       Shape ellipse = new Ellipse()
+                       {
+                               Width = 100,
+                               Height = 100,
+                               Fill = new SolidColorBrush(global::Windows.UI.Color.FromArgb(255, 255, 0, 0)),
+
+                       };
+                       Canvas.SetLeft(ellipse, 0);
+                       Canvas.SetTop(ellipse, 0);
+                       m_Canvas.Children.Add(ellipse);
+
+                       //text
+                       TextBlock text = new TextBlock()
+                       {
+                               FontSize = 50,
+                               FontWeight = global::Windows.UI.Text.FontWeights.Normal,
+                               Text = "hello world",
+                               Foreground = new SolidColorBrush(global::Windows.UI.Color.FromArgb(255, 255, 0, 0))
+                       };
+                       Canvas.SetLeft(text, 0);
+                       Canvas.SetTop(text, 150);
+                       m_Canvas.Children.Add(text);
+               }
+       }
+}
index aa8f862..6252820 100644 (file)
     </Compile>
     <Compile Include="BrokenImageSourceHandler.cs" />
     <Compile Include="BrokenNativeControl.cs" />
+    <Compile Include="CustomRenderers.cs" />
     <Compile Include="MainPage.xaml.cs">
       <DependentUpon>MainPage.xaml</DependentUpon>
     </Compile>
diff --git a/Xamarin.Forms.ControlGallery.WindowsPhone/CustomRenderers.cs b/Xamarin.Forms.ControlGallery.WindowsPhone/CustomRenderers.cs
new file mode 100644 (file)
index 0000000..2f6cae9
--- /dev/null
@@ -0,0 +1,57 @@
+using Windows.UI.Xaml.Controls;
+using Windows.UI.Xaml.Media;
+using Windows.UI.Xaml.Shapes;
+using Xamarin.Forms.Platform.WinRT;
+
+[assembly: ExportRenderer(typeof(Xamarin.Forms.Controls.Bugzilla42602.TextBoxView), typeof(Xamarin.Forms.ControlGallery.WindowsPhone.TextBoxViewRenderer))]
+namespace Xamarin.Forms.ControlGallery.WindowsPhone
+{
+       public class TextBoxViewRenderer : BoxViewRenderer
+       {
+               Canvas m_Canvas;
+
+               protected override void OnElementChanged(ElementChangedEventArgs<BoxView> e)
+               {
+                       base.OnElementChanged(e);
+
+                       ArrangeNativeChildren = true;
+
+                       if (m_Canvas != null)
+                               Children.Remove(m_Canvas);
+
+                       m_Canvas = new Canvas()
+                       {
+                               Width = 200,
+                               Height = 200,
+                               Background = new SolidColorBrush(global::Windows.UI.Color.FromArgb(0, 255, 255, 255)),
+                               IsHitTestVisible = false
+                       };
+
+                       Children.Add(m_Canvas);
+
+                       //ellipse
+                       Shape ellipse = new Ellipse()
+                       {
+                               Width = 100,
+                               Height = 100,
+                               Fill = new SolidColorBrush(global::Windows.UI.Color.FromArgb(255, 255, 0, 0)),
+
+                       };
+                       Canvas.SetLeft(ellipse, 0);
+                       Canvas.SetTop(ellipse, 0);
+                       m_Canvas.Children.Add(ellipse);
+
+                       //text
+                       TextBlock text = new TextBlock()
+                       {
+                               FontSize = 50,
+                               FontWeight = global::Windows.UI.Text.FontWeights.Normal,
+                               Text = "hello world",
+                               Foreground = new SolidColorBrush(global::Windows.UI.Color.FromArgb(255, 255, 0, 0))
+                       };
+                       Canvas.SetLeft(text, 0);
+                       Canvas.SetTop(text, 150);
+                       m_Canvas.Children.Add(text);
+               }
+       }
+}
index a856543..349b0b4 100644 (file)
       <DependentUpon>App.xaml</DependentUpon>
     </Compile>
     <Compile Include="BrokenNativeControl.cs" />
+    <Compile Include="CustomRenderers.cs" />
     <Compile Include="MainPage.xaml.cs">
       <DependentUpon>MainPage.xaml</DependentUpon>
     </Compile>
diff --git a/Xamarin.Forms.ControlGallery.WindowsUniversal/CustomRenderers.cs b/Xamarin.Forms.ControlGallery.WindowsUniversal/CustomRenderers.cs
new file mode 100644 (file)
index 0000000..8d937d4
--- /dev/null
@@ -0,0 +1,57 @@
+using Windows.UI.Xaml.Controls;
+using Windows.UI.Xaml.Media;
+using Windows.UI.Xaml.Shapes;
+using Xamarin.Forms.Platform.UWP;
+
+[assembly: ExportRenderer(typeof(Xamarin.Forms.Controls.Bugzilla42602.TextBoxView), typeof(Xamarin.Forms.ControlGallery.WindowsUniversal.TextBoxViewRenderer))]
+namespace Xamarin.Forms.ControlGallery.WindowsUniversal
+{
+       public class TextBoxViewRenderer : BoxViewRenderer
+       {
+               Canvas m_Canvas;
+
+               protected override void OnElementChanged(ElementChangedEventArgs<BoxView> e)
+               {
+                       base.OnElementChanged(e);
+
+                       ArrangeNativeChildren = true;
+
+                       if (m_Canvas != null)
+                               Children.Remove(m_Canvas);
+
+                       m_Canvas = new Canvas()
+                       {
+                               Width = 200,
+                               Height = 200,
+                               Background = new SolidColorBrush(Windows.UI.Color.FromArgb(0, 255, 255, 255)),
+                               IsHitTestVisible = false
+                       };
+
+                       Children.Add(m_Canvas);
+
+                       //ellipse
+                       Shape ellipse = new Ellipse()
+                       {
+                               Width = 100,
+                               Height = 100,
+                               Fill = new SolidColorBrush(Windows.UI.Color.FromArgb(255, 255, 0, 0)),
+
+                       };
+                       Canvas.SetLeft(ellipse, 0);
+                       Canvas.SetTop(ellipse, 0);
+                       m_Canvas.Children.Add(ellipse);
+
+                       //text
+                       TextBlock text = new TextBlock()
+                       {
+                               FontSize = 50,
+                               FontWeight = Windows.UI.Text.FontWeights.Normal,
+                               Text = "hello world",
+                               Foreground = new SolidColorBrush(Windows.UI.Color.FromArgb(255, 255, 0, 0))
+                       };
+                       Canvas.SetLeft(text, 0);
+                       Canvas.SetTop(text, 150);
+                       m_Canvas.Children.Add(text);
+               }
+       }
+}
index 58ad67b..7e1847e 100644 (file)
       <DependentUpon>App.xaml</DependentUpon>
     </Compile>
     <Compile Include="BrokenNativeControl.cs" />
+    <Compile Include="CustomRenderers.cs" />
     <Compile Include="MainPage.xaml.cs">
       <DependentUpon>MainPage.xaml</DependentUpon>
     </Compile>
diff --git a/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Bugzilla42602.cs b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Bugzilla42602.cs
new file mode 100644 (file)
index 0000000..05e4101
--- /dev/null
@@ -0,0 +1,39 @@
+using System;
+
+using Xamarin.Forms.CustomAttributes;
+using Xamarin.Forms.Internals;
+
+namespace Xamarin.Forms.Controls
+{
+       [Preserve(AllMembers = true)]
+       [Issue(IssueTracker.Bugzilla, 42602, "[Win] Custom BoxView Renderer Does Not Render All Its Children Elements", PlatformAffected.WinRT)]
+       public class Bugzilla42602 : TestContentPage
+       {
+               AbsoluteLayout content;
+
+               protected override void Init()
+               {
+                       //background white 800 x 600 square
+                       content = new AbsoluteLayout()
+                       {
+                               BackgroundColor = Color.White,
+                               WidthRequest = 800,
+                               HeightRequest = 800,
+                               VerticalOptions = LayoutOptions.CenterAndExpand,
+                               HorizontalOptions = LayoutOptions.CenterAndExpand
+                       };
+
+                       //test TextBoxView 400 x 400, color gray, should have a red ellipse and a red "hello world"
+
+                       var test = new TextBoxView() { WidthRequest = 300, HeightRequest = 300, BackgroundColor = Color.Blue };
+                       content.Children.Add(test, new Point((content.WidthRequest - test.WidthRequest) / 2f, (content.HeightRequest - test.HeightRequest) / 2f));
+
+                       Content = content;
+               }
+
+               public class TextBoxView : BoxView
+               {
+
+               }
+       }
+}
index c5f46d5..670f286 100644 (file)
     <Compile Include="$(MSBuildThisFileDirectory)ImageLoadingErrorHandling.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Bugzilla33561.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Bugzilla43214.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)Bugzilla42602.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Bugzilla43161.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Bugzilla41271.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)_Template.cs" />
index 3ad826b..b81f73e 100644 (file)
@@ -1,4 +1,5 @@
 using System;
+using System.Collections.Generic;
 using System.ComponentModel;
 using Windows.Foundation;
 using Windows.UI.Xaml;
@@ -28,6 +29,8 @@ namespace Xamarin.Forms.Platform.WinRT
 
                protected bool AutoTrack { get; set; } = true;
 
+               protected bool ArrangeNativeChildren { get; set; }
+
                IElementController ElementController => Element as IElementController;
 
                protected VisualElementTracker<TElement, TNativeElement> Tracker
@@ -160,11 +163,14 @@ namespace Xamarin.Forms.Platform.WinRT
 
                        Element.IsInNativeLayout = true;
 
+                       var myRect = new Rect(0, 0, finalSize.Width, finalSize.Height);
+
                        if (Control != null)
                        {
-                               Control.Arrange(new Rect(0, 0, finalSize.Width, finalSize.Height));
+                               Control.Arrange(myRect);
                        }
 
+                       List<UIElement> arrangedChildren = null;
                        for (var i = 0; i < ElementController.LogicalChildren.Count; i++)
                        {
                                var child = ElementController.LogicalChildren[i] as VisualElement;
@@ -176,6 +182,30 @@ namespace Xamarin.Forms.Platform.WinRT
                                Rectangle bounds = child.Bounds;
 
                                renderer.ContainerElement.Arrange(new Rect(bounds.X, bounds.Y, Math.Max(0, bounds.Width), Math.Max(0, bounds.Height)));
+
+                               if (ArrangeNativeChildren)
+                               {
+                                       if (arrangedChildren == null)
+                                               arrangedChildren = new List<UIElement>();
+                                       arrangedChildren.Add(renderer.ContainerElement);
+                               }
+                       }
+
+                       if (ArrangeNativeChildren)
+                       {
+                               // in the event that a custom renderer has added native controls,
+                               // we need to be sure to arrange them so that they are laid out.
+                               var nativeChildren = Children;
+                               for (int i = 0; i < nativeChildren.Count; i++)
+                               {
+                                       var nativeChild = nativeChildren[i];
+                                       if (arrangedChildren?.Contains(nativeChild) == true)
+                                               // don't try to rearrange renderers that were just arranged, 
+                                               // lest you suffer a layout cycle
+                                               continue;
+                                       else
+                                               nativeChild.Arrange(myRect);
+                               }
                        }
 
                        Element.IsInNativeLayout = false;