Size LabelF9pSize(double widthConstraint, double fontSize)
        {
            if (_currentDrawState == null ||
                _currentDrawState.IsEmpty ||
                Control == null ||
                Element == null ||
                _disposed)
            {
                return(Size.Zero);
            }

            var displayScale = (float)Resources.DisplayMetrics.DensityDpi / (float)Android.Util.DisplayMetricsDensity.Default;

            _currentMeasureState = new TextControlState(_currentDrawState)
            {
                AvailWidth  = (int)System.Math.Floor(System.Math.Min(widthConstraint * displayScale, int.MaxValue / 3)),
                AvailHeight = int.MaxValue / 3,
                TextSize    = (float)fontSize,
            };

            _measureControl    = _measureControl ?? new F9PTextView(Settings.Context);
            _lastMeasureResult = InternalLayout(_measureControl, _currentMeasureState);
            _lastMeasureState  = new TextControlState(_currentMeasureState);

            return(new Size(_lastMeasureResult.Value.Request.Width / displayScale, _lastMeasureResult.Value.Request.Height / displayScale));
        }
        SizeRequest DrawLabel(double width, double height)
        {
            if (_currentDrawState == null ||
                _currentDrawState.IsEmpty ||
                Control == null ||
                Element == null ||
                _disposed ||
                width < 0 ||
                height < 0
                )
            {
                return(new SizeRequest(Size.Zero));
            }
            if (double.IsInfinity(width))
            {
                width = int.MaxValue / 3;
            }
            if (double.IsInfinity(height))
            {
                height = int.MaxValue / 3;
            }

            var displayScale = (float)Resources.DisplayMetrics.DensityDpi / (float)Android.Util.DisplayMetricsDensity.Default;

            _currentDrawState.AvailWidth  = (int)System.Math.Floor(width * displayScale);
            _currentDrawState.AvailHeight = (int)System.Math.Floor(height * displayScale);
            _lastDrawResult = InternalLayout(Control, _currentDrawState);
            _lastDrawState  = new TextControlState(_currentDrawState);

            return(_lastDrawResult.Value);
        }
        /// <summary>
        /// Raises the element changed event.
        /// </summary>
        /// <param name="e">E.</param>
        protected override void OnElementChanged(ElementChangedEventArgs <Label> e)
        {
            base.OnElementChanged(e);

            _lastMeasureState  = null;
            _lastDrawResult    = null;
            _lastMeasureResult = null;
            _measureControl?.Dispose();
            _measureControl   = null;
            _currentDrawState = new TextControlState
            {
                Lines         = e.NewElement.Lines,
                AutoFit       = e.NewElement.AutoFit,
                LineBreakMode = e.NewElement.LineBreakMode,
                SyncFontSize  = (float)e.NewElement.SynchronizedFontSize,
            };

            if (e.OldElement != null)
            {
                e.OldElement.RendererIndexAtPoint            -= IndexAtPoint;
                e.OldElement.RendererSizeForWidthAndFontSize -= LabelF9pSize;
                e.OldElement.Draw -= DrawLabel;
                Control?.SkipNextInvalidate();
            }
            if (e.NewElement != null)
            {
                if (Control == null)
                {
                    var view = new F9PTextView(Context);
                    InitControl(view);
                    SetNativeControl(view);
                }
                else
                {
                    InitControl(Control);
                }
                Control.IsNativeDrawEnabled = false;

                e.NewElement.RendererIndexAtPoint            += IndexAtPoint;
                e.NewElement.RendererSizeForWidthAndFontSize += LabelF9pSize;
                e.NewElement.Draw += DrawLabel;

                if (e.NewElement.Width > 0 && e.NewElement.Height > 0)
                {
                    var displayScale = (float)Resources.DisplayMetrics.DensityDpi / (float)Android.Util.DisplayMetricsDensity.Default;
                    DrawLabel(e.NewElement.Width * displayScale, e.NewElement.Height * displayScale);
                }
                Control.IsNativeDrawEnabled = true;
            }
        }
        protected override void Dispose(bool disposing)
        {
            if (!_disposed && disposing)
            {
                _disposed = true;
                if (Element is Forms9Patch.Label element)
                {
                    try // incase it has been garbage collected
                    {
                        element.RendererIndexAtPoint            -= IndexAtPoint;
                        element.RendererSizeForWidthAndFontSize -= LabelF9pSize;
                        element.Draw -= DrawLabel;
                    }
#pragma warning disable RECS0022 // A catch clause that catches System.Exception and has an empty body
                    catch (System.Exception) { }
#pragma warning restore RECS0022 // A catch clause that catches System.Exception and has an empty body
                }
                _currentDrawState = null;
                _measureControl?.Dispose();
                _measureControl = null;
            }
            base.Dispose(disposing);
        }
        SizeRequest InternalLayout(F9PTextView control, TextControlState state)
        {
            if (Element is Forms9Patch.Label element && control != null)
            {
                ICharSequence text        = state.JavaText;
                var           tmpFontSize = BoundTextSize(element.FontSize);
                control.Typeface = state.Typeface;
                //control.SetTextColor(state.TextColor.ToAndroid());
                UpdateColor(control);
                control.TextSize = tmpFontSize;

                control.IsNativeDrawEnabled = false;
                control.SetSingleLine(false);
                control.SetMaxLines(int.MaxValue / 2);
                control.SetIncludeFontPadding(false);
                control.Ellipsize = null;

                double tmpHt = -1;
                double tmpWd = -1;

                var fontMetrics    = control.Paint.GetFontMetrics();
                var fontLineHeight = fontMetrics.Descent - fontMetrics.Ascent;
                var fontLeading    = System.Math.Abs(fontMetrics.Bottom - fontMetrics.Descent);

                if (state.Lines == 0)
                {
                    if (state.AvailHeight < int.MaxValue / 3)
                    {
                        tmpFontSize = TextPaintExtensions.ZeroLinesFit(state.JavaText, new TextPaint(control.Paint), ModelMinFontSize, tmpFontSize, state.AvailWidth, state.AvailHeight);
                    }
                }
                else
                {
                    if (state.AutoFit == AutoFit.Lines)
                    {
                        if (state.AvailHeight > int.MaxValue / 3)
                        {
                            tmpHt = System.Math.Round(state.Lines * fontLineHeight + (state.Lines - 1) * fontLeading);
                        }
                        else
                        {
                            var fontPointSize   = tmpFontSize;
                            var lineHeightRatio = fontLineHeight / fontPointSize;
                            var leadingRatio    = fontLeading / fontPointSize;
                            tmpFontSize = ((state.AvailHeight / (state.Lines + leadingRatio * (state.Lines - 1))) / lineHeightRatio - 0.1f);
                        }
                    }
                    else if (state.AutoFit == AutoFit.Width)
                    {
                        tmpFontSize = TextPaintExtensions.WidthFit(state.JavaText, new TextPaint(control.Paint), state.Lines, ModelMinFontSize, tmpFontSize, state.AvailWidth, state.AvailHeight);
                    }
                }

                tmpFontSize = BoundTextSize(tmpFontSize);

                // this is the optimal font size.  Let it be known!
                if (System.Math.Abs(tmpFontSize - element.FittedFontSize) > 0.1)
                {
                    if (System.Math.Abs(tmpFontSize - element.FontSize) < 0.1 || (element.FontSize < 0 && System.Math.Abs(tmpFontSize - F9PTextView.DefaultTextSize) < 0.1))
                    {
                        element.FittedFontSize = -1;
                    }
                    else
                    {
                        element.FittedFontSize = tmpFontSize;
                    }
                }

                var syncFontSize = (float)((ILabel)element).SynchronizedFontSize;
                if (syncFontSize >= 0 && System.Math.Abs(tmpFontSize - syncFontSize) > 0.1)
                {
                    tmpFontSize = syncFontSize;
                }

                control.TextSize       = tmpFontSize;
                state.RenderedFontSize = tmpFontSize;
                var layout = TextExtensions.StaticLayout(state.JavaText, new TextPaint(control.Paint), state.AvailWidth, Android.Text.Layout.Alignment.AlignNormal, 1.0f, 0.0f, true);

                int lines = state.Lines;
                if (lines == 0 && state.AutoFit == AutoFit.None)
                {
                    for (int i = 0; i < layout.LineCount; i++)
                    {
                        if (layout.GetLineBottom(i) <= state.AvailHeight - layout.TopPadding - layout.BottomPadding)
                        {
                            lines++;
                        }
                        else
                        {
                            break;
                        }
                    }
                }
                if (layout.Height > state.AvailHeight || (lines > 0 && layout.LineCount > lines))
                {
                    if (state.Lines == 1)
                    {
                        control.SetSingleLine(true);
                        control.SetMaxLines(1);
                        control.Ellipsize = state.LineBreakMode.ToEllipsize();
                    }
                    else
                    {
                        layout = TextPaintExtensions.Truncate(state.Text, element.F9PFormattedString, new TextPaint(control.Paint), state.AvailWidth, state.AvailHeight, element.AutoFit, element.LineBreakMode, ref lines, ref text);
                    }
                }
                lines = lines > 0 ? System.Math.Min(lines, layout.LineCount) : layout.LineCount;

                for (int i = 0; i < lines; i++)
                {
                    tmpHt = layout.GetLineBottom(i);
                    var width = layout.GetLineWidth(i);
                    if (width > tmpWd)
                    {
                        tmpWd = System.Math.Ceiling(width);
                    }
                }
                if (state.AutoFit == AutoFit.None && state.Lines > 0)
                {
                    control.SetMaxLines(state.Lines);
                }

                if (element.IsDynamicallySized && state.Lines > 0 && state.AutoFit == AutoFit.Lines)
                {
                    fontMetrics    = control.Paint.GetFontMetrics();
                    fontLineHeight = fontMetrics.Descent - fontMetrics.Ascent;
                    fontLeading    = System.Math.Abs(fontMetrics.Bottom - fontMetrics.Descent);
                    tmpHt          = fontLineHeight * state.Lines + fontLeading * (state.Lines - 1);
                }

                control.Gravity = element.HorizontalTextAlignment.ToHorizontalGravityFlags() | element.VerticalTextAlignment.ToVerticalGravityFlags();

                if (element.Text != null)
                {
                    control.Text = text.ToString();
                }
                else
                {
                    control.TextFormatted = text;
                }

                var result = new SizeRequest(new Size(System.Math.Ceiling(tmpWd), System.Math.Ceiling(tmpHt)), new Size(10, System.Math.Ceiling(tmpHt)));

                if (element.LineBreakMode == LineBreakMode.NoWrap)
                {
                    control.SetSingleLine(true);
                }

                control.IsNativeDrawEnabled = true;
                if (control == Control)
                {
                    //P42.Utils.Debug.Message(Element?.HtmlText, "!!!~~~~~~~ REQUEST LAYOUT~~~~~~~~~!!!");
                    // none of the below seems to help the issue where the label is not apparing in ConnectionCalc cells
                    //Control.RequestLayout();
                    ////P42.Utils.Debug.Message(Element?.HtmlText, "Control.Invalidate()");
                    //Control.Invalidate();
                    //Invalidate();
                    //var widthSpec = MeasureSpec.MakeMeasureSpec(tmpWd, Android.Views.MeasureSpecMode.Exactly);
                    //var heightSpec = MeasureSpec.MakeMeasureSpec(tmpHt, Android.Views.MeasureSpecMode.Exactly);
                    //Control.Measure(widthSpec, heightSpec);
                    //Control.RequestLayout();
                    //Control.Layout(0, 0, (int)System.Math.Ceiling(tmpWd), (int)System.Math.Ceiling(tmpHt));
                    //Control
                }
                return(result);
            }
            return(new SizeRequest(Size.Zero));
        }
        /// <summary>
        /// Gets the size of the desired.
        /// </summary>
        /// <returns>The desired size.</returns>
        /// <param name="widthConstraint">Width constraint.</param>
        /// <param name="heightConstraint">Height constraint.</param>
        public override SizeRequest GetDesiredSize(int widthConstraint, int heightConstraint)
        {
            if (_currentDrawState == null ||
                _currentDrawState.IsEmpty ||
                Control == null ||
                Element == null ||
                _disposed)
            {
                return(new SizeRequest(Size.Zero));
            }

            var width = MeasureSpec.GetSize(widthConstraint);

            if (MeasureSpec.GetMode(widthConstraint) == Android.Views.MeasureSpecMode.Unspecified)
            {
                width = int.MaxValue / 2;
            }
            var height = MeasureSpec.GetSize(heightConstraint);

            if (MeasureSpec.GetMode(heightConstraint) == Android.Views.MeasureSpecMode.Unspecified)
            {
                height = int.MaxValue / 2;
            }

            if (width <= 0 || height <= 0)
            {
                return(new SizeRequest(Size.Zero));
            }

            _currentMeasureState = new TextControlState(_currentDrawState)
            {
                AvailWidth  = width,
                AvailHeight = height
            };

            if (_lastMeasureResult != null && _lastMeasureResult.HasValue &&
                _lastMeasureState == _currentMeasureState &&
                _lastMeasureResult.Value.Request.Width > 0 &&
                _lastMeasureResult.Value.Request.Height > 0 &&
                _lastMeasureState.RenderedFontSize >= _currentMeasureState.TextSize &&
                _lastMeasureResult.Value.Request.Width <= _currentMeasureState.AvailWidth &&
                _lastMeasureResult.Value.Request.Height <= _currentMeasureState.AvailHeight
                )
            {
                return(_lastMeasureResult.Value);
            }

            if (_lastDrawResult != null && _lastDrawResult.HasValue &&
                _lastDrawState == _currentMeasureState &&
                _lastDrawResult.Value.Request.Width > 0 &&
                _lastDrawResult.Value.Request.Height > 0 &&
                _lastDrawState.RenderedFontSize >= _currentDrawState.TextSize &&
                _lastDrawResult.Value.Request.Width <= _currentMeasureState.AvailWidth &&
                _lastDrawResult.Value.Request.Height <= _currentMeasureState.AvailWidth)
            {
                return(_lastDrawResult.Value);
            }

            _measureControl    = _measureControl ?? new F9PTextView(Settings.Context);
            _lastMeasureResult = InternalLayout(_measureControl, _currentMeasureState);
            _lastMeasureState  = new TextControlState(_currentMeasureState);

            return(_lastMeasureResult.Value);
        }