Display Files in a Document Viewer - Xamarin C#

This tutorial shows how to load a document into a Document Viewer in a C# Xamarin application using the LEADTOOLS SDK.

Overview  
Summary This tutorial covers how to display files in a Document Viewer in a C# Xamarin application.
Completion Time 45 minutes
Visual Studio Project Download tutorial project (422 KB)
Platform C# Xamarin Cross-Platform Application
IDE Visual Studio 2017, 2019
Development License Download LEADTOOLS

Required Knowledge

Get familiar with the basic steps of creating a project by reviewing the Add References and Set a License tutorial, before working on the Display Files in a Document Viewer - Xamarin C# tutorial.

Create the Project and Add LEADTOOLS References

Start with a copy of the project created in the Add References and Set a License tutorial. If you do not have that project, follow the steps in that tutorial to create it.

The references needed depend upon the purpose of the project. References can be added by NuGet references.

This tutorial requires the following NuGet packages:

Set the License File

The License unlocks the features needed for the project. It must be set before any toolkit function is called. For details including tutorials for different platforms, refer to Setting a Runtime License.

There are two types of runtime licenses:

Note

How to properly add LEADTOOLS NuGet and local references is covered in the Add References and Set a License tutorial.

Initialize the Document Viewer

With the project created, the references added, and the license set, coding can begin.

In the Solution Explorer, open MainPage.xaml. Add the following code inside the ContentPage to add two grids, one for the DocumentViewer and one for the thumbnails, and add a Load button.

<ContentPage.Content> 
   <Grid x:Name="documentViewerContainer" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" BackgroundColor="Black"> 
      <Grid.RowDefinitions> 
         <RowDefinition x:Name="viewGridRow" Height="6*"/> 
         <RowDefinition x:Name="thumbGridRow" Height="2*"/> 
         <RowDefinition x:Name="loadButtonRow" Height="Auto" /> 
      </Grid.RowDefinitions> 
 
      <Grid Grid.Row="0" x:Name="viewGrid" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand"> 
         <Grid.ColumnDefinitions> 
            <ColumnDefinition Width="*"/> 
         </Grid.ColumnDefinitions> 
      </Grid> 
 
      <Grid Grid.Row="1" x:Name="thumbnailsGrid" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand"> 
         <Grid.ColumnDefinitions> 
            <ColumnDefinition Width="*"/> 
         </Grid.ColumnDefinitions> 
      </Grid> 
 
      <Grid Margin="10,5" Grid.Row="2" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand"> 
         <Grid.ColumnDefinitions> 
            <ColumnDefinition Width="*"/> 
         </Grid.ColumnDefinitions> 
         <Button x:Name="_loadDocument" Text="Load" BorderColor="LightCoral" Clicked="_loadDocument_Clicked" HorizontalOptions="FillAndExpand"/> 
      </Grid> 
   </Grid> 
</ContentPage.Content> 

Right-click on the page and select View Code or press F7, to bring up the code behind MainPage.xaml. Make sure that the following statements are added to the using block at the top.

C#
// Using block at the top 
using System; 
using System.IO; 
using System.ComponentModel; 
using System.Text; 
using System.Threading.Tasks; 
using Xamarin.Forms; 
using Leadtools; 
using Leadtools.Document; 
using Leadtools.Caching; 
using Leadtools.Document.Viewer; 
using Leadtools.Controls; 
using DataService; 

Add the below global variables to the MainPage class.

C#
private LEADDocument virtualDocument;  
private FileCache cache;  
private DocumentViewer documentViewer;  

Add the below code to initialize the Document Viewer.

Add a new method to the MainPage class named InitDocumentViewer(). Call this method inside the MainPage() method after the InitializeComponent() call. Add the below code to initialize the DocumentViewer thumbnails container.

C#
private void InitDocumentViewer() 
{ 
   var createOptions = new DocumentViewerCreateOptions(); 
   // Set the UI part where the Document Viewer is displayed 
   createOptions.ViewContainer = viewGrid; 
   // Set the UI part where the Thumbnails are displayed 
   createOptions.ThumbnailsContainer = thumbnailsGrid; 
   // Not using annotations for now 
   createOptions.UseAnnotations = false; 
 
   // Now create the viewer 
   documentViewer = DocumentViewerFactory.CreateDocumentViewer(createOptions); 
   documentViewer.View.ImageViewer.Zoom(ControlSizeMode.FitAlways, 1.0, documentViewer.View.ImageViewer.DefaultZoomOrigin); 
   documentViewer.View.ImageViewer.ViewLayout = new ImageViewerSingleViewLayout(); 
   ImageViewerPanZoomInteractiveMode _mode = new ImageViewerPanZoomInteractiveMode(); 
   documentViewer.View.ImageViewer.InteractiveModes.Add(_mode); 
   documentViewer.Thumbnails.ImageViewer.ViewLayout = new ImageViewerHorizontalViewLayout(); 
 
   cache = new FileCache 
   { 
      CacheDirectory = DependencyService.Get<IGetPaths>().GetCachePath() 
   }; 
   virtualDocument = DocumentFactory.Create(new CreateDocumentOptions() { Cache = cache, UseCache = true }); 
} 

Add a new namespace to the bottom of MainPage.xaml.cs called DataService. This namespace will be the dependency service for the methods that have different implementations on Android and iOS.

C#
// Dependency service  
namespace DataService 
{ 
   public interface IGetPaths 
   { 
      String GetAppPath(); 
      String GetCachePath(); 
   } 
   public interface IDocumentPicker 
   { 
      Task<Stream> GetDocumentStreamAsync(); 
   } 
} 

Add the DocumentPicker Implementation Class (Android)

Right-click on <Project>.Android and select Add -> New Item. Select the Class option and name the class DocumentPickerImplementation.cs, then click Add.

Add DocumentPickerImplementation.cs to Android project

Add the below using statements to the new class:

C#
using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.IO; 
using System.Threading.Tasks; 
 
using Android.App; 
using Android.Content; 
using Android.OS; 
using Android.Runtime; 
using Android.Views; 
using Android.Widget; 
 
using Xamarin.Forms; 
using DataService; 
using Display_Files_in_a_Document_Viewer.Droid; 

Add a method to the DocumentPickerImplementation class called GetDocumentStreamAsync() that returns a Task<Stream>. Use the code below to implement the DocumentPicker on an Android device.

C#
[assembly: Dependency(typeof(DocumentPickerImplementation))] 
namespace Display_Files_in_a_Document_Viewer.Droid 
{ 
   class DocumentPickerImplementation : IDocumentPicker 
   { 
      public Task<Stream> GetDocumentStreamAsync() 
      { 
         String[] supportedMimeTypes = 
            { 
            "application/pdf" 
         }; 
 
         Intent intent = new Intent(); 
         intent.SetType(supportedMimeTypes.Length == 1 ? supportedMimeTypes[0] : "*/*"); 
         if (supportedMimeTypes.Length > 0) 
         { 
            intent.PutExtra(Intent.ExtraMimeTypes, supportedMimeTypes); 
         } 
         intent.SetAction(Intent.ActionOpenDocument); 
         intent.AddFlags(ActivityFlags.GrantReadUriPermission); 
 
         MainActivity activity = MainActivity.Instance; 
         if (activity.PickDocumentTaskCompletionSource == null || activity.PickDocumentTaskCompletionSource.Task.IsCompleted || activity.PickDocumentTaskCompletionSource.Task.IsCanceled) 
         { 
            activity.StartActivityForResult(Intent.CreateChooser(intent, "Select Document"), MainActivity.PickDocumentId); 
            activity.PickDocumentTaskCompletionSource = new TaskCompletionSource<Stream>(); 
            return activity.PickDocumentTaskCompletionSource.Task; 
         } 
         else 
         { 
            return activity.PickDocumentTaskCompletionSource.Task; 
         } 
      } 
   } 
} 

Note

Make sure to add the [assembly: Dependency(typeof(DocumentPickerImplementation))] line of code so that it can find the implementation of the DependencyService.

In the Solution Explorer, open MainActivity.cs. Make sure that the below using statements are added.

C#
using System; 
using System.IO; 
using System.Threading.Tasks; 
 
using Android.App; 
using Android.Content.PM; 
using Android.Runtime; 
using Android.Views; 
using Android.Widget; 
using Android.OS; 
using Android.Content; 

Add Instance = this; to the OnCreate method to allow the code to call the MainActivity's instance.

C#
// Add this variable used in `OnCreate()` 
public static MainActivity Instance { get; private set; } 
 
protected override void OnCreate(Bundle savedInstanceState) 
{ 
   TabLayoutResource = Resource.Layout.Tabbar; 
   ToolbarResource = Resource.Layout.Toolbar; 
 
   base.OnCreate(savedInstanceState); 
 
   global::Xamarin.Forms.Forms.Init(this, savedInstanceState); 
   Instance = this; 
   LoadApplication(new App()); 
} 

Add the code below to the OnActivityResult method to handle the file selected with the DocumentPicker, gather the stream, and hand the stream to the task completion source.

C#
// Add these variables used in `OnActivityResult()` 
public TaskCompletionSource<Stream> PickDocumentTaskCompletionSource { set; get; } 
public static readonly int PickDocumentId = 1000; 
 
protected override void OnActivityResult(int requestCode, Result resultCode, Intent data) 
{ 
   base.OnActivityResult(requestCode, resultCode, data); 
 
   if (requestCode == PickDocumentId) 
   { 
      if (resultCode == Result.Ok && data != null) 
      { 
         Android.Net.Uri uri = data.Data; 
         Stream stream = ContentResolver.OpenInputStream(uri); 
 
         PickDocumentTaskCompletionSource.SetResult(stream); 
      } 
      else 
      { 
         PickDocumentTaskCompletionSource.SetResult(null); 
      } 
   } 
} 

Add the GetPaths Implementation Class (Android)

Right-click on <Project>.Android and select Add -> New Item. Select the Class option and name the class GetPathsImplementation.cs, then click Add.

Add the below using statements to the new class:

C#
using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.IO; 
using System.Threading.Tasks; 
 
using Android.App; 
using Android.Content; 
using Android.OS; 
using Android.Runtime; 
using Android.Views; 
using Android.Widget; 
 
using Xamarin.Forms; 
using DataService; 
using Display_Files_in_a_Document_Viewer.Droid; 

Add two methods to the GetPathsImplementation class named GetAppPath() and GetCachePath(). Add the code below to return the local paths used by the application and used for the cache, respectively.

C#
[assembly: Dependency(typeof(GetPathsImplementation))] 
namespace Display_Files_in_a_Document_Viewer.Droid 
{ 
   class GetPathsImplementation : IGetPaths 
   { 
      public String GetAppPath() 
      { 
         Java.IO.File basedir = Android.OS.Environment.ExternalStorageDirectory; 
         Java.IO.File leadDir = new Java.IO.File($"{basedir}/Leadtools"); 
         if (!leadDir.Exists()) leadDir.Mkdir(); 
         Java.IO.File appDir = new Java.IO.File($"{leadDir}/DocumentViewerDemo"); 
         if (!appDir.Exists()) appDir.Mkdir(); 
         return appDir.AbsolutePath; 
      } 
      public String GetCachePath() 
      { 
         Java.IO.File droidCacheDir = new Java.IO.File($@"{GetAppPath()}/Cache"); 
         if (!droidCacheDir.Exists()) droidCacheDir.Mkdir(); 
         return droidCacheDir.AbsolutePath; 
      } 
   } 
} 

Note

Make sure to add the [assembly: Dependency(typeof(GetPathsImplementation))] line of code so that it can find the implementation of the DependencyService.

Add the DocumentPicker Implementation Class (iOS)

Right-click on <Project>.iOS and select Add -> New Item. Select the Class option and name the class DocumentPickerImplementation.cs, then click Add.

Add DocumentPickerImplementation.cs to iOS project

Add the below using statements to the new class:

C#
using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.IO; 
using System.Threading.Tasks; 
 
using Foundation; 
using UIKit; 
 
using Xamarin.Forms; 
using Display_Files_in_a_Document_Viewer.iOS; 
using DataService; 
using MobileCoreServices; 

Add a new method to the DocumentPickerImplementation class called GetDocumentStreamAsync() that returns a Task<Stream>. Add the below code to implement the DocumentPicker on an iOS device.

C#
[assembly: Dependency(typeof(DocumentPickerImplementation))] 
namespace Display_Files_in_a_Document_Viewer.iOS 
{ 
   [Xamarin.Forms.Internals.Preserve(AllMembers = true)] 
   class DocumentPickerImplementation : IDocumentPicker 
   { 
 
      TaskCompletionSource<Stream> taskCompletionSource; 
      UIDocumentPickerViewController documentPicker; 
      public Task<Stream> GetDocumentStreamAsync() 
      { 
         try 
         { 
            var allowedUTIs = new string[] 
            { 
               UTType.PDF 
            }; 
 
            // Create and define UIDocumentPickerViewController 
            documentPicker = new UIDocumentPickerViewController(allowedUTIs, UIDocumentPickerMode.Import); 
            // Set event handlers 
            documentPicker.WasCancelled += DocumentPicker_WasCancelled; 
            documentPicker.DidPickDocumentAtUrls += DocumentPicker_DidPickDocumentAtUrls; 
            // Present UIImagePickerController; 
            UIWindow window = UIApplication.SharedApplication.KeyWindow; 
            var viewController = window.RootViewController; 
            viewController.PresentModalViewController(documentPicker, true); 
            // Return Task object 
            taskCompletionSource = new TaskCompletionSource<Stream>(); 
            return taskCompletionSource.Task; 
         } 
         catch (Exception ex) { Console.WriteLine(ex.Message); return null; } 
      } 
 
 
      private void DocumentPicker_DidPickDocumentAtUrls(object sender, UIDocumentPickedAtUrlsEventArgs e) 
      { 
         string filename = e.Urls[0].LastPathComponent; 
         if (filename != null) 
         { 
            NSData data = NSData.FromUrl(e.Urls[0]); 
            Stream stream = data.AsStream(); 
            taskCompletionSource.SetResult(stream); 
         } 
         else 
         { 
            taskCompletionSource.SetResult(null); 
         } 
         documentPicker.DismissModalViewController(true); 
      } 
 
      private void DocumentPicker_WasCancelled(object sender, EventArgs e) 
      { 
         taskCompletionSource.SetResult(null); 
         documentPicker.DismissModalViewController(true); 
      } 
   } 
} 

Note

Make sure to add the [assembly: Dependency(typeof(DocumentPickerImplementation))] line of code so that it can find the implementation of the DependencyService.

In the Solution Explorer, open AppDelegate.cs. Add the code below to the FinishedLaunching() method:

C#
public override bool FinishedLaunching(UIApplication app, NSDictionary options) 
{ 
   global::Xamarin.Forms.Forms.Init(); 
   Leadtools.Controls.iOS.Assembly.Use(); 
   Leadtools.Document.Pdf.Assembly.Use(); 
   LoadApplication(new App()); 
 
   return base.FinishedLaunching(app, options); 
} 

Add the GetPaths Implementation Class (iOS)

Right-click on <Project>.iOS and select Add -> New Item. Select the Class option and name the class GetPathsImplementation.cs, then click Add.

Add the below using statements to the new class:

C#
using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.IO; 
using System.Threading.Tasks; 
 
using Foundation; 
using UIKit; 
 
using Xamarin.Forms; 
using Display_Files_in_a_Document_Viewer.iOS; 
using DataService; 
using MobileCoreServices; 

Add two methods to the GetPathsImplementation class named GetAppPath() and GetCachePath(). Add the code below to return the local paths used by the application and used for the cache, respectively.

C#
[assembly: Dependency(typeof(GetPathsImplementation))] 
namespace DocViewer.iOS 
{ 
   [Xamarin.Forms.Internals.Preserve(AllMembers = true)] 
   class GetPathsImplementation : IGetPaths 
   { 
      public String GetAppPath() 
      { 
         var documents = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments); 
         var appDir = System.IO.Path.Combine(documents, "DocViewer"); 
         if (!Directory.Exists(appDir)) Directory.CreateDirectory(appDir); 
         return appDir; 
      } 
      public String GetCachePath() 
      { 
         var cacheDir = System.IO.Path.Combine(GetAppPath(), "Cache"); 
         if (!Directory.Exists(cacheDir)) Directory.CreateDirectory(cacheDir); 
         return cacheDir; 
      } 
   } 
} 

Note

Make sure to add the [assembly: Dependency(typeof(GetPathsImplementation))] line of code so that it can find the implementation of the DependencyService.

Add the Load Document Code

In the Solution Explorer, navigate back to MainPage.xaml.cs. Add the below code inside the _loadDocument_Clicked handler, created in MainPage.xaml, to load the document from the device's gallery and set the document in the viewer.

C#
private async void _loadDocument_Clicked(object sender, EventArgs e) 
{ 
   try 
   { 
      Stream documentStream = await DependencyService.Get<IDocumentPicker>().GetDocumentStreamAsync(); 
 
      if (documentStream != null) 
      { 
         LEADDocument leadDocument = DocumentFactory.LoadFromStream( 
            documentStream, 
            new LoadDocumentOptions 
            { 
               UseCache = true, 
               Cache = cache, 
               LoadEmbeddedAnnotations = true 
            }); 
         virtualDocument.Pages.Clear(); 
         for (int i = 0; i < leadDocument.Pages.Count; i++) 
         { 
            virtualDocument.Pages.Add(leadDocument.Pages[i]); 
         } 
      } 
      documentViewer.BeginUpdate(); 
      documentViewer.SetDocument(virtualDocument); 
      documentViewer.View.Invalidate(); 
      if (documentViewer.Thumbnails != null) 
         documentViewer.Thumbnails.Invalidate(); 
      documentViewer.EndUpdate(); 
   } 
   catch (Exception ex) 
   { 
      await DisplayAlert("Error", ex.ToString(), "OK"); 
   } 
} 

Run the Project

Select the desired project (iOS or Android) and run the project by pressing F5, or by selecting Debug -> Start Debugging.

If the steps were followed correctly, the application will run, displaying that the license was set properly. To test, click on the Load button to bring up the DocumentPicker implemented for the desired project. Select a PDF to load and the document will appear in the viewer.

Android:

Android Device Screen Capture

iOS:

iOS Device Screen Capture

Wrap-up

This tutorial showed how to initialize the Xamarin Document Viewer, load a document, and set the document into the viewer. It also covered how to use the DocumentViewer and LEADDocument classes.

See Also

Help Version 21.0.2021.7.27
Products | Support | Contact Us | Intellectual Property Notices
© 1991-2021 LEAD Technologies, Inc. All Rights Reserved.

Products | Support | Contact Us | Intellectual Property Notices
© 1991-2021 LEAD Technologies, Inc. All Rights Reserved.