raV720
raV720

Reputation: 594

Create missing custom renderer for single platform

I need to use PdfViewer from some library. This library provides control for Android, IOS, UWP platforms but not for WPF platform. How to create a renderer only for WPF platform?

WPF renderer:

[assembly: ExportRenderer(typeof(PdfViewer), typeof(PdfViewerRenderer))]
public class PdfViewerRenderer : Xamarin.Forms.Platform.WPF.ViewRenderer<PdfViewer, WPF.PlatformImplementations.PdfViewer>
{

}

WPF control implementation:

namespace WPF.PlatformImplementations
{ 
    public class PdfViewer 
    { 
     //custom implementation of pdf viewer for wpf
    } 
}

Net standard library contains PdfViewer which derives from SfPdfViewer from a library:

namespace Views
{ 
    public class PdfViewer : SfPdfViewer 
    { 
     //implementation of pdf viewer using base class from multiplatform library
    } 
}

The above approach does not work for WPF and works for other platforms. When I change inheritance from SfPdfViewer to Xamarin.Forms.View then it works for WPF but obviously does not work for other platforms. So the problem is to make renderer mechanizm to recognize Views.PdfViewer as control based on Xamarin.Forms.View.

Upvotes: 3

Views: 478

Answers (2)

Sharada
Sharada

Reputation: 13601

Every Xamarin.Forms control has a accompanying renderer for each platform that needs to create an instance of a native control.

SfPdfViewer is a xamarin-forms control that only represents the shared UI state, and interaction logic. It needs a corresponding native control to actually render the view and interact with WPF platform.

In order to do that, first step would be to choose a WPF based control for viewing PDF files. For this example I have used WPF Pdf Viewer by SyncFusion; but any other option - probably like an embedded WebView or an open-source option like PdfiumViewer should also work.

Step 1:

Install nuget package for Syncfusion.PdfViewer.WPF in WPF project

Step 2:

Wire up the with forms-element (SfPdfViewer) with native-control (PdfViewerControl) using platform renderer.

[assembly: ExportRenderer(typeof(SfPdfViewer), typeof(SfPdfViewerRenderer))]
namespace PdfViewer.Demo.WPF
{
    public class SfPdfViewerRenderer
        : ViewRenderer<SfPdfViewer, Syncfusion.Windows.PdfViewer.PdfViewerControl>
    {
        Syncfusion.Windows.PdfViewer.PdfViewerControl _nativeControl;

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

            // If new forms element attached, wire up the native control
            if (e.NewElement != null)
            {
                _nativeControl = new Syncfusion.Windows.PdfViewer.PdfViewerControl();
                SetNativeControl(_nativeControl);
            }
            // Otherwise perform some cleanup
            else
            {
                if(_nativeControl != null)
                {
                    _nativeControl.Unload();
                    _nativeControl = null;
                }
            }

            UpdateNativeControlProperties();
        }

        /// <summary>
        /// Basically sync property values from forms-element (SfPdfViewer) 
        /// to native-control (PdfViewerControl)
        /// In this example - we only sync with 'input file stream'
        /// </summary>
        private void UpdateNativeControlProperties()
        {
            if (Element != null && Element.InputFileStream != null)
            {
                Control.Load(Element.InputFileStream);
            }
            else
            {
                Control.Unload();
            }
        }

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

            if (e.PropertyName == nameof(SfPdfViewer.InputFileStream))
            {
                UpdateNativeControlProperties();
            }
        }
    }
}

Reference sample project uploaded on Github.

Demo

Upvotes: 3

Saamer
Saamer

Reputation: 5099

One way to do this is to create a CustomPdfViewer interface in the Core project, and have individual implementations in each native project using Dependency injection.

In the native implementations for each of the iOS, Android, & UWP projects, just make it use the PdfViewer library that works for you.

And then for WPF, just use the SfPdfViewer instead.

Eg: You can take a look at the official documentation here on DependencyService with examples

Once you do this, you will have your solution. But if you want to go one step further and access the CustomPdfViewer you created inside your XAML, you can use "XAML Markup Extensions" represented by the term local as you can also see in the example here.

Upvotes: 0

Related Questions