Page thumbnails are miniature previews of the pages in a document. You can use page thumbnails to navigate to a required page quickly. We can achieve the thumbnail view at the application level by exporting PDF pages as images.
In this blog, we’ll see how to do this using our Syncfusion Xamarin PDF Viewer component.
First, create a new Xamarin app and install Syncfusion Xamarin.Forms PDF Viewer and ListView (to show the thumbnail images) NuGet packages in it.
Then, create a content view in the ThumbnailView.xaml file by adding Xamarin.Forms ListView as a child to it.
Refer to the following code example.
<ContentView xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:syncfusion="clr-namespace:Syncfusion.ListView.XForms;assembly=Syncfusion.SfListView.XForms" xmlns:local="clr-namespace:PdfViewerThumbnail" x:Class="PdfViewerThumbnail.Views.ThumbnailView"> <ContentView.Resources> <ResourceDictionary> <local:ImageSourceConverter x:Key="ImageSourceConverter"/> <DataTemplate x:Key="itemTemplate"> <ViewCell > <ViewCell.View> <Grid x:Name="grid" RowSpacing="10" ColumnSpacing="10" Margin="10,10,10,10"> <Image Source="{Binding ImageSource, Converter={StaticResource ImageSourceConverter}}" Aspect="Fill"/> </Grid> </ViewCell.View> </ViewCell> </DataTemplate> </ResourceDictionary> </ContentView.Resources> <syncfusion:SfListView x:Name="listView" x:FieldModifier="public" Margin="10" VerticalOptions="CenterAndExpand" HorizontalOptions="CenterAndExpand" RowSpacing="10" ColumnSpacing="10" ItemTapped="listView_ItemTapped" ItemSize="150" LoadMoreOption="Auto" LoadMoreCommand="{Binding LoadMoreItemsCommand}" LoadMoreCommandParameter="{Binding Source={x:Reference Name=listView}}" LoadMorePosition="Bottom" ItemTemplate="{StaticResource itemTemplate}" Orientation="Horizontal" ItemsSource="{Binding ImageCollection}" > <syncfusion:SfListView.LoadMoreTemplate> <DataTemplate> <Grid IsVisible="{Binding IsBusy, Source={x:Reference Name=listView}}" > <syncfusion:LoadMoreIndicator WidthRequest="50" HeightRequest="50" Color="White" IsRunning="True" VerticalOptions="CenterAndExpand"/> </Grid> </DataTemplate> </syncfusion:SfListView.LoadMoreTemplate> </syncfusion:SfListView> </ContentView>
The ImageSourceConverter class helps convert the thumbnail images obtained as byte[] to the Stream object.
Refer to the following code example.
/// <summary> /// Represents the image conversion methods for the thumbnail view. /// </summary> public class ImageSourceConverter: IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { var image = value as byte[]; if (image == null) return null; return ImageSource.FromStream(() => new MemoryStream(image)); } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { throw new NotImplementedException(); } }
Next, create the main content page (say PdfViewerView.xaml). Add the Syncfusion Xamarin.Forms PDF Viewer component to display the PDF document and the created thumbnail view as the child elements to the main content page.
Refer to the following code example.
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="PdfViewerThumbnail.PdfViewerView" xmlns:syncfusion="clr-namespace:Syncfusion.SfPdfViewer.XForms;assembly=Syncfusion.SfPdfViewer.XForms" xmlns:local="clr-namespace:PdfViewerThumbnail" xmlns:views="clr-namespace:PdfViewerThumbnail.Views"> <ContentPage.BindingContext> <local:PdfViewerThumbnailViewModel></local:PdfViewerThumbnailViewModel> </ContentPage.BindingContext> <Grid x:Name="MainGrid"> <Grid.RowDefinitions> <RowDefinition Height="{Static GridLength.Star}" /> </Grid.RowDefinitions> <Grid Grid.Row="0" x:Name="MainContent" VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand"> <Grid.RowDefinitions> <RowDefinition Height="50"/> <RowDefinition Height="*" /> <RowDefinition Height="50" /> </Grid.RowDefinitions> <Grid Grid.Row="0" x:Name="AppBar" BackgroundColor="#1777D6" HorizontalOptions="FillAndExpand"> <Grid.ColumnDefinitions> <ColumnDefinition Width="*"></ColumnDefinition> <ColumnDefinition Width="50"></ColumnDefinition> <ColumnDefinition Width="50"></ColumnDefinition> </Grid.ColumnDefinitions> <Label Grid.Column="0" Grid.Row="0" x:Name="thumbnail" Text="Thumbnail View" Margin="5,0,0,0" TextColor="White" FontSize="Medium" VerticalOptions="Center"></Label> </Grid> <Grid Grid.Row="1" x:Name="PdfViewerGrid"> <syncfusion:SfPdfViewer x:Name="pdfViewerControl" PageNumber="{Binding PageNumber}" InputFileStream="{Binding PdfDocumentStream}" /> </Grid> <Grid Grid.Row="2" x:Name="PageThumbnailsGrid" HorizontalOptions="FillAndExpand" BackgroundColor="#1777D6"> <views:ThumbnailView x:Name="pageThumbnails"></views:ThumbnailView> </Grid> </Grid> </Grid> </ContentPage>
Now, we can export the PDF document pages as images using the ExportAsImage or ExportAsImageAsync methods.
To load the populated images on-demand, use the LoadMoreCommand method of the Xamarin.Forms ListView component.
When the PDF document is loaded, at first, just a few pages will be exported and added to the thumbnail view. Then, the miniature pages for the thumbnails will be added on-demand using the LoadMoreCommand execution method.
Implement the following code logic to export the images in the pdfViewerControl_DocumentLoaded event handler.
private void pdfViewerControl_DocumentLoaded(object sender, EventArgs args) { int initialThumbnailsCount = 8; // PdfViewerControl object that helps to navigate to the selected page index. pageThumbnails.PdfViewerControl = this.pdfViewerControl; // Export PDF pages as images and add them to the thumbnail view. var pageCount = pdfViewerControl.PageCount > initialThumbnailsCount ? initialThumbnailsCount : pdfViewerControl.PageCount; (BindingContext as PdfViewerThumbnailViewModel).AddThumbnailImages(pageThumbnails.listView, 0, pageCount - 1); }
To load more thumbnail items on demand, bind an execution method (like LoadMoreItems) to the LoadMoreCommand in the view model class PdfViewerThumbnailViewModel. Then, implement the following business logic.
/// <summary> /// Constructor of the view model class. /// </summary> public PdfViewerThumbnailViewModel() { LoadMoreItemsCommand = new Command<object>(LoadMoreItems); //Accessing the PDF document added as embedded resource as stream. m_pdfDocumentStream = typeof(App).GetTypeInfo().Assembly.GetManifestResourceStream("PdfViewerThumbnail.Assets.GIS Succinctly.pdf"); } /// <summary> /// Load more items to the thumbnail view on demand. /// </summary> /// <param name="obj">List view</param> internal void LoadMoreItems(object obj) { var startPageIndex = this.ThumbnailImages.Count; if (startPageIndex == this.PdfViewer.PageCount) { return; } int endPageIndex = startPageIndex + loadMoreItemsCount; endPageIndex = endPageIndex > this.PdfViewer.PageCount - 1 ? this.PdfViewer.PageCount - 1 : endPageIndex - 1; AddThumbnailImages(obj as SfListView, startPageIndex, endPageIndex); } /// <summary> /// Export PDF pages as images and add them to the thumbnail view. /// </summary> /// <param name="listView">list view object</param> /// <param name="startPageIndex">start page index of the document</param> /// <param name="endPageIndex">end page index of the document</param> internal async void AddThumbnailImages(SfListView listView, int startPageIndex, int endPageIndex) { if (!listView.IsBusy) { listView.IsBusy = true; Stream[] imageStreamList = null; imageStreamList = await PdfViewer?.ExportAsImageAsync(startPageIndex, endPageIndex, 0.3f); foreach (Stream imageStream in imageStreamList) { imageStream.Position = 0; byte[] bytes = ReadBytes(imageStream); this.ThumbnailImages.Add(new ThumbnailModel(bytes)); } listView.IsBusy = false; } } /// <summary> /// Convert image stream to byte array. /// </summary> /// <param name="imageStream">image stream</param> /// <returns>image byte array</returns> internal byte[] ReadBytes(Stream imageStream) { byte[] buffer = new byte[16 * 1024]; using (MemoryStream ms = new MemoryStream()) { int read; while ((read = imageStream.Read(buffer, 0, buffer.Length)) > 0) { ms.Write(buffer, 0, read); } return ms.ToArray(); } }
Create the model class ThumbnailModel to maintain the properties for each thumbnail image.
public class ThumbnailModel: INotifyPropertyChanged { private byte[] _imageSource; public byte[] ImageSource { get { return _imageSource; } set { _imageSource = value; this.RaisedOnPropertyChanged("ImageSource"); } } public event PropertyChangedEventHandler PropertyChanged; public ThumbnailModel(byte[] imageSource) { ImageSource = imageSource; } public void RaisedOnPropertyChanged(string _PropertyName) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(_PropertyName)); } } }
Selecting a thumbnail image should navigate you to the corresponding page in the PDF Viewer. This can be done by assigning the selected item index of the ListView to the PDF Viewer’s PageNumber property.
Implement the following code logic to navigate to the selected thumbnail image in the listView_ItemTapped event handler of the ThumbnailView partial class.
private void listView_ItemTapped(object sender, Syncfusion.ListView.XForms.ItemTappedEventArgs e) { ThumbnailModel thumbnailModel = e.ItemData as ThumbnailModel; //Selected page index of the thumbnail view. int pageIndex = (BindingContext as PdfViewerThumbnailViewModel).ThumbnailImages.IndexOf(thumbnailModel); //Bindable property of PdfViewerControl's PageNumber. (BindingContext as PdfViewerThumbnailViewModel).PageNumber = pageIndex + 1; }
Check out the example application to view PDF pages as thumbnails using our Xamarin.Forms PDF Viewer component.
Execution of this example app will provide output like in the following screenshot.
Thanks for reading! We have seen how to view PDF pages as thumbnails using our Syncfusion Xamarin.Forms PDF Viewer component. This will help you easily navigate PDF pages. Try out the steps given in this blog post and share your feedback in the comment section below.
Check out our Xamarin.Forms PDF Viewer documentation to explore other features with code examples.
For existing customers, the new version is available for download from the License and Downloads page for Essential Studio®. If you aren’t a customer yet, you can try our 30-day free trial to check out these features.
For questions, you can contact us through our support forum, support portal, or feedback portal. We are always happy to assist you!