Skip to content

Commit

Permalink
Fix renderer shim ordering when creating native renderer (#1105)
Browse files Browse the repository at this point in the history
* Set Renderer before setting virtual view

* - fix NET6 targets
  • Loading branch information
PureWeen authored May 28, 2021
1 parent 1f18df6 commit 61fa76f
Show file tree
Hide file tree
Showing 10 changed files with 176 additions and 280 deletions.
4 changes: 4 additions & 0 deletions src/Compatibility/Core/src/Compatibility-net6.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
</Page>
<Page Remove="$(WindowsRoot)TabbedPageStyle.xaml" />
<ProjectReference Include="..\..\..\Controls\src\Xaml\Controls.Xaml-net6.csproj" />
<Compile Include="RendererToHandlerShim.Windows.cs" />
</ItemGroup>

<ItemGroup Condition=" '$(TargetPlatformIdentifier)' == 'android' ">
Expand All @@ -66,15 +67,18 @@
<Compile Remove="$(AndroidRoot)AppCompat\Resource.cs" />
<Compile Remove="$(AndroidRoot)bin\**\*.cs" />
<Compile Remove="$(AndroidRoot)obj\**\*.cs" />
<Compile Include="RendererToHandlerShim.Android.cs" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetPlatformIdentifier)' == 'ios' OR '$(TargetPlatformIdentifier)' == 'maccatalyst' ">
<Compile Include="$(iOSRoot)**\*.cs"></Compile>
<EmbeddedResource Include="$(iOSRoot)Resources\*.resx" />
<Compile Remove="$(iOSRoot)bin\**\*.cs" />
<Compile Remove="$(iOSRoot)obj\**\*.cs" />
<Compile Include="RendererToHandlerShim.iOS.cs" />
</ItemGroup>

<ItemGroup>
<Compile Include="RendererToHandlerShim.cs" />
<Compile Include="AppHostBuilderExtensions.cs" />
<Compile Include="MauiHandlersCollectionExtensions.cs" />
<Compile Include="Forms.cs" />
Expand Down
3 changes: 3 additions & 0 deletions src/Compatibility/Core/src/Compatibility.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
<Compile Remove="$(AndroidRoot)AppCompat\Resource.cs" />
<Compile Remove="$(AndroidRoot)bin\**\*.cs" />
<Compile Remove="$(AndroidRoot)obj\**\*.cs" />
<Compile Include="RendererToHandlerShim.Android.cs" />
</ItemGroup>
<ItemGroup Condition=" $(TargetFramework.StartsWith('Xamarin.iOS')) ">
<Compile Include="$(iOSRoot)**\*.cs"></Compile>
Expand All @@ -55,13 +56,15 @@
<Reference Include="System.Xml" />
<Reference Include="System.Core" />
<Reference Include="System.Net.Http" />
<Compile Include="RendererToHandlerShim.iOS.cs" />
</ItemGroup>

<ItemGroup>
<Compile Include="AppHostBuilderExtensions.cs" />
<Compile Include="MauiHandlersCollectionExtensions.cs" />
<Compile Include="Forms.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="RendererToHandlerShim.cs" />
</ItemGroup>

<ItemGroup>
Expand Down
48 changes: 48 additions & 0 deletions src/Compatibility/Core/src/RendererToHandlerShim.Android.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
using System;
using System.Collections.Generic;
using System.Text;
using Android.Views;
using Microsoft.Maui.Controls.Compatibility.Platform.Android;
using Microsoft.Maui.Graphics;
using static Microsoft.Maui.Controls.Compatibility.Platform.Android.AppCompat.Platform;
using NativeView = Android.Views.View;

namespace Microsoft.Maui.Controls.Compatibility
{
public partial class RendererToHandlerShim
{
protected override NativeView CreateNativeView()
{
return VisualElementRenderer.View;
}

IVisualElementRenderer CreateRenderer(IView view)
{
if (Context != null)
{
var renderer = Internals.Registrar.Registered.GetHandlerForObject<IVisualElementRenderer>(view, Context)
?? new DefaultRenderer(Context);
return renderer;
}

return null;
}

public override Size GetDesiredSize(double widthConstraint, double heightConstraint)
{
return GetNativeSize(
VisualElementRenderer, widthConstraint, heightConstraint);
}

public override void NativeArrange(Rectangle frame)
{
// This is a hack to force the shimmed control to actually do layout; without this, some controls won't actually
// call OnLayout after SetFrame if their sizes haven't changed (e.g., ScrollView)
// Luckily, measuring with MeasureSpecMode.Exactly is pretty fast, since it just returns the value you give it.
NativeView?.Measure(MeasureSpecMode.Exactly.MakeMeasureSpec((int)frame.Width),
MeasureSpecMode.Exactly.MakeMeasureSpec((int)frame.Height));

base.NativeArrange(frame);
}
}
}
31 changes: 31 additions & 0 deletions src/Compatibility/Core/src/RendererToHandlerShim.Windows.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using System;
using System.Collections.Generic;
using System.Text;
using NativeView = Microsoft.UI.Xaml.FrameworkElement;
using Microsoft.Maui.Controls.Compatibility.Platform.UWP;
using Microsoft.Maui.Graphics;

namespace Microsoft.Maui.Controls.Compatibility
{
public partial class RendererToHandlerShim
{
protected override NativeView CreateNativeView()
{
return VisualElementRenderer.ContainerElement;
}

IVisualElementRenderer CreateRenderer(IView view)
{
return Internals.Registrar.Registered.GetHandlerForObject<IVisualElementRenderer>(view)
?? new DefaultRenderer();
}

public override Size GetDesiredSize(double widthConstraint, double heightConstraint)
{
if (widthConstraint < 0 || heightConstraint < 0)
return Size.Zero;

return VisualElementRenderer.GetDesiredSize(widthConstraint, heightConstraint);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,35 @@
using System;
using Android.Views;
using Microsoft.Maui.Graphics;
#if __ANDROID__
using static Microsoft.Maui.Controls.Compatibility.Platform.Android.AppCompat.Platform;
using NativeView = Android.Views.View;
using IVisualElementRenderer = Microsoft.Maui.Controls.Compatibility.Platform.Android.IVisualElementRenderer;
using ViewHandler = Microsoft.Maui.Handlers.ViewHandler<Microsoft.Maui.IView, Android.Views.View>;
using VisualElementChangedEventArgs = Microsoft.Maui.Controls.Compatibility.Platform.Android.VisualElementChangedEventArgs;
#elif __IOS__ || MACCATALYST
using static Microsoft.Maui.Controls.Compatibility.Platform.iOS.Platform;
using NativeView = UIKit.UIView;
using Microsoft.Maui.Controls.Compatibility.Platform.iOS;
using ViewHandler = Microsoft.Maui.Handlers.ViewHandler<Microsoft.Maui.IView, UIKit.UIView>;
#elif NETSTANDARD
using NativeView = System.Object;
using ViewHandler = Microsoft.Maui.Handlers.ViewHandler<Microsoft.Maui.IView, System.Object>;
#elif WINDOWS
using ViewHandler = Microsoft.Maui.Handlers.ViewHandler<Microsoft.Maui.IView, Microsoft.UI.Xaml.FrameworkElement>;
using NativeView = Microsoft.UI.Xaml.FrameworkElement;
using static Microsoft.Maui.Controls.Compatibility.Platform.UWP.Platform;
using Microsoft.Maui.Controls.Platform;
using Microsoft.Maui.Controls.Compatibility.Platform.UWP;
#endif

namespace Microsoft.Maui.Controls.Compatibility
{
public class RendererToHandlerShim : ViewHandler
public partial class RendererToHandlerShim : ViewHandler
{
public RendererToHandlerShim() : base(ViewHandler.ViewMapper)
{
}

#if __ANDROID__ || __IOS__ || WINDOWS
internal IVisualElementRenderer VisualElementRenderer { get; private set; }

public static IViewHandler CreateShim(object renderer)
Expand All @@ -22,10 +43,6 @@ public static IViewHandler CreateShim(object renderer)
return new RendererToHandlerShim();
}

public RendererToHandlerShim() : base(ViewHandler.ViewMapper)
{
}

public RendererToHandlerShim(IVisualElementRenderer visualElementRenderer) : this()
{
if (visualElementRenderer != null)
Expand All @@ -35,7 +52,6 @@ public RendererToHandlerShim(IVisualElementRenderer visualElementRenderer) : thi
public void SetupRenderer(IVisualElementRenderer visualElementRenderer)
{
VisualElementRenderer = visualElementRenderer;
VisualElementRenderer.ElementChanged += OnElementChanged;

if (VisualElementRenderer.Element is IView view)
{
Expand All @@ -44,6 +60,8 @@ public void SetupRenderer(IVisualElementRenderer visualElementRenderer)
}
else if (VisualElementRenderer.Element != null)
throw new Exception($"{VisualElementRenderer.Element} must implement: {nameof(Microsoft.Maui.IView)}");

VisualElementRenderer.ElementChanged += OnElementChanged;
}

void OnElementChanged(object sender, VisualElementChangedEventArgs e)
Expand All @@ -60,20 +78,15 @@ void OnElementChanged(object sender, VisualElementChangedEventArgs e)
throw new Exception($"{e.NewElement} must implement: {nameof(Microsoft.Maui.IView)}");
}

protected override global::Android.Views.View CreateNativeView()
{
return VisualElementRenderer.View;
}

protected override void ConnectHandler(global::Android.Views.View nativeView)
protected override void ConnectHandler(NativeView nativeView)
{
base.ConnectHandler(nativeView);
VirtualView.Handler = this;
}

protected override void DisconnectHandler(global::Android.Views.View nativeView)
protected override void DisconnectHandler(NativeView nativeView)
{
Platform.Android.AppCompat.Platform.SetRenderer(
SetRenderer(
VisualElementRenderer.Element,
null);

Expand All @@ -85,14 +98,15 @@ protected override void DisconnectHandler(global::Android.Views.View nativeView)

public override void SetVirtualView(IView view)
{
if (VisualElementRenderer == null && Context != null)
if (VisualElementRenderer == null)
{
var renderer = Internals.Registrar.Registered.GetHandlerForObject<IVisualElementRenderer>(view, Context)
?? new Platform.Android.AppCompat.Platform.DefaultRenderer(Context);

SetupRenderer(renderer);
SetupRenderer(CreateRenderer(view));
}

SetRenderer(
(VisualElement)view,
VisualElementRenderer);

if (VisualElementRenderer.Element != view)
{
VisualElementRenderer.SetElement((VisualElement)view);
Expand All @@ -101,27 +115,12 @@ public override void SetVirtualView(IView view)
{
base.SetVirtualView(view);
}

Platform.Android.AppCompat.Platform.SetRenderer(
VisualElementRenderer.Element,
VisualElementRenderer);
}

public override Size GetDesiredSize(double widthConstraint, double heightConstraint)
{
return Platform.Android.AppCompat.Platform.GetNativeSize(
VisualElementRenderer, widthConstraint, heightConstraint);
}

public override void NativeArrange(Rectangle frame)
#else
protected override NativeView CreateNativeView()
{
// This is a hack to force the shimmed control to actually do layout; without this, some controls won't actually
// call OnLayout after SetFrame if their sizes haven't changed (e.g., ScrollView)
// Luckily, measuring with MeasureSpecMode.Exactly is pretty fast, since it just returns the value you give it.
NativeView?.Measure(MeasureSpecMode.Exactly.MakeMeasureSpec((int)frame.Width),
MeasureSpecMode.Exactly.MakeMeasureSpec((int)frame.Height));

base.NativeArrange(frame);
throw new NotImplementedException();
}
#endif
}
}
32 changes: 32 additions & 0 deletions src/Compatibility/Core/src/RendererToHandlerShim.iOS.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Maui.Controls.Compatibility.Platform.iOS;
using static Microsoft.Maui.Controls.Compatibility.Platform.iOS.Platform;
using NativeView = UIKit.UIView;

namespace Microsoft.Maui.Controls.Compatibility
{
public partial class RendererToHandlerShim
{
protected override NativeView CreateNativeView()
{
return VisualElementRenderer.NativeView;
}

IVisualElementRenderer CreateRenderer(IView view)
{
return Internals.Registrar.Registered.GetHandlerForObject<IVisualElementRenderer>(view)
?? new DefaultRenderer();
}

public override void UpdateValue(string property)
{
base.UpdateValue(property);
if (property == "Frame")
{
NativeArrange(VisualElementRenderer.Element.Bounds);
}
}
}
}
Loading

0 comments on commit 61fa76f

Please sign in to comment.