summaryrefslogtreecommitdiff
path: root/Xamarin.Forms.Platform.WinRT/VisualElementRenderer.cs
diff options
context:
space:
mode:
Diffstat (limited to 'Xamarin.Forms.Platform.WinRT/VisualElementRenderer.cs')
-rw-r--r--Xamarin.Forms.Platform.WinRT/VisualElementRenderer.cs401
1 files changed, 401 insertions, 0 deletions
diff --git a/Xamarin.Forms.Platform.WinRT/VisualElementRenderer.cs b/Xamarin.Forms.Platform.WinRT/VisualElementRenderer.cs
new file mode 100644
index 00000000..96565d74
--- /dev/null
+++ b/Xamarin.Forms.Platform.WinRT/VisualElementRenderer.cs
@@ -0,0 +1,401 @@
+using System;
+using System.ComponentModel;
+using Windows.Foundation;
+using Windows.UI.Xaml;
+using Windows.UI.Xaml.Automation;
+using Windows.UI.Xaml.Controls;
+
+#if WINDOWS_UWP
+
+namespace Xamarin.Forms.Platform.UWP
+#else
+
+namespace Xamarin.Forms.Platform.WinRT
+#endif
+{
+ public class VisualElementRenderer<TElement, TNativeElement> : Panel, IVisualElementRenderer, IDisposable, IEffectControlProvider where TElement : VisualElement
+ where TNativeElement : FrameworkElement
+ {
+ bool _disposed;
+ EventHandler<VisualElementChangedEventArgs> _elementChangedHandlers;
+ VisualElementTracker<TElement, TNativeElement> _tracker;
+
+ public TNativeElement Control { get; private set; }
+
+ public TElement Element { get; private set; }
+
+ protected bool AutoPackage { get; set; } = true;
+
+ protected bool AutoTrack { get; set; } = true;
+
+ protected VisualElementTracker<TElement, TNativeElement> Tracker
+ {
+ get { return _tracker; }
+ set
+ {
+ if (_tracker == value)
+ return;
+
+ if (_tracker != null)
+ {
+ _tracker.Dispose();
+ _tracker.Updated -= OnTrackerUpdated;
+ }
+
+ _tracker = value;
+
+ if (_tracker != null)
+ {
+ _tracker.Updated += OnTrackerUpdated;
+ UpdateTracker();
+ }
+ }
+ }
+
+ VisualElementPackager Packager { get; set; }
+
+ public void Dispose()
+ {
+ Dispose(true);
+ }
+
+ void IEffectControlProvider.RegisterEffect(Effect effect)
+ {
+ var platformEffect = effect as PlatformEffect;
+ if (platformEffect != null)
+ OnRegisterEffect(platformEffect);
+ }
+
+ public FrameworkElement ContainerElement
+ {
+ get { return this; }
+ }
+
+ VisualElement IVisualElementRenderer.Element
+ {
+ get { return Element; }
+ }
+
+ event EventHandler<VisualElementChangedEventArgs> IVisualElementRenderer.ElementChanged
+ {
+ add
+ {
+ if (_elementChangedHandlers == null)
+ _elementChangedHandlers = value;
+ else
+ _elementChangedHandlers = (EventHandler<VisualElementChangedEventArgs>)Delegate.Combine(_elementChangedHandlers, value);
+ }
+
+ remove { _elementChangedHandlers = (EventHandler<VisualElementChangedEventArgs>)Delegate.Remove(_elementChangedHandlers, value); }
+ }
+
+ public virtual SizeRequest GetDesiredSize(double widthConstraint, double heightConstraint)
+ {
+ if (Children.Count == 0)
+ return new SizeRequest();
+
+ var constraint = new Windows.Foundation.Size(widthConstraint, heightConstraint);
+ TNativeElement child = Control;
+
+ child.Measure(constraint);
+ var result = new Size(Math.Ceiling(child.DesiredSize.Width), Math.Ceiling(child.DesiredSize.Height));
+
+ return new SizeRequest(result);
+ }
+
+ public void SetElement(VisualElement element)
+ {
+ TElement oldElement = Element;
+ Element = (TElement)element;
+
+ if (oldElement != null)
+ {
+ oldElement.PropertyChanged -= OnElementPropertyChanged;
+ oldElement.FocusChangeRequested -= OnElementFocusChangeRequested;
+ }
+
+ if (element != null)
+ {
+ Element.PropertyChanged += OnElementPropertyChanged;
+ Element.FocusChangeRequested += OnElementFocusChangeRequested;
+
+ if (AutoPackage && Packager == null)
+ Packager = new VisualElementPackager(this);
+
+ if (AutoTrack && Tracker == null)
+ {
+ Tracker = new VisualElementTracker<TElement, TNativeElement>();
+ }
+
+ // Disabled until reason for crashes with unhandled exceptions is discovered
+ // Without this some layouts may end up with improper sizes, however their children
+ // will position correctly
+ //Loaded += (sender, args) => {
+ if (Packager != null)
+ Packager.Load();
+ //};
+ }
+
+ OnElementChanged(new ElementChangedEventArgs<TElement>(oldElement, Element));
+
+ var controller = (IElementController)oldElement;
+ if (controller != null && controller.EffectControlProvider == this)
+ {
+ controller.EffectControlProvider = null;
+ }
+
+ controller = element;
+ if (controller != null)
+ controller.EffectControlProvider = this;
+ }
+
+ public event EventHandler<ElementChangedEventArgs<TElement>> ElementChanged;
+
+ protected override Windows.Foundation.Size ArrangeOverride(Windows.Foundation.Size finalSize)
+ {
+ if (Element == null || finalSize.Width * finalSize.Height == 0)
+ return finalSize;
+
+ Element.IsInNativeLayout = true;
+
+ if (Control != null)
+ {
+ Control.Arrange(new Rect(0, 0, finalSize.Width, finalSize.Height));
+ }
+
+ for (var i = 0; i < Element.LogicalChildren.Count; i++)
+ {
+ var child = Element.LogicalChildren[i] as VisualElement;
+ if (child == null)
+ continue;
+ IVisualElementRenderer renderer = Platform.GetRenderer(child);
+ if (renderer == null)
+ continue;
+ Rectangle bounds = child.Bounds;
+
+ renderer.ContainerElement.Arrange(new Rect(bounds.X, bounds.Y, Math.Max(0, bounds.Width), Math.Max(0, bounds.Height)));
+ }
+
+ Element.IsInNativeLayout = false;
+
+ return finalSize;
+ }
+
+ protected virtual void Dispose(bool disposing)
+ {
+ if (!disposing || _disposed)
+ return;
+
+ _disposed = true;
+
+ Tracker?.Dispose();
+ Tracker = null;
+
+ Packager?.Dispose();
+ Packager = null;
+
+ SetNativeControl(null);
+ SetElement(null);
+ }
+
+ protected override Windows.Foundation.Size MeasureOverride(Windows.Foundation.Size availableSize)
+ {
+ if (Element == null || availableSize.Width * availableSize.Height == 0)
+ return new Windows.Foundation.Size(0, 0);
+
+ Element.IsInNativeLayout = true;
+
+ for (var i = 0; i < Element.LogicalChildren.Count; i++)
+ {
+ var child = Element.LogicalChildren[i] as VisualElement;
+ if (child == null)
+ continue;
+ IVisualElementRenderer renderer = Platform.GetRenderer(child);
+ if (renderer == null)
+ continue;
+
+ renderer.ContainerElement.Measure(availableSize);
+ }
+
+ double width = Math.Max(0, Element.Width);
+ double height = Math.Max(0, Element.Height);
+ var result = new Windows.Foundation.Size(width, height);
+ if (Control != null)
+ {
+ double w = Element.Width;
+ double h = Element.Height;
+ if (w == -1)
+ w = availableSize.Width;
+ if (h == -1)
+ h = availableSize.Height;
+ w = Math.Max(0, w);
+ h = Math.Max(0, h);
+ Control.Measure(new Windows.Foundation.Size(w, h));
+ }
+
+ Element.IsInNativeLayout = false;
+
+ return result;
+ }
+
+ protected virtual void OnElementChanged(ElementChangedEventArgs<TElement> e)
+ {
+ var args = new VisualElementChangedEventArgs(e.OldElement, e.NewElement);
+ if (_elementChangedHandlers != null)
+ _elementChangedHandlers(this, args);
+
+ EventHandler<ElementChangedEventArgs<TElement>> changed = ElementChanged;
+ if (changed != null)
+ changed(this, e);
+ }
+
+ protected virtual void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ if (e.PropertyName == VisualElement.IsEnabledProperty.PropertyName)
+ UpdateEnabled();
+ else if (e.PropertyName == VisualElement.BackgroundColorProperty.PropertyName)
+ UpdateBackgroundColor();
+ }
+
+ protected virtual void OnRegisterEffect(PlatformEffect effect)
+ {
+ effect.Container = this;
+ effect.Control = Control;
+ }
+
+ protected virtual void SetAutomationId(string id)
+ {
+ SetValue(AutomationProperties.AutomationIdProperty, id);
+ }
+
+ protected void SetNativeControl(TNativeElement control)
+ {
+ TNativeElement oldControl = Control;
+ Control = control;
+
+ if (oldControl != null)
+ {
+ Children.Remove(oldControl);
+
+ oldControl.Loaded -= OnControlLoaded;
+ oldControl.GotFocus -= OnControlGotFocus;
+ oldControl.LostFocus -= OnControlLostFocus;
+ }
+
+ UpdateTracker();
+
+ if (control == null)
+ return;
+
+ Control.HorizontalAlignment = HorizontalAlignment.Stretch;
+ Control.VerticalAlignment = VerticalAlignment.Stretch;
+
+ Children.Add(control);
+
+ Element.IsNativeStateConsistent = false;
+ control.Loaded += OnControlLoaded;
+
+ control.GotFocus += OnControlGotFocus;
+ control.LostFocus += OnControlLostFocus;
+
+ UpdateBackgroundColor();
+
+ if (Element != null && !string.IsNullOrEmpty(Element.AutomationId))
+ SetAutomationId(Element.AutomationId);
+ }
+
+ protected virtual void UpdateBackgroundColor()
+ {
+ Color backgroundColor = Element.BackgroundColor;
+ var control = Control as Control;
+ if (control != null)
+ {
+ if (!backgroundColor.IsDefault)
+ {
+ control.Background = backgroundColor.ToBrush();
+ }
+ else
+ {
+ control.ClearValue(Windows.UI.Xaml.Controls.Control.BackgroundProperty);
+ }
+ }
+ else
+ {
+ if (!backgroundColor.IsDefault)
+ {
+ Background = backgroundColor.ToBrush();
+ }
+ else
+ {
+ ClearValue(BackgroundProperty);
+ }
+ }
+ }
+
+ protected virtual void UpdateNativeControl()
+ {
+ UpdateEnabled();
+ }
+
+ internal virtual void OnElementFocusChangeRequested(object sender, VisualElement.FocusRequestArgs args)
+ {
+ var control = Control as Control;
+ if (control == null)
+ return;
+
+ if (args.Focus)
+ args.Result = control.Focus(FocusState.Programmatic);
+ else
+ {
+ UnfocusControl(control);
+ args.Result = true;
+ }
+ }
+
+ internal void UnfocusControl(Control control)
+ {
+ if (control == null || !control.IsEnabled)
+ return;
+
+ control.IsEnabled = false;
+ control.IsEnabled = true;
+ }
+
+ void OnControlGotFocus(object sender, RoutedEventArgs args)
+ {
+ ((IVisualElementController)Element).SetValueFromRenderer(VisualElement.IsFocusedPropertyKey, true);
+ }
+
+ void OnControlLoaded(object sender, RoutedEventArgs args)
+ {
+ Element.IsNativeStateConsistent = true;
+ }
+
+ void OnControlLostFocus(object sender, RoutedEventArgs args)
+ {
+ ((IVisualElementController)Element).SetValueFromRenderer(VisualElement.IsFocusedPropertyKey, false);
+ }
+
+ void OnTrackerUpdated(object sender, EventArgs e)
+ {
+ UpdateNativeControl();
+ }
+
+ void UpdateEnabled()
+ {
+ var control = Control as Control;
+ if (control != null)
+ control.IsEnabled = Element.IsEnabled;
+ }
+
+ void UpdateTracker()
+ {
+ if (_tracker == null)
+ return;
+
+ _tracker.Control = Control;
+ _tracker.Element = Element;
+ _tracker.Container = ContainerElement;
+ }
+ }
+} \ No newline at end of file