Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Does a controls OnElementChanged() get called when I leave a page?

I am using this iOS SegmentedControlRenderer on a page.

But when I go to the page this way: Navigation.PushAsync(new CFSPage()) and then click on the back arrow to go to the previous page, the OnElementChanged event in my customer iOS renderer is fired. The result with my renderer is that the following line gives a null reference error:

segmentedControl.TintColor = e.NewElement?.TintColor.ToUIColor();

Can someone please explain what's the purpose of the ? here and also should this line be after or should it be inside the if (e.NewElement != null) check. Am I correct in saying that the following lines should NOT be executed if there is no NewElement?

segmentedControl.TintColor = e.NewElement?.TintColor.ToUIColor();
SetNativeControl(segmentedControl);
SetSelectedSegment();

This renderer as is doesn't have an

protected override void Dispose(bool disposing)

Is that something that's missing?

Renderer

public class SegmentedControlRenderer : ViewRenderer<SegmentedControl, UISegmentedControl>
{
    protected override void OnElementChanged(ElementChangedEventArgs<SegmentedControl> e)
    {
        base.OnElementChanged(e);
        UISegmentedControl segmentedControl = null;
        if (Control == null)
        {
            segmentedControl = new UISegmentedControl();
            for (var i = 0; i < e.NewElement.Children.Count; i++)
            {
             segmentedControl.InsertSegment(Element.Children[i].Text, i, false);
            }
            SetNativeControl(segmentedControl);
            SetSelectedSegment();
        }
        if (e.OldElement != null)
        {
            if (segmentedControl != null)
                segmentedControl.ValueChanged -= NativeValueChanged;
        }
        if (e.NewElement != null)
        {
            segmentedControl.ValueChanged += NativeValueChanged;
        }
        segmentedControl.TintColor = e.NewElement?.TintColor.ToUIColor();
        SetNativeControl(segmentedControl);
        SetSelectedSegment();
    }

    protected override void OnElementPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
    {
        base.OnElementPropertyChanged(sender, e);

        if (e.PropertyName == nameof(SegmentedControl.SelectedSegment))
            SetSelectedSegment();
        if (e.PropertyName == SegmentedControl.TintColorProperty.PropertyName)
            SetSegmentTintColor();
    }

    void NativeValueChanged(object sender, EventArgs e)
    {
        if (Element is SegmentedControl formsElement)
        {
            formsElement.SelectedSegment = (int)Control.SelectedSegment;
        };
    }

    void SetSegmentTintColor()
    {
        if (Element is SegmentedControl formsElement)
            Control.TintColor = formsElement.TintColor.ToUIColor();
    }

    void SetSelectedSegment()
    {
        if (Element is SegmentedControl formsElement)
        {
            if (formsElement.SelectedSegment >= 0 && formsElement.SelectedSegment < Control.NumberOfSegments)
                Control.SelectedSegment = formsElement.SelectedSegment;
        }
    }
}
like image 459
Alan2 Avatar asked Sep 16 '18 10:09

Alan2


2 Answers

The following is an example of how OnElementChanged in most renderers is structured:

protected override void OnElementChanged(ElementChangedEventArgs<Type> e)
{
    base.OnElementChanged(e);

    if (e.OldElement != null)
    {
        // Unsubscribe from event handlers and cleanup any resources
    }

    if (e.NewElement != null)
    {
        if (Control == null)
        {
            // Instantiate the native control and assign it to the Control property with
            // the SetNativeControl method
        }

        // Configure the control and subscribe to event handlers
    }
}

Please note that Control might be null when if (e.OldElement != null) is true and should not normally be recreated in this situation.

See also Xamarin.Forms iOS ButtonRenderer.

like image 86
Benl Avatar answered Nov 12 '22 18:11

Benl


The ?. can be found at C# 6.0 docs:

You need to check every access of variables to ensure you are not dereferencing null. The null conditional operator makes those checks much easier and fluid.

Simply replace the member access . with ?.

In short:

segmentedControl.TintColor = e.NewElement?.TintColor.ToUIColor();

is the same as:

  if (e.NewElement != null)
  {
      segmentedControl.TintColor = e.NewElement.TintColor.ToUIColor();
  }

So yes, you are correct, it should not execute if there is NewElement.

like image 3
Barr J Avatar answered Nov 12 '22 19:11

Barr J