2D Graphics for Xamarin.Forms apps (iOS, Android, UWP, WPF)

I'm looking for a nuget that provides simple 2d graphics for cross-platform projects. I want to use it to make some custom graphical controls (like a checkbox, gradient boxes, etc.

SkiaSharp is great, but it doesn't work with WPF, and unfortunately I need to support WPF.

I recently found NGraphics and NControls, but again, I don't thing UWP or WPF are supported.
Nothing is rendered in UWP or WPF, and some posts say I need to call NControlViewRenderer.Init() after Forms.Init(), but no such method exists leading me to believe UWP and WPF aren't supported.

Does anyone have any recommendations?

Best Answer

  • CaseCase US ✭✭✭
    Accepted Answer

    After some digging, I've got SkiaSharp working in WPF!
    This isn't my code, I found it by @mattleibow in the SkiaSharp issues, "Add a WPF renderer for Xamarin.Forms"

    SKCanvasViewRendererBase.cs

    using System;
    using System.ComponentModel;
    using SkiaSharp;
    using SkiaSharp.Views.Forms;
    using Xamarin.Forms.Platform.WPF;
    using SKFormsView = SkiaSharp.Views.Forms.SKCanvasView;
    using SKNativeView = SkiaSharp.Views.WPF.SKElement;
    using SKNativePaintSurfaceEventArgs = SkiaSharp.Views.Desktop.SKPaintSurfaceEventArgs;
    
    namespace MyLibrary.SkiaRenderer
    {
        public abstract class SKCanvasViewRendererBase<TFormsView, TNativeView> : ViewRenderer<TFormsView, TNativeView>
            where TFormsView : SKFormsView
            where TNativeView : SKNativeView
        {
    
            protected SKCanvasViewRendererBase()
            {
                Initialize();
            }
    
            private void Initialize()
            {
            }
    
            protected override void OnElementChanged(ElementChangedEventArgs<TFormsView> e)
            {
                if (e.OldElement != null)
                {
                    var oldController = (ISKCanvasViewController)e.OldElement;
    
                    // unsubscribe from events
                    oldController.SurfaceInvalidated -= OnSurfaceInvalidated;
                    oldController.GetCanvasSize -= OnGetCanvasSize;
                }
    
                if (e.NewElement != null)
                {
                    var newController = (ISKCanvasViewController)e.NewElement;
    
                    // create the native view
                    if (Control == null)
                    {
                        var view = CreateNativeControl();
                        view.PaintSurface += OnPaintSurface;
                        SetNativeControl(view);
                    }
    
                    // set the initial values
                    Control.IgnorePixelScaling = e.NewElement.IgnorePixelScaling;
    
                    // subscribe to events from the user
                    newController.SurfaceInvalidated += OnSurfaceInvalidated;
                    newController.GetCanvasSize += OnGetCanvasSize;
    
                    // paint for the first time
                    OnSurfaceInvalidated(newController, EventArgs.Empty);
                }
    
                base.OnElementChanged(e);
            }
    
            protected virtual TNativeView CreateNativeControl()
            {
                return (TNativeView)Activator.CreateInstance(typeof(TNativeView));
            }
    
            protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
            {
                base.OnElementPropertyChanged(sender, e);
    
                if (e.PropertyName == SKFormsView.IgnorePixelScalingProperty.PropertyName)
                {
                    Control.IgnorePixelScaling = Element.IgnorePixelScaling;
                }
            }
    
            protected override void Dispose(bool disposing)
            {
                // detach all events before disposing
                var controller = (ISKCanvasViewController)Element;
                if (controller != null)
                {
                    controller.SurfaceInvalidated -= OnSurfaceInvalidated;
                    controller.GetCanvasSize -= OnGetCanvasSize;
                }
    
                var control = Control;
                if (control != null)
                {
                    control.PaintSurface -= OnPaintSurface;
                }
    
                base.Dispose(disposing);
            }
    
            private void OnPaintSurface(object sender, SKNativePaintSurfaceEventArgs e)
            {
                var controller = Element as ISKCanvasViewController;
    
                // the control is being repainted, let the user know
                controller?.OnPaintSurface(new SKPaintSurfaceEventArgs(e.Surface, e.Info));
            }
    
            private void OnSurfaceInvalidated(object sender, EventArgs eventArgs)
            {
                // repaint the native control
                Control.InvalidateVisual();
            }
    
            // the user asked for the size
            private void OnGetCanvasSize(object sender, GetPropertyValueEventArgs<SKSize> e)
            {
                e.Value = Control?.CanvasSize ?? SKSize.Empty;
            }
        }
    }
    

    SKCanvasViewRenderer.cs

    using MyLibrary.SkiaRenderer;
    using Xamarin.Forms.Platform.WPF;
    using SKFormsView = SkiaSharp.Views.Forms.SKCanvasView;
    using SKNativeView = SkiaSharp.Views.WPF.SKElement;
    
    [assembly: ExportRenderer(typeof(SKFormsView), typeof(SKCanvasViewRenderer))]
    namespace MyLibrary.SkiaRenderer
    {
        public class SKCanvasViewRenderer : SKCanvasViewRendererBase<SKFormsView, SKNativeView>
        {
        }
    }
    

Answers

  • Hi Case,

    i hope
    Syncfusion.Chart.Base will work for both. i am not conformed with it but you can try once.

  • CaseCase USMember ✭✭✭
    Accepted Answer

    After some digging, I've got SkiaSharp working in WPF!
    This isn't my code, I found it by @mattleibow in the SkiaSharp issues, "Add a WPF renderer for Xamarin.Forms"

    SKCanvasViewRendererBase.cs

    using System;
    using System.ComponentModel;
    using SkiaSharp;
    using SkiaSharp.Views.Forms;
    using Xamarin.Forms.Platform.WPF;
    using SKFormsView = SkiaSharp.Views.Forms.SKCanvasView;
    using SKNativeView = SkiaSharp.Views.WPF.SKElement;
    using SKNativePaintSurfaceEventArgs = SkiaSharp.Views.Desktop.SKPaintSurfaceEventArgs;
    
    namespace MyLibrary.SkiaRenderer
    {
        public abstract class SKCanvasViewRendererBase<TFormsView, TNativeView> : ViewRenderer<TFormsView, TNativeView>
            where TFormsView : SKFormsView
            where TNativeView : SKNativeView
        {
    
            protected SKCanvasViewRendererBase()
            {
                Initialize();
            }
    
            private void Initialize()
            {
            }
    
            protected override void OnElementChanged(ElementChangedEventArgs<TFormsView> e)
            {
                if (e.OldElement != null)
                {
                    var oldController = (ISKCanvasViewController)e.OldElement;
    
                    // unsubscribe from events
                    oldController.SurfaceInvalidated -= OnSurfaceInvalidated;
                    oldController.GetCanvasSize -= OnGetCanvasSize;
                }
    
                if (e.NewElement != null)
                {
                    var newController = (ISKCanvasViewController)e.NewElement;
    
                    // create the native view
                    if (Control == null)
                    {
                        var view = CreateNativeControl();
                        view.PaintSurface += OnPaintSurface;
                        SetNativeControl(view);
                    }
    
                    // set the initial values
                    Control.IgnorePixelScaling = e.NewElement.IgnorePixelScaling;
    
                    // subscribe to events from the user
                    newController.SurfaceInvalidated += OnSurfaceInvalidated;
                    newController.GetCanvasSize += OnGetCanvasSize;
    
                    // paint for the first time
                    OnSurfaceInvalidated(newController, EventArgs.Empty);
                }
    
                base.OnElementChanged(e);
            }
    
            protected virtual TNativeView CreateNativeControl()
            {
                return (TNativeView)Activator.CreateInstance(typeof(TNativeView));
            }
    
            protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
            {
                base.OnElementPropertyChanged(sender, e);
    
                if (e.PropertyName == SKFormsView.IgnorePixelScalingProperty.PropertyName)
                {
                    Control.IgnorePixelScaling = Element.IgnorePixelScaling;
                }
            }
    
            protected override void Dispose(bool disposing)
            {
                // detach all events before disposing
                var controller = (ISKCanvasViewController)Element;
                if (controller != null)
                {
                    controller.SurfaceInvalidated -= OnSurfaceInvalidated;
                    controller.GetCanvasSize -= OnGetCanvasSize;
                }
    
                var control = Control;
                if (control != null)
                {
                    control.PaintSurface -= OnPaintSurface;
                }
    
                base.Dispose(disposing);
            }
    
            private void OnPaintSurface(object sender, SKNativePaintSurfaceEventArgs e)
            {
                var controller = Element as ISKCanvasViewController;
    
                // the control is being repainted, let the user know
                controller?.OnPaintSurface(new SKPaintSurfaceEventArgs(e.Surface, e.Info));
            }
    
            private void OnSurfaceInvalidated(object sender, EventArgs eventArgs)
            {
                // repaint the native control
                Control.InvalidateVisual();
            }
    
            // the user asked for the size
            private void OnGetCanvasSize(object sender, GetPropertyValueEventArgs<SKSize> e)
            {
                e.Value = Control?.CanvasSize ?? SKSize.Empty;
            }
        }
    }
    

    SKCanvasViewRenderer.cs

    using MyLibrary.SkiaRenderer;
    using Xamarin.Forms.Platform.WPF;
    using SKFormsView = SkiaSharp.Views.Forms.SKCanvasView;
    using SKNativeView = SkiaSharp.Views.WPF.SKElement;
    
    [assembly: ExportRenderer(typeof(SKFormsView), typeof(SKCanvasViewRenderer))]
    namespace MyLibrary.SkiaRenderer
    {
        public class SKCanvasViewRenderer : SKCanvasViewRendererBase<SKFormsView, SKNativeView>
        {
        }
    }
    
Sign In or Register to comment.