From e567ad224e3d837fcff9c1ed8b3552ce6b4ddbe2 Mon Sep 17 00:00:00 2001 From: Shane Neuville Date: Fri, 21 Jan 2022 13:58:02 -0600 Subject: [PATCH] Frame Compatibility Handlers (#4210) * Frame Compat Handlers * - gallery fixes * - fix up property change and autopackaging * - add mappers * - fix up frame renderer mappers * - fix WinUI Measure calls * - fix containerview return * - fix android measure * - fix layout call * - fix handler registrations * - clean up apphost registration * - disable swipeview on iOS for now * - fix iOS shadow * - revert mapper fix * - simplify containerview check Co-authored-by: Redth --- .../src/Android/_5724CustomRenderers.cs | 7 +- .../CustomRenderers/_9774CustomRenderer.cs | 2 +- .../_CustomFrame10348Renderer.cs | 2 +- .../src/iOS/PerformanceTrackerRenderer.cs | 2 +- .../src/Android/AppCompat/FrameRenderer.cs | 2 + .../Android/FastRenderers/FrameRenderer.cs | 1 + .../Core/src/AppHostBuilderExtensions.cs | 4 +- .../src/Handlers/Android/FrameRenderer.cs | 359 ++++++++++++++++++ .../Core/src/Handlers/Android/ViewRenderer.cs | 1 + .../Handlers/Android/VisualElementRenderer.cs | 25 +- .../ListView/Android/ListViewRenderer.cs | 1 + .../ListView/Windows/ListViewRenderer.cs | 1 + .../Handlers/ListView/iOS/ListViewRenderer.cs | 2 +- .../TableView/Android/TableViewRenderer.cs | 1 + .../TableView/Windows/TableViewRenderer.cs | 5 + .../TableView/iOS/TableViewRenderer.cs | 2 +- .../src/Handlers/VisualElementRenderer.cs | 81 ++-- .../src/Handlers/Windows/FrameRenderer.cs | 128 +++++++ .../Handlers/Windows/VisualElementRenderer.cs | 85 ++++- .../Core/src/Handlers/iOS/FrameRenderer.cs | 190 +++++++++ .../Core/src/Handlers/iOS/ViewRenderer.cs | 7 +- .../src/Handlers/iOS/VisualElementRenderer.cs | 25 +- .../Core/src/Windows/FrameRenderer.cs | 2 + .../Core/src/iOS/Renderers/FrameRenderer.cs | 4 +- .../Pages/Compatibility/FramePage.xaml | 6 +- .../Pages/CompatibilityPage.xaml | 2 +- .../src/Core/HandlerImpl/Frame.Impl.cs | 25 +- .../VisualElement/VisualElement.cs | 4 +- .../Core/Hosting/AppHostBuilderExtensions.cs | 15 +- src/Core/src/Platform/ElementExtensions.cs | 10 +- 30 files changed, 917 insertions(+), 84 deletions(-) create mode 100644 src/Compatibility/Core/src/Handlers/Android/FrameRenderer.cs create mode 100644 src/Compatibility/Core/src/Handlers/Windows/FrameRenderer.cs create mode 100644 src/Compatibility/Core/src/Handlers/iOS/FrameRenderer.cs diff --git a/src/Compatibility/ControlGallery/src/Android/_5724CustomRenderers.cs b/src/Compatibility/ControlGallery/src/Android/_5724CustomRenderers.cs index 70b027f077c5..694fe38b0805 100644 --- a/src/Compatibility/ControlGallery/src/Android/_5724CustomRenderers.cs +++ b/src/Compatibility/ControlGallery/src/Android/_5724CustomRenderers.cs @@ -54,12 +54,7 @@ protected override void OnElementPropertyChanged(object sender, PropertyChangedE } } - public class CustomFrameRenderer5724 : -#if !LEGACY_RENDERERS - Compatibility.Platform.Android.FastRenderers.FrameRenderer -#else - FrameRenderer -#endif + public class CustomFrameRenderer5724 : Handlers.Compatibility.FrameRenderer { public CustomFrameRenderer5724(Context context) : base(context) { diff --git a/src/Compatibility/ControlGallery/src/iOS/CustomRenderers/_9774CustomRenderer.cs b/src/Compatibility/ControlGallery/src/iOS/CustomRenderers/_9774CustomRenderer.cs index 2e5d17ce954f..eafdeb7200c1 100644 --- a/src/Compatibility/ControlGallery/src/iOS/CustomRenderers/_9774CustomRenderer.cs +++ b/src/Compatibility/ControlGallery/src/iOS/CustomRenderers/_9774CustomRenderer.cs @@ -10,7 +10,7 @@ [assembly: ExportRenderer(typeof(CustomFrame9974), typeof(_9774CustomRenderer))] namespace Microsoft.Maui.Controls.Compatibility.ControlGallery.iOS.CustomRenderers { - public class _9774CustomRenderer : FrameRenderer + public class _9774CustomRenderer : Handlers.Compatibility.FrameRenderer { public _9774CustomRenderer() { diff --git a/src/Compatibility/ControlGallery/src/iOS/CustomRenderers/_CustomFrame10348Renderer.cs b/src/Compatibility/ControlGallery/src/iOS/CustomRenderers/_CustomFrame10348Renderer.cs index c5f2fd7cb1d1..9a5e02764e72 100644 --- a/src/Compatibility/ControlGallery/src/iOS/CustomRenderers/_CustomFrame10348Renderer.cs +++ b/src/Compatibility/ControlGallery/src/iOS/CustomRenderers/_CustomFrame10348Renderer.cs @@ -11,7 +11,7 @@ [assembly: ExportRenderer(typeof(CustomFrame10348), typeof(_CustomFrame10348Renderer))] namespace Microsoft.Maui.Controls.Compatibility.ControlGallery.iOS.CustomRenderers { - public class _CustomFrame10348Renderer : FrameRenderer + public class _CustomFrame10348Renderer : Handlers.Compatibility.FrameRenderer { protected override void OnElementChanged(ElementChangedEventArgs e) { diff --git a/src/Compatibility/ControlGallery/src/iOS/PerformanceTrackerRenderer.cs b/src/Compatibility/ControlGallery/src/iOS/PerformanceTrackerRenderer.cs index 4712d26aba65..59472eca93fd 100644 --- a/src/Compatibility/ControlGallery/src/iOS/PerformanceTrackerRenderer.cs +++ b/src/Compatibility/ControlGallery/src/iOS/PerformanceTrackerRenderer.cs @@ -234,7 +234,7 @@ protected override void Dispose(bool disposing) } } - public class PerformanceTrackingFrame : FrameRenderer, IDrawnObservable + public class PerformanceTrackingFrame : Handlers.Compatibility.FrameRenderer, IDrawnObservable { readonly SubviewWatcher _watcher; int _Drawn; diff --git a/src/Compatibility/Core/src/Android/AppCompat/FrameRenderer.cs b/src/Compatibility/Core/src/Android/AppCompat/FrameRenderer.cs index 7993247bd7ae..7db98c722479 100644 --- a/src/Compatibility/Core/src/Android/AppCompat/FrameRenderer.cs +++ b/src/Compatibility/Core/src/Android/AppCompat/FrameRenderer.cs @@ -1,9 +1,11 @@ +using System; using Android.Content; namespace Microsoft.Maui.Controls.Compatibility.Platform.Android.AppCompat { // This version of FrameRenderer is here for backward compatibility with anyone referencing // FrameRenderer from this namespace + [Obsolete("Use Microsoft.Maui.Controls.Handlers.Compatibility.FrameRenderer instead")] public class FrameRenderer : FastRenderers.FrameRenderer { public FrameRenderer(Context context) : base(context) diff --git a/src/Compatibility/Core/src/Android/FastRenderers/FrameRenderer.cs b/src/Compatibility/Core/src/Android/FastRenderers/FrameRenderer.cs index a20ba2da0fc7..5d913aa5267d 100644 --- a/src/Compatibility/Core/src/Android/FastRenderers/FrameRenderer.cs +++ b/src/Compatibility/Core/src/Android/FastRenderers/FrameRenderer.cs @@ -14,6 +14,7 @@ namespace Microsoft.Maui.Controls.Compatibility.Platform.Android.FastRenderers { + [Obsolete("Use Microsoft.Maui.Controls.Handlers.Compatibility.FrameRenderer instead")] public class FrameRenderer : CardView, IVisualElementRenderer, IViewRenderer, ITabStop { float _defaultElevation = -1f; diff --git a/src/Compatibility/Core/src/AppHostBuilderExtensions.cs b/src/Compatibility/Core/src/AppHostBuilderExtensions.cs index 534c407de76f..7296c1414756 100644 --- a/src/Compatibility/Core/src/AppHostBuilderExtensions.cs +++ b/src/Compatibility/Core/src/AppHostBuilderExtensions.cs @@ -80,8 +80,8 @@ static MauiAppBuilder SetupDefaults(this MauiAppBuilder builder) handlers.AddHandler(typeof(ViewCell), typeof(Handlers.Compatibility.ViewCellRenderer)); handlers.AddHandler(typeof(SwitchCell), typeof(Handlers.Compatibility.SwitchCellRenderer)); handlers.AddHandler(typeof(TableView), typeof(Handlers.Compatibility.TableViewRenderer)); - - handlers.TryAddCompatibilityRenderer(typeof(Frame), typeof(FrameRenderer)); + handlers.AddHandler(typeof(Frame), typeof(Handlers.Compatibility.FrameRenderer)); + handlers.TryAddCompatibilityRenderer(typeof(BoxView), typeof(BoxRenderer)); handlers.TryAddCompatibilityRenderer(typeof(Entry), typeof(EntryRenderer)); handlers.TryAddCompatibilityRenderer(typeof(Editor), typeof(EditorRenderer)); diff --git a/src/Compatibility/Core/src/Handlers/Android/FrameRenderer.cs b/src/Compatibility/Core/src/Handlers/Android/FrameRenderer.cs new file mode 100644 index 000000000000..c8758e54b3cc --- /dev/null +++ b/src/Compatibility/Core/src/Handlers/Android/FrameRenderer.cs @@ -0,0 +1,359 @@ +#nullable enable + +using System; +using System.ComponentModel; +using Android.Content; +using Android.Graphics; +using Android.Graphics.Drawables; +using Android.Views; +using AndroidX.CardView.Widget; +using AndroidX.Core.View; +using Microsoft.Maui.Controls.Platform; +using Microsoft.Maui.Graphics; +using AColor = Android.Graphics.Color; +using AView = Android.Views.View; +using Color = Microsoft.Maui.Graphics.Color; +using ARect = Android.Graphics.Rect; + +namespace Microsoft.Maui.Controls.Handlers.Compatibility +{ + public class FrameRenderer : CardView, INativeViewHandler + { + public static IPropertyMapper Mapper + = new PropertyMapper(ViewRenderer.VisualElementRendererMapper) + { + [Frame.HasShadowProperty.PropertyName] = (h, _) => h.UpdateShadow(), + [VisualElement.BackgroundColorProperty.PropertyName] = (h, _) => h.UpdateBackgroundColor(), + [VisualElement.BackgroundProperty.PropertyName] = (h, _) => h.UpdateBackground(), + [Frame.CornerRadiusProperty.PropertyName] = (h, _) => h.UpdateCornerRadius(), + [Frame.BorderColorProperty.PropertyName] = (h, _) => h.UpdateBorderColor(), + [Microsoft.Maui.Controls.Compatibility.Layout.IsClippedToBoundsProperty.PropertyName] = (h, _) => h.UpdateClippedToBounds(), + [Frame.ContentProperty.PropertyName] = (h, _) => h.UpdateContent() + }; + + public static CommandMapper CommandMapper + = new CommandMapper(ViewRenderer.VisualElementRendererCommandMapper); + + + float _defaultElevation = -1f; + float _defaultCornerRadius = -1f; + + readonly ARect _clipRect = new(); + int _height; + int _width; + readonly Controls.Compatibility.Platform.Android.MotionEventHelper _motionEventHelper = new Controls.Compatibility.Platform.Android.MotionEventHelper(); + bool _disposed; + Frame? _element; + GradientDrawable? _backgroundDrawable; + private IMauiContext? _mauiContext; + protected IPropertyMapper _mapper; + protected CommandMapper? _commandMapper; + protected readonly IPropertyMapper _defaultMapper; + + public event EventHandler? ElementChanged; + public event EventHandler? ElementPropertyChanged; + + public FrameRenderer(Context context) : base(context) + { + _defaultMapper = Mapper; + _mapper = _defaultMapper; + _commandMapper = CommandMapper; + } + + protected CardView Control => this; + + protected Frame? Element + { + get { return _element; } + set + { + if (value != null) + (this as INativeViewHandler).SetVirtualView(value); + } + } + + Size IViewHandler.GetDesiredSize(double widthMeasureSpec, double heightMeasureSpec) + { + return VisualElementRenderer.GetDesiredSize(this, heightMeasureSpec, widthMeasureSpec, + new Size(20, 20)); + } + + protected override void Dispose(bool disposing) + { + if (_disposed) + return; + + _disposed = true; + + if (disposing) + { + if (Element != null) + { + Element.PropertyChanged -= OnElementPropertyChanged; + } + + if (_backgroundDrawable != null) + { + _backgroundDrawable.Dispose(); + _backgroundDrawable = null; + } + + while (ChildCount > 0) + { + var child = GetChildAt(0); + child?.RemoveFromParent(); + child?.Dispose(); + } + + Element?.Handler?.DisconnectHandler(); + } + + base.Dispose(disposing); + } + + protected virtual void OnElementChanged(ElementChangedEventArgs e) + { + ElementChanged?.Invoke(this, new VisualElementChangedEventArgs(e.OldElement, e.NewElement)); + + if (e.NewElement != null) + { + this.EnsureId(); + _backgroundDrawable = new GradientDrawable(); + _backgroundDrawable.SetShape(ShapeType.Rectangle); + this.SetBackground(_backgroundDrawable); + + ElevationHelper.SetElevation(this, e.NewElement); + } + } + + protected override void OnLayout(bool changed, int l, int t, int r, int b) + { + if (Element == null) + return; + + + if (ChildCount > 0) + { + var platformView = GetChildAt(0); + if (platformView != null) + { + platformView.Layout(0, 0, r - l, b - t); + } + } + + if (Element.IsClippedToBounds) + { + _clipRect.Right = r - l; + _clipRect.Bottom = b - t; + ClipBounds = _clipRect; + } + else + { + ClipBounds = null; + } + } + + public override void Draw(Canvas? canvas) + { + Controls.Compatibility.Platform.Android.CanvasExtensions.ClipShape(canvas, Context, Element); + + base.Draw(canvas); + } + + public override bool OnTouchEvent(MotionEvent? e) + { + if (base.OnTouchEvent(e)) + { + return true; + } + + return _motionEventHelper.HandleMotionEvent(Parent, e); + } + + protected override void OnSizeChanged(int w, int h, int oldw, int oldh) + { + base.OnSizeChanged(w, h, oldw, oldh); + + if (w != _width || h != _height) + UpdateBackground(); + } + + protected virtual void OnElementPropertyChanged(object? sender, PropertyChangedEventArgs e) + { + if (this.IsDisposed()) + { + return; + } + + if (Element != null && e.PropertyName != null) + _mapper.UpdateProperty(this, Element, e.PropertyName); + + ElementPropertyChanged?.Invoke(this, e); + _motionEventHelper.UpdateElement(Element); + } + + void UpdateClippedToBounds() + { + if (Element == null) + return; + + var shouldClip = Element.IsSet(Microsoft.Maui.Controls.Compatibility.Layout.IsClippedToBoundsProperty) + ? Element.IsClippedToBounds : Element.CornerRadius > 0f; + + this.SetClipToOutline(shouldClip); + } + + void UpdateBackgroundColor() + { + if (_disposed || Element == null || _backgroundDrawable == null) + return; + + Color bgColor = Element.BackgroundColor; + _backgroundDrawable.SetColor(bgColor?.ToNative() ?? AColor.White); + } + + void UpdateBackground() + { + if (_disposed || Element == null) + return; + + Brush background = Element.Background; + + if (Brush.IsNullOrEmpty(background)) + { + if (_backgroundDrawable.UseGradients()) + { + _backgroundDrawable?.Dispose(); + _backgroundDrawable = null; + this.SetBackground(null); + + _backgroundDrawable = new GradientDrawable(); + _backgroundDrawable.SetShape(ShapeType.Rectangle); + this.SetBackground(_backgroundDrawable); + } + + UpdateBackgroundColor(); + } + else + { + _height = Control.Height; + _width = Control.Width; + _backgroundDrawable.UpdateBackground(background, _height, _width); + } + } + + void UpdateBorderColor() + { + if (_disposed || Element == null || _backgroundDrawable == null) + return; + + Color borderColor = Element.BorderColor; + + if (borderColor == null) + _backgroundDrawable.SetStroke(0, AColor.Transparent); + else + _backgroundDrawable.SetStroke(3, borderColor.ToNative()); + } + + void UpdateShadow() + { + if (_disposed || Element == null) + return; + + float elevation = _defaultElevation; + + if (elevation == -1f) + _defaultElevation = elevation = CardElevation; + + if (Element.HasShadow) + CardElevation = elevation; + else + CardElevation = 0f; + } + + void UpdateCornerRadius() + { + if (_disposed || Element == null || _backgroundDrawable == null) + return; + + if (_defaultCornerRadius == -1f) + { + _defaultCornerRadius = Radius; + } + + float cornerRadius = Element.CornerRadius; + + if (cornerRadius == -1f) + cornerRadius = _defaultCornerRadius; + else + cornerRadius = Context.ToPixels(cornerRadius); + + _backgroundDrawable.SetCornerRadius(cornerRadius); + + UpdateClippedToBounds(); + } + + void UpdateContent() + { + var content = Element?.Content; + + if (content == null || _mauiContext == null) + { + if (ChildCount == 1) + RemoveViewAt(0); + + return; + } + + var platformView = content.ToNative(_mauiContext); + AddView(platformView); + } + #region INativeViewHandler + bool IViewHandler.HasContainer { get => false; set { } } + + object? IViewHandler.ContainerView => null; + + IView? IViewHandler.VirtualView => Element; + + object IElementHandler.NativeView => this; + + Maui.IElement? IElementHandler.VirtualView => Element; + + IMauiContext? IElementHandler.MauiContext => _mauiContext; + + AView INativeViewHandler.NativeView => this; + + AView? INativeViewHandler.ContainerView => this; + + void IViewHandler.NativeArrange(Rectangle rect) => + this.NativeArrangeHandler(rect); + + void IElementHandler.SetMauiContext(IMauiContext mauiContext) => + _mauiContext = mauiContext; + + void IElementHandler.SetVirtualView(Maui.IElement view) => + VisualElementRenderer.SetVirtualView(view, this, OnElementChanged, ref _element, ref _mapper, _defaultMapper, false); + + void IElementHandler.UpdateValue(string property) + { + if (Element != null) + { + OnElementPropertyChanged(Element, new PropertyChangedEventArgs(property)); + } + } + + void IElementHandler.Invoke(string command, object? args) + { + _commandMapper?.Invoke(this, Element, command, args); + } + + void IElementHandler.DisconnectHandler() + { + if (Element?.Handler == (INativeViewHandler)this) + Element.Handler = null; + + _element = null; + } + #endregion + } +} diff --git a/src/Compatibility/Core/src/Handlers/Android/ViewRenderer.cs b/src/Compatibility/Core/src/Handlers/Android/ViewRenderer.cs index 02d94c55af6e..d8101cbcc545 100644 --- a/src/Compatibility/Core/src/Handlers/Android/ViewRenderer.cs +++ b/src/Compatibility/Core/src/Handlers/Android/ViewRenderer.cs @@ -21,6 +21,7 @@ public abstract partial class ViewRenderer : VisualElemen AViewGroup? _container; public TNativeView? Control => ((IElementHandler)this).NativeView as TNativeView ?? _nativeView; + object? IElementHandler.NativeView => _nativeView; public ViewRenderer(Context context) : this(context, VisualElementRendererMapper, VisualElementRendererCommandMapper) { diff --git a/src/Compatibility/Core/src/Handlers/Android/VisualElementRenderer.cs b/src/Compatibility/Core/src/Handlers/Android/VisualElementRenderer.cs index 4dcd080a54dc..a1403e2218ea 100644 --- a/src/Compatibility/Core/src/Handlers/Android/VisualElementRenderer.cs +++ b/src/Compatibility/Core/src/Handlers/Android/VisualElementRenderer.cs @@ -12,6 +12,29 @@ namespace Microsoft.Maui.Controls.Handlers.Compatibility public abstract partial class VisualElementRenderer : AViewGroup, INativeViewHandler where TElement : Element, IView { + object? IElementHandler.NativeView => ChildCount > 0 ? GetChildAt(0) : null; + + static partial void ProcessAutoPackage(Maui.IElement element) + { + if (element?.Handler?.NativeView is not AViewGroup viewGroup) + return; + + viewGroup.RemoveAllViews(); + + if (element is not IVisualTreeElement vte) + return; + + var mauiContext = element?.Handler?.MauiContext; + if (mauiContext == null) + return; + + foreach (var child in vte.GetVisualChildren()) + { + if (child is Maui.IElement childElement) + viewGroup.AddView(childElement.ToNative(mauiContext)); + } + } + public void UpdateLayout() { if (Element != null) @@ -25,7 +48,7 @@ protected override void OnLayout(bool changed, int l, int t, int r, int b) var platformView = GetChildAt(0); if (platformView != null) { - platformView.Layout(l, t, r, b); + platformView.Layout(0, 0, r - l, b - t); } } } diff --git a/src/Compatibility/Core/src/Handlers/ListView/Android/ListViewRenderer.cs b/src/Compatibility/Core/src/Handlers/ListView/Android/ListViewRenderer.cs index 57b562287a6f..1baa2ae444ec 100644 --- a/src/Compatibility/Core/src/Handlers/ListView/Android/ListViewRenderer.cs +++ b/src/Compatibility/Core/src/Handlers/ListView/Android/ListViewRenderer.cs @@ -41,6 +41,7 @@ public class ListViewRenderer : ViewRenderer public ListViewRenderer(Context context) : base(context, Mapper, CommandMapper) { + AutoPackage = false; } protected override Size MinimumSize() diff --git a/src/Compatibility/Core/src/Handlers/ListView/Windows/ListViewRenderer.cs b/src/Compatibility/Core/src/Handlers/ListView/Windows/ListViewRenderer.cs index b4cde8be7bcc..cbe7c3875c27 100644 --- a/src/Compatibility/Core/src/Handlers/ListView/Windows/ListViewRenderer.cs +++ b/src/Compatibility/Core/src/Handlers/ListView/Windows/ListViewRenderer.cs @@ -52,6 +52,7 @@ public class ListViewRenderer : ViewRenderer public ListViewRenderer() : base(Mapper, CommandMapper) { + AutoPackage = false; } internal class ListViewTransparent : WListView diff --git a/src/Compatibility/Core/src/Handlers/ListView/iOS/ListViewRenderer.cs b/src/Compatibility/Core/src/Handlers/ListView/iOS/ListViewRenderer.cs index 06d38b38f356..889a799e90c4 100644 --- a/src/Compatibility/Core/src/Handlers/ListView/iOS/ListViewRenderer.cs +++ b/src/Compatibility/Core/src/Handlers/ListView/iOS/ListViewRenderer.cs @@ -58,7 +58,7 @@ protected UITableViewRowAnimation ReloadSectionsAnimation public ListViewRenderer() : base(Mapper, CommandMapper) { - + AutoPackage = false; } public override void LayoutSubviews() diff --git a/src/Compatibility/Core/src/Handlers/TableView/Android/TableViewRenderer.cs b/src/Compatibility/Core/src/Handlers/TableView/Android/TableViewRenderer.cs index f680a220f99b..1bb9853d5c8a 100644 --- a/src/Compatibility/Core/src/Handlers/TableView/Android/TableViewRenderer.cs +++ b/src/Compatibility/Core/src/Handlers/TableView/Android/TableViewRenderer.cs @@ -22,6 +22,7 @@ public class TableViewRenderer : ViewRenderer public TableViewRenderer(Context context) : base(context, Mapper, CommandMapper) { + AutoPackage = false; } protected virtual TableViewModelRenderer GetModelRenderer(AListView listView, TableView view) diff --git a/src/Compatibility/Core/src/Handlers/TableView/Windows/TableViewRenderer.cs b/src/Compatibility/Core/src/Handlers/TableView/Windows/TableViewRenderer.cs index 762b772f23f0..df651720c2f9 100644 --- a/src/Compatibility/Core/src/Handlers/TableView/Windows/TableViewRenderer.cs +++ b/src/Compatibility/Core/src/Handlers/TableView/Windows/TableViewRenderer.cs @@ -13,6 +13,11 @@ public class TableViewRenderer : ViewRenderer public TableViewRenderer() { - + AutoPackage = false; } protected override Size MinimumSize() diff --git a/src/Compatibility/Core/src/Handlers/VisualElementRenderer.cs b/src/Compatibility/Core/src/Handlers/VisualElementRenderer.cs index 1960f5361d53..182332be33bd 100644 --- a/src/Compatibility/Core/src/Handlers/VisualElementRenderer.cs +++ b/src/Compatibility/Core/src/Handlers/VisualElementRenderer.cs @@ -20,7 +20,7 @@ namespace Microsoft.Maui.Controls.Handlers.Compatibility #if WINDOWS public abstract partial class VisualElementRenderer : INativeViewHandler where TElement : VisualElement - where TNativeElement : UI.Xaml.FrameworkElement + where TNativeElement : PlatformView #else public abstract partial class VisualElementRenderer : INativeViewHandler where TElement : Element, IView @@ -48,6 +48,7 @@ public abstract partial class VisualElementRenderer : INativeViewHandl protected readonly IPropertyMapper _defaultMapper; protected IMauiContext MauiContext => _mauiContext ?? throw new InvalidOperationException("MauiContext not set"); public TElement? Element => _virtualView; + protected bool AutoPackage { get; set; } = true; #if ANDROID public VisualElementRenderer(Android.Content.Context context) : this(context, VisualElementRendererMapper, VisualElementRendererCommandMapper) @@ -63,7 +64,7 @@ public VisualElementRenderer() : this(VisualElementRendererMapper, VisualElement #if ANDROID internal VisualElementRenderer(Android.Content.Context context, IPropertyMapper mapper, CommandMapper? commandMapper = null) #else - internal VisualElementRenderer(IPropertyMapper mapper, CommandMapper? commandMapper = null) + internal VisualElementRenderer(IPropertyMapper mapper, CommandMapper? commandMapper = null) #endif #if ANDROID @@ -95,19 +96,22 @@ protected virtual void OnElementChanged(ElementChangedEventArgs e) ElementChangedPartial(e); } - partial void ElementPropertyChangedPartial(object sender, PropertyChangedEventArgs e); protected virtual void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e) { + if (Element != null && e.PropertyName != null) + _mapper.UpdateProperty(this, Element, e.PropertyName); + ElementPropertyChanged?.Invoke(sender, e); ElementPropertyChangedPartial(sender, e); } - public virtual Size GetDesiredSize(double widthConstraint, double heightConstraint) + + internal static Size GetDesiredSize(INativeViewHandler handler, double widthConstraint, double heightConstraint, Size minimumSize) { - var size = this.GetDesiredSizeFromHandler(widthConstraint, heightConstraint); - var minSize = MinimumSize(); + var size = handler.GetDesiredSizeFromHandler(widthConstraint, heightConstraint); + var minSize = minimumSize; if (size.Height < minSize.Height || size.Width < minSize.Width) { @@ -120,6 +124,9 @@ public virtual Size GetDesiredSize(double widthConstraint, double heightConstrai return size; } + public virtual Size GetDesiredSize(double widthConstraint, double heightConstraint) => + GetDesiredSize(this, widthConstraint, heightConstraint, MinimumSize()); + protected virtual Size MinimumSize() { return new Size(); @@ -127,7 +134,7 @@ protected virtual Size MinimumSize() #if IOS - protected virtual void SetBackgroundColor(Color? color) + protected virtual void SetBackgroundColor(Color? color) #else protected virtual void UpdateBackgroundColor() #endif @@ -137,7 +144,7 @@ protected virtual void UpdateBackgroundColor() } #if IOS - protected virtual void SetBackground(Brush brush) + protected virtual void SetBackground(Brush brush) #else protected virtual void UpdateBackground() #endif @@ -166,17 +173,15 @@ protected virtual void SetImportantForAccessibility() bool IViewHandler.HasContainer { get => true; set { } } - object? IViewHandler.ContainerView => null; + object? IViewHandler.ContainerView => this; IView? IViewHandler.VirtualView => Element; - object IElementHandler.NativeView => this; - Maui.IElement? IElementHandler.VirtualView => Element; IMauiContext? IElementHandler.MauiContext => _mauiContext; - PlatformView INativeViewHandler.NativeView => this; + PlatformView? INativeViewHandler.NativeView => (Element?.Handler as IElementHandler)?.NativeView as PlatformView; PlatformView? INativeViewHandler.ContainerView => this; @@ -188,29 +193,35 @@ void IElementHandler.SetMauiContext(IMauiContext mauiContext) _mauiContext = mauiContext; } - void IElementHandler.SetVirtualView(Maui.IElement view) + internal static void SetVirtualView( + Maui.IElement view, + INativeViewHandler nativeViewHandler, + Action> onElementChanged, + ref TElement? currentVirtualView, + ref IPropertyMapper _mapper, + IPropertyMapper _defaultMapper, + bool autoPackage) { - var oldElement = _virtualView; - _virtualView = view as TElement; - OnElementChanged(new ElementChangedEventArgs(oldElement, _virtualView)); + if (currentVirtualView == view) + return; - _ = view ?? throw new ArgumentNullException(nameof(view)); + var oldElement = currentVirtualView; + currentVirtualView = view as TElement; + onElementChanged?.Invoke(new ElementChangedEventArgs(oldElement, currentVirtualView)); - if (Element == view) - return; + _ = view ?? throw new ArgumentNullException(nameof(view)); - var oldVirtualView = Element; - if (oldVirtualView?.Handler != null) - oldVirtualView.Handler = null; + if (oldElement?.Handler != null) + oldElement.Handler = null; - _virtualView = (TElement)view; + currentVirtualView = (TElement)view; - if (_virtualView.Handler != (INativeViewHandler)this) - _virtualView.Handler = this; + if (currentVirtualView.Handler != nativeViewHandler) + currentVirtualView.Handler = nativeViewHandler; _mapper = _defaultMapper; - if (Element is IPropertyMapperView imv) + if (currentVirtualView is IPropertyMapperView imv) { var map = imv.GetPropertyMapperOverrides(); if (map is not null) @@ -220,14 +231,24 @@ void IElementHandler.SetVirtualView(Maui.IElement view) } } - _mapper.UpdateProperties(this, _virtualView); + if (autoPackage) + { + ProcessAutoPackage(view); + } + + _mapper.UpdateProperties(nativeViewHandler, currentVirtualView); } + static partial void ProcessAutoPackage(Maui.IElement element); + + void IElementHandler.SetVirtualView(Maui.IElement view) => + SetVirtualView(view, this, OnElementChanged, ref _virtualView, ref _mapper, _defaultMapper, AutoPackage); + void IElementHandler.UpdateValue(string property) { if (Element != null) { - _mapper.UpdateProperty(this, Element, property); + OnElementPropertyChanged(Element, new PropertyChangedEventArgs(property)); } } @@ -279,7 +300,7 @@ public static void MapBackgroundColor(INativeViewHandler handler, TElement view) if (handler is VisualElementRenderer ver) #endif #if IOS - ver.SetBackgroundColor(view.Background?.ToColor()); + ver.SetBackgroundColor(view.Background?.ToColor()); #else ver.UpdateBackgroundColor(); #endif @@ -293,7 +314,7 @@ public static void MapBackground(INativeViewHandler handler, TElement view) if (handler is VisualElementRenderer ver) #endif #if IOS - ver.SetBackground(view.Background); + ver.SetBackground(view.Background); #else ver.UpdateBackground(); #endif diff --git a/src/Compatibility/Core/src/Handlers/Windows/FrameRenderer.cs b/src/Compatibility/Core/src/Handlers/Windows/FrameRenderer.cs new file mode 100644 index 000000000000..c079270cba17 --- /dev/null +++ b/src/Compatibility/Core/src/Handlers/Windows/FrameRenderer.cs @@ -0,0 +1,128 @@ +using System.ComponentModel; +using Microsoft.Maui.Graphics; +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Automation.Peers; +using Microsoft.UI.Xaml.Controls; +using Microsoft.Maui.Controls.Platform; +using WBorder = Microsoft.UI.Xaml.Controls.Border; + +namespace Microsoft.Maui.Controls.Handlers.Compatibility +{ + public class FrameRenderer : ViewRenderer + { + public static IPropertyMapper Mapper + = new PropertyMapper(VisualElementRendererMapper); + + public static CommandMapper CommandMapper + = new CommandMapper(VisualElementRendererCommandMapper); + + public FrameRenderer() : base(Mapper, CommandMapper) + { + AutoPackage = false; + } + + protected override AutomationPeer OnCreateAutomationPeer() + { + // We need an automation peer so we can interact with this in automated tests + if (Control == null) + { + return new FrameworkElementAutomationPeer(this); + } + + return new FrameworkElementAutomationPeer(Control); + } + + protected override void OnElementChanged(ElementChangedEventArgs e) + { + base.OnElementChanged(e); + + if (e.NewElement != null) + { + if (Control == null) + SetNativeControl(new WBorder()); + + PackChild(); + UpdateBorder(); + UpdateCornerRadius(); + } + } + + protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e) + { + base.OnElementPropertyChanged(sender, e); + + if (e.PropertyName == "Content") + { + PackChild(); + } + else if (e.PropertyName == Frame.BorderColorProperty.PropertyName || e.PropertyName == Frame.HasShadowProperty.PropertyName) + { + UpdateBorder(); + } + else if (e.PropertyName == Frame.CornerRadiusProperty.PropertyName) + { + UpdateCornerRadius(); + } + } + + protected override void UpdateBackgroundColor() + { + // Background color change must be handled separately + // because the background would protrude through the border if the corners are rounded + // as the background would be applied to the renderer's FrameworkElement + Color backgroundColor = Element.BackgroundColor; + + if (Control != null) + { + Control.Background = backgroundColor.IsDefault() ? + new Microsoft.UI.Xaml.Media.SolidColorBrush((global::Windows.UI.Color)Resources["SystemAltHighColor"]) : backgroundColor.ToNative(); + } + } + + protected override void UpdateBackground() + { + Color backgroundColor = Element.BackgroundColor; + Brush background = Element.Background; + + if (Control != null) + { + if (Brush.IsNullOrEmpty(background)) + Control.Background = backgroundColor.IsDefault() ? + new Microsoft.UI.Xaml.Media.SolidColorBrush((global::Windows.UI.Color)Resources["SystemAltHighColor"]) : backgroundColor.ToNative(); + else + Control.Background = background.ToBrush(); + } + } + + void PackChild() + { + if (Element.Content == null) + return; + + Control.Child = Element.Content.ToNative(MauiContext); + } + + void UpdateBorder() + { + if (Element.BorderColor.IsNotDefault()) + { + Control.BorderBrush = Element.BorderColor.ToNative(); + Control.BorderThickness = WinUIHelpers.CreateThickness(1); + } + else + { + Control.BorderBrush = new Color(0, 0, 0, 0).ToNative(); + } + } + + void UpdateCornerRadius() + { + float cornerRadius = Element.CornerRadius; + + if (cornerRadius == -1f) + cornerRadius = 5f; // default corner radius + + Control.CornerRadius = WinUIHelpers.CreateCornerRadius(cornerRadius); + } + } +} \ No newline at end of file diff --git a/src/Compatibility/Core/src/Handlers/Windows/VisualElementRenderer.cs b/src/Compatibility/Core/src/Handlers/Windows/VisualElementRenderer.cs index ba1e7eb012eb..00d23cda54b1 100644 --- a/src/Compatibility/Core/src/Handlers/Windows/VisualElementRenderer.cs +++ b/src/Compatibility/Core/src/Handlers/Windows/VisualElementRenderer.cs @@ -5,6 +5,8 @@ using System.Text; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; +using WRect = Windows.Foundation.Rect; +using WSize = Windows.Foundation.Size; namespace Microsoft.Maui.Controls.Handlers.Compatibility { @@ -17,6 +19,8 @@ public abstract partial class VisualElementRenderer public FrameworkElement ContainerElement => this; public TNativeElement? Control => ((IElementHandler)this).NativeView as TNativeElement ?? _nativeView; + object? IElementHandler.NativeView => _nativeView; + public UIElement? GetNativeElement() => Control; protected virtual void UpdateNativeControl() { } @@ -47,34 +51,59 @@ protected virtual void Dispose(bool disposing) { } - protected override global::Windows.Foundation.Size MeasureOverride(global::Windows.Foundation.Size availableSize) + protected override WSize MeasureOverride(global::Windows.Foundation.Size availableSize) { - if (Children.Count > 0) + if (Element == null || availableSize.Width * availableSize.Height == 0) + return new WSize(0, 0); + + if (Control != null) + { + Control.Measure(availableSize); + } + + var mauiContext = Element?.Handler?.MauiContext; + var minimumSize = MinimumSize(); + var mauiRect = Control?.DesiredSize ?? minimumSize.ToNative(); + + if (Element is not IVisualTreeElement vte || mauiContext == null) + return mauiRect; + + var width = Math.Max(mauiRect.Width, minimumSize.Width); + var height = Math.Max(mauiRect.Height, minimumSize.Height); + + foreach (var child in vte.GetVisualChildren()) { - var platformView = Children[0]; - if (platformView != null) + if (child is Maui.IElement childElement && childElement.Handler is INativeViewHandler nvh) { - platformView.Measure(availableSize); - return platformView.DesiredSize; + var size = nvh.GetDesiredSizeFromHandler(availableSize.Width, availableSize.Height); + height = Math.Max(height, size.Height); + width = Math.Max(width, size.Width); } } - return base.MeasureOverride(availableSize); + return new WSize(width, height); } - protected override global::Windows.Foundation.Size ArrangeOverride(global::Windows.Foundation.Size finalSize) + protected override WSize ArrangeOverride(global::Windows.Foundation.Size finalSize) { - if (Children.Count > 0) + var myRect = new WRect(0, 0, finalSize.Width, finalSize.Height); + if (Control != null) { - var platformView = Children[0]; - if (platformView != null) - { - platformView.Arrange(new global::Windows.Foundation.Rect(0, 0, finalSize.Width, finalSize.Height)); - return finalSize; - } + Control.Arrange(myRect); } - return base.ArrangeOverride(finalSize); + var mauiContext = Element?.Handler?.MauiContext; + if (Element is not IVisualTreeElement vte || mauiContext == null) + return finalSize; + + var mauiRect = new Graphics.Rectangle(0, 0, finalSize.Width, finalSize.Height); + foreach (var child in vte.GetVisualChildren()) + { + if (child is Maui.IElement childElement && childElement.Handler is INativeViewHandler nvh) + nvh.NativeArrangeHandler(mauiRect); + } + + return finalSize; } void IDisposable.Dispose() @@ -102,6 +131,30 @@ protected virtual void SetAutomationPropertiesName() VisualElement.MapAutomationPropertiesName(this, Element); } + static partial void ProcessAutoPackage(Maui.IElement element) + { + if (element.Handler is not INativeViewHandler nvh || + nvh.ContainerView is not Panel panel) + { + return; + } + + panel.Children.Clear(); + + if (element is not IVisualTreeElement vte) + return; + + var mauiContext = element?.Handler?.MauiContext; + if (mauiContext == null) + return; + + foreach (var child in vte.GetVisualChildren()) + { + if (child is Maui.IElement childElement) + panel.Children.Add(childElement.ToNative(mauiContext)); + } + } + public static void MapAutomationPropertiesLabeledBy(INativeViewHandler handler, TElement view) { if (handler is VisualElementRenderer ver) diff --git a/src/Compatibility/Core/src/Handlers/iOS/FrameRenderer.cs b/src/Compatibility/Core/src/Handlers/iOS/FrameRenderer.cs new file mode 100644 index 000000000000..bd4193577c10 --- /dev/null +++ b/src/Compatibility/Core/src/Handlers/iOS/FrameRenderer.cs @@ -0,0 +1,190 @@ +using System.ComponentModel; +using System.Drawing; +using CoreGraphics; +using Microsoft.Maui.Controls.Platform; +using ObjCRuntime; +using UIKit; + +namespace Microsoft.Maui.Controls.Handlers.Compatibility +{ + public class FrameRenderer : VisualElementRenderer + { + public static IPropertyMapper Mapper + = new PropertyMapper(VisualElementRendererMapper); + + public static CommandMapper CommandMapper + = new CommandMapper(VisualElementRendererCommandMapper); + + UIView _actualView; + CGSize _previousSize; + bool _isDisposed; + + public FrameRenderer() : base(Mapper, CommandMapper) + { + _actualView = new FrameView(); + AddSubview(_actualView); + } + + public override void AddSubview(UIView view) + { + if (view != _actualView) + _actualView.AddSubview(view); + else + base.AddSubview(view); + } + + protected override void OnElementChanged(ElementChangedEventArgs e) + { + base.OnElementChanged(e); + + if (e.NewElement != null) + { + SetupLayer(); + } + } + + protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e) + { + base.OnElementPropertyChanged(sender, e); + + if (e.PropertyName == VisualElement.BackgroundColorProperty.PropertyName || + e.PropertyName == VisualElement.BackgroundProperty.PropertyName || + e.PropertyName == Microsoft.Maui.Controls.Frame.BorderColorProperty.PropertyName || + e.PropertyName == Microsoft.Maui.Controls.Frame.HasShadowProperty.PropertyName || + e.PropertyName == Microsoft.Maui.Controls.Frame.CornerRadiusProperty.PropertyName || + e.PropertyName == Microsoft.Maui.Controls.Frame.IsClippedToBoundsProperty.PropertyName || + e.PropertyName == VisualElement.IsVisibleProperty.PropertyName) + SetupLayer(); + } + + public override void TraitCollectionDidChange(UITraitCollection previousTraitCollection) + { + base.TraitCollectionDidChange(previousTraitCollection); + // Make sure the control adheres to changes in UI theme + if (NativeVersion.IsAtLeast(13) && previousTraitCollection?.UserInterfaceStyle != TraitCollection.UserInterfaceStyle) + SetupLayer(); + } + + public virtual void SetupLayer() + { + if (_actualView == null) + return; + + float cornerRadius = Element.CornerRadius; + + if (cornerRadius == -1f) + cornerRadius = 5f; // default corner radius + + _actualView.Layer.CornerRadius = cornerRadius; + _actualView.Layer.MasksToBounds = cornerRadius > 0; + + if (Element.BackgroundColor == null) + _actualView.Layer.BackgroundColor = Controls.Compatibility.Platform.iOS.ColorExtensions.BackgroundColor.CGColor; + else + { + // BackgroundColor gets set on the base class too which messes with + // the corner radius, shadow, etc. so override that behaviour here + BackgroundColor = UIColor.Clear; + _actualView.Layer.BackgroundColor = Element.BackgroundColor.ToCGColor(); + } + + _actualView.Layer.RemoveBackgroundLayer(); + + if (!Brush.IsNullOrEmpty(Element.Background)) + { + var backgroundLayer = this.GetBackgroundLayer(Element.Background); + + if (backgroundLayer != null) + { + _actualView.Layer.BackgroundColor = UIColor.Clear.CGColor; + Layer.InsertBackgroundLayer(backgroundLayer, 0); + backgroundLayer.CornerRadius = cornerRadius; + } + } + + if (Element.BorderColor == null) + _actualView.Layer.BorderColor = UIColor.Clear.CGColor; + else + { + _actualView.Layer.BorderColor = Element.BorderColor.ToCGColor(); + _actualView.Layer.BorderWidth = 1; + } + + Layer.RasterizationScale = UIScreen.MainScreen.Scale; + Layer.ShouldRasterize = true; + Layer.MasksToBounds = false; + + _actualView.Layer.RasterizationScale = UIScreen.MainScreen.Scale; + _actualView.Layer.ShouldRasterize = true; + _actualView.Layer.MasksToBounds = Element.IsClippedToBounds; + } + + public override void LayoutSubviews() + { + if (_previousSize != Bounds.Size) + SetNeedsDisplay(); + + base.LayoutSubviews(); + } + + public override void Draw(CGRect rect) + { + if (_actualView != null) + _actualView.Frame = Bounds; + + base.Draw(rect); + + _previousSize = Bounds.Size; + } + + protected override void Dispose(bool disposing) + { + if (_isDisposed) + return; + + _isDisposed = true; + + base.Dispose(disposing); + + if (disposing) + { + if (_actualView != null) + { + for (var i = 0; i < _actualView.GestureRecognizers?.Length; i++) + _actualView.GestureRecognizers.Remove(_actualView.GestureRecognizers[i]); + + for (var j = 0; j < _actualView.Subviews.Length; j++) + _actualView.Subviews.Remove(_actualView.Subviews[j]); + + _actualView.RemoveFromSuperview(); + _actualView.Dispose(); + _actualView = null; + } + } + } + + [Microsoft.Maui.Controls.Internals.Preserve(Conditional = true)] + class FrameView : UIView + { + public override void RemoveFromSuperview() + { + for (var i = Subviews.Length - 1; i >= 0; i--) + { + var item = Subviews[i]; + item.RemoveFromSuperview(); + } + } + + public override bool PointInside(CGPoint point, UIEvent uievent) + { + foreach (var view in Subviews) + { + if (view.HitTest(ConvertPointToView(point, view), uievent) != null) + return true; + } + + return false; + } + } + } +} diff --git a/src/Compatibility/Core/src/Handlers/iOS/ViewRenderer.cs b/src/Compatibility/Core/src/Handlers/iOS/ViewRenderer.cs index 4873d54b3a29..9bfd5045aae8 100644 --- a/src/Compatibility/Core/src/Handlers/iOS/ViewRenderer.cs +++ b/src/Compatibility/Core/src/Handlers/iOS/ViewRenderer.cs @@ -3,6 +3,7 @@ using CoreGraphics; using Microsoft.Maui.Graphics; using ObjCRuntime; +using UIKit; using PlatformView = UIKit.UIView; namespace Microsoft.Maui.Controls.Handlers.Compatibility @@ -21,6 +22,7 @@ public abstract partial class ViewRenderer : VisualElemen TNativeView? _nativeView; public TNativeView? Control => ((IElementHandler)this).NativeView as TNativeView ?? _nativeView; + object? IElementHandler.NativeView => _nativeView; public ViewRenderer() : this(VisualElementRendererMapper, VisualElementRendererCommandMapper) { @@ -35,9 +37,10 @@ internal ViewRenderer(IPropertyMapper mapper, CommandMapper? commandMapper = nul public override void LayoutSubviews() { base.LayoutSubviews(); - if(_nativeView != null && Element != null) + var platformView = (this as IElementHandler).NativeView as UIView; + if (platformView != null && Element != null) { - _nativeView.Frame = new CoreGraphics.CGRect(0, 0, (nfloat)Element.Width, (nfloat)Element.Height); + platformView.Frame = new CoreGraphics.CGRect(0, 0, (nfloat)Element.Width, (nfloat)Element.Height); } } diff --git a/src/Compatibility/Core/src/Handlers/iOS/VisualElementRenderer.cs b/src/Compatibility/Core/src/Handlers/iOS/VisualElementRenderer.cs index 104250927d75..c28d1a8a7283 100644 --- a/src/Compatibility/Core/src/Handlers/iOS/VisualElementRenderer.cs +++ b/src/Compatibility/Core/src/Handlers/iOS/VisualElementRenderer.cs @@ -7,11 +7,34 @@ namespace Microsoft.Maui.Controls.Handlers.Compatibility { - public abstract partial class VisualElementRenderer : UIView, INativeViewHandler + public abstract partial class VisualElementRenderer : UIView, INativeViewHandler, IElementHandler where TElement : Element, IView { + object? IElementHandler.NativeView => Subviews.Length > 0 ? Subviews[0] : null; + public virtual UIViewController? ViewController => null; + static partial void ProcessAutoPackage(Maui.IElement element) + { + if (element?.Handler?.NativeView is not UIView viewGroup) + return; + + viewGroup.ClearSubviews(); + + if (element is not IVisualTreeElement vte) + return; + + var mauiContext = element?.Handler?.MauiContext; + if (mauiContext == null) + return; + + foreach (var child in vte.GetVisualChildren()) + { + if (child is Maui.IElement childElement) + viewGroup.AddSubview(childElement.ToNative(mauiContext)); + } + } + protected virtual void UpdateNativeWidget() { diff --git a/src/Compatibility/Core/src/Windows/FrameRenderer.cs b/src/Compatibility/Core/src/Windows/FrameRenderer.cs index 3a7ea08f262a..264a9be851de 100644 --- a/src/Compatibility/Core/src/Windows/FrameRenderer.cs +++ b/src/Compatibility/Core/src/Windows/FrameRenderer.cs @@ -5,9 +5,11 @@ using Microsoft.UI.Xaml.Controls; using Microsoft.Maui.Controls.Platform; using WBorder = Microsoft.UI.Xaml.Controls.Border; +using System; namespace Microsoft.Maui.Controls.Compatibility.Platform.UWP { + [Obsolete("Use Microsoft.Maui.Controls.Handlers.Compatibility.FrameRenderer instead")] public class FrameRenderer : ViewRenderer { public FrameRenderer() diff --git a/src/Compatibility/Core/src/iOS/Renderers/FrameRenderer.cs b/src/Compatibility/Core/src/iOS/Renderers/FrameRenderer.cs index 1d2049dd38e8..b64de4c1548b 100644 --- a/src/Compatibility/Core/src/iOS/Renderers/FrameRenderer.cs +++ b/src/Compatibility/Core/src/iOS/Renderers/FrameRenderer.cs @@ -1,4 +1,5 @@ -using System.ComponentModel; +using System; +using System.ComponentModel; using System.Drawing; using CoreGraphics; using Microsoft.Maui.Controls.Platform; @@ -7,6 +8,7 @@ namespace Microsoft.Maui.Controls.Compatibility.Platform.iOS { + [Obsolete("Use Microsoft.Maui.Controls.Handlers.Compatibility.FrameRenderer instead")] public class FrameRenderer : VisualElementRenderer, ITabStop { UIView _actualView; diff --git a/src/Controls/samples/Controls.Sample/Pages/Compatibility/FramePage.xaml b/src/Controls/samples/Controls.Sample/Pages/Compatibility/FramePage.xaml index fd67bc07cb82..013573359deb 100644 --- a/src/Controls/samples/Controls.Sample/Pages/Compatibility/FramePage.xaml +++ b/src/Controls/samples/Controls.Sample/Pages/Compatibility/FramePage.xaml @@ -11,7 +11,8 @@ Text="Default" Style="{StaticResource Headline}"/> + BackgroundColor="LightGray" + HasShadow="False">