Falko Falko - 1 month ago 27
Android Question

Force redraw of Xamarin.Forms View with custom renderer

I have a visual element

MyButton
with a custom renderer implemented for iOS.

Shared:

namespace RendererTest
{
public class MyButton: Button
{
public Color BoundaryColor { get; set; }
}

public static class App
{
public static Page GetMainPage()
{
var button = new MyButton { Text = "Click me!", BoundaryColor = Color.Red };
button.Clicked += (sender, e) => (sender as MyButton).BoundaryColor = Color.Blue;
return new ContentPage { Content = button };
}
}
}


iOS:

[assembly:ExportRenderer(typeof(MyButton), typeof(MyButtonRenderer))]

namespace RendererTest.iOS
{
public class MyButtonRenderer: ButtonRenderer
{
public override void Draw(RectangleF rect)
{
using (var context = UIGraphics.GetCurrentContext()) {
context.SetFillColor(Element.BackgroundColor.ToCGColor());
context.SetStrokeColor((Element as MyButton).BoundaryColor.ToCGColor());
context.SetLineWidth(10);
context.AddPath(CGPath.FromRect(Bounds));
context.DrawPath(CGPathDrawingMode.FillStroke);
}
}
}
}


When pressing the button, the red boundary should become blue. Apparently the renderer does not notice the changed property. How can I trigger a redraw?



(This example is for iOS. But my question applies to Android as well.)

Answer

Two modifications were required:

  1. Call OnPropertyChanged within the setter of the BoundaryColor property:

    public class MyButton: Button
    {
        Color boundaryColor = Color.Red;
    
        public Color BoundaryColor {
            get {
                return boundaryColor;
            }
            set {
                boundaryColor = value;
                OnPropertyChanged();  // <-- here
            }
        }
    }
    
  2. Subscribe to the event within the OnElementChanged method of MyButtonRenderer:

    public class MyButtonRenderer: ButtonRenderer
    {
        protected override void OnElementChanged(ElementChangedEventArgs<Button> e)
        {
            base.OnElementChanged(e);
            Element.PropertyChanged += (s_, e_) => SetNeedsDisplay();  // <-- here
        }
    
        public override void Draw(RectangleF rect)
        {
            // ...
        }
    }
    

Note: It seems to be important to subscribe within OnElementChanged and not the constructor. Otherwise a System.Reflection.TargetInvocationException is raised.