SKCanvasView Fires Paint Surface repeatedly

I have a list of objects that are being stored in an ObservableCollection. When a particular item is selected from the list, it navigates to a Content page that contains a StackLayout. This contains three children, two of which are labels and a SKCanvasView

StackLayout sl = new StackLayout { Padding = 10 };
sl.Children.Add(new Label { 
    Text = "Status: " + PatientStatus.FromValue(patient.Status).ToString(), 
    FontSize = Device.GetNamedSize(NamedSize.Large, typeof(Label)) 
});
sl.Children.Add(new Label { 
    Text = "Completed: " + patient.Completed, 
    FontSize = Device.GetNamedSize(NamedSize.Large, typeof(Label)) 
});
sgl = new SKCanvasView();
sgl.PaintSurface += Sgl_PaintSurface;
sl.Children.Add(sgl);
Content = sl;

On initialization, the two labels are populated with the appropriate value from the passed object (patient). A copy of this is also stored in a global variable within the same class.

Sgl_PaintSurface fires fine, and it goes through the code, however, it continues firing again and again. If it does stop, then the application shows a blank canvas under the two labels. It does not contain any artifacts at all. The background is slightly different then the application, so I can see the outline of the box.

private void Sgl_PaintSurface(object sender, SKPaintSurfaceEventArgs e)
{
    var Status = PatientStatus.FromValue(pt.Status);
    var surface = e.Surface;
    var canvas = surface.Canvas;
    canvas.Clear(SKColors.White);

    var complete = new SKPaint
    {
        IsAntialias = true,
        Style = SKPaintStyle.Fill,
        Color = SKColors.Green,
        StrokeWidth = 5,
        TextSize = 15,
        TextAlign = SKTextAlign.Center
    };
    var active = new SKPaint
    {
        IsAntialias = true,
        Style = SKPaintStyle.Fill,
        Color = SKColors.Green,
        StrokeWidth = 5,
        TextSize = 15,
        FakeBoldText = true,
        TextAlign = SKTextAlign.Center
    };
    var incomplete = new SKPaint
    {
        IsAntialias = true,
        Style = SKPaintStyle.Fill,
        Color = SKColors.Silver,
        StrokeWidth = 5,
        TextSize = 15,
        TextAlign = SKTextAlign.Center
    };
    if (height > width)
        for (int x = 50; x <= (50 * 7); x = x + 50)
        {
            var currentState = Status == PatientStatus.FromOrder((x / 50) - 1) ? active : (Status > PatientStatus.FromOrder((x / 50) - 1) ? complete : incomplete);

            if (x < (50 * 7))
                canvas.DrawLine(100, x, 100, x + 50, currentState == active ? incomplete : currentState);
            canvas.DrawCircle(100, x, 10, currentState);
            canvas.DrawText(PatientStatus.FromOrder((x / 50) - 1).ToString(), 150, x + 3, currentState);
        }
    else
        for (int x = 100; x <= (100 * 7); x = x + 100)
        {
            var currentState = Status == PatientStatus.FromOrder((x / 100) - 1) ? active : (Status > PatientStatus.FromOrder((x / 100) - 1) ? complete : incomplete);

            if (x < (100 * 7))
                canvas.DrawLine(x, 100, x + 100, 100, currentState == active ? incomplete : currentState);
            canvas.DrawCircle(x, 100, 10, currentState);
            int stringLength = PatientStatus.FromOrder((x / 100) - 1).ToString().Length;
            canvas.DrawText(PatientStatus.FromOrder((x / 100) - 1).ToString(), x, 60, currentState);
        }
    //sgl.PaintSurface -= Sgl_PaintSurface;
}

I have this same code in a separate project, and it only fires the event once, which suggests an issue with the page load, but I am not certain. I have also tried to remove the event listener from the object after it executes initially, but that also produces a blank canvas.

I am testing this using an Android VM running in Parallels.

Any ideas on how to resolve this?

Best Answer

Answers

  • mattleibowmattleibow ZAXamarin Team Xamurai

    As you mention, the separate project that works, seems to point to something causing the paint to fire multiple times.

    What may cause the paint to fire, is canvas size changes. Maybe during the binding process, the labels are changing size and thus causing a layout - which may trigger a resize.

    How many times does it cause a repaint? In a continuous loop, or just a few/several times?

    I also notice this line:

    var Status = PatientStatus.FromValue(pt.Status);
    

    That appears to indicate that you are triggering a repaint from somewhere. What are the conditions for this and if you are doing something manually, what happens when you disable that code?

  • GrahamCampbell.0419GrahamCampbell.0419 USMember ✭✭
    edited August 2017

    The repaint seems to happen 2-3 times and then it displays nothing with the code listed above. I did make a small change that seems to have made a difference though. In the code I listed above, I had placed the canvas in a stack layout. When I placed it into a grid, either using C# or XAML, it displayed the rendered graphic correctly.

    I also attempted to place the canvas inside a grid containing rows and columns, and it also did not render. Even when using the grid, the event fires 3 times... the difference is that it actually renders on the screen where the stack layout does not

    I don't know if that is a limitation or a coding failure on my part (I am new to Xamarin). Ultimately, it is rendering correctly now, but I would prefer to put it into a Stack Layout from the c# side if it will support it.

    The line of code you listed before is basically a glorified enum. The class that it references returns an object from a list, nothing more. Incidentally, it worked correctly from the standalone program (which used a grid, not a stack layout), so I don't think that's the culprit.

    The first section of the code now reads:

            pt = patient;
            InitializeComponent();
            Title = "Patient: " + patient.Id.ToString();
            try
            {
                Grid g = new Grid();
                sgl = new SKCanvasView();
                sgl.PaintSurface += Sgl_PaintSurface;
                g.Children.Add(sgl);
                Content = g;
            }
            catch (Exception ex)
           {
                throw ex;
            }
    

    The only addition to the latter code is to include text to replace the labels listed in the stack view. This would compensate for your earlier suggestion that the size of the labels may be triggering a repaint of the canvas.

    I appreciate the help and the insights

  • That's it exactly. It still fires twice, but it is rendering in the stack layout, and it rendering perfectly.

    StackLayout sl = new StackLayout { Padding = 10 };
    sgl = new SKCanvasView {
        HorizontalOptions = LayoutOptions.FillAndExpand,
        VerticalOptions = LayoutOptions.FillAndExpand
    };
    sgl.PaintSurface += Sgl_PaintSurface;
    sl.Children.Add(sgl);
    Content = sl;
    

    Thanks very much for your assistance with this!

Sign In or Register to comment.