This tutorial shows how to use the LEADTOOLS SDK in a C# Xamarin application and display images in an Image Viewer.
Overview | |
---|---|
Summary | This tutorial covers how to display images in a LEADTOOLS Image Viewer a C# Xamarin application |
Completion Time | 30 minutes |
Visual Studio Project | Download tutorial project (463 KB) |
Platform | C# Xamarin Cross-Platform Application |
IDE | Visual Studio 2017, 2019 |
Development License | Download LEADTOOLS |
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 Image in an Image Viewer - Xamarin C# tutorial.
In Visual Studio, create a new C# Xamarin project, and add the following necessary LEADTOOLS references.
The references needed depend upon the purpose of the project. For this project, the following NuGet package are needed:
Leadtools.Viewer.Controls.Xamarin
Leadtools.Formats.Raster.Common
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
Adding LEADTOOLS NuGet references and setting a license are covered in more detail in the Add References and Set a License tutorial.
With the project created, the references added, and the license set, coding can begin.
In the Solution Explorer, open the MainPage.xaml.cs
and ensure that the following are added to the using
area at the top of the code:
using Xamarin.Essentials;
Add the following global variable:
bool PermissionsGranted;
Add a MainPage_Appearing()
event handler to the MainPage
class.
public MainPage()
{
// Keep rest of code as is
InitializeComponent();
Appearing += MainPage_Appearing;
}
Use the code below to check the Storage Read permission for the application and prompt the user to enable them before loading the ContentPage with the Image Viewer control.
private async void MainPage_Appearing(object sender, EventArgs e)
{
PermissionsGranted = await VerifyPermissions();
if (PermissionsGranted == false)
return;
else
App.Current.MainPage = new ImageViewerPage();
}
private async Task<bool> VerifyPermissions()
{
try
{
PermissionStatus status = PermissionStatus.Unknown;
// Storage Read permission
status = await Permissions.CheckStatusAsync<Permissions.StorageRead>();
if (status != PermissionStatus.Granted)
{
await DisplayAlert("Storage Read Permission Required", "This app will load images from storage for display", "OK");
status = await Permissions.RequestAsync<Permissions.StorageRead>();
if (status != PermissionStatus.Granted)
return false;
}
// All needed permissions granted
return true;
}
catch (Exception ex)
{
await DisplayAlert("Error", ex.ToString(), "OK");
return false;
}
}
In the Solution Explorer, right-click on Info.plist
. Click Open With.... Select Generic PList Editor, then click OK.
Click the +
button on the last row in the editor. This will be called Custom Property
, with the Type set to String and an empty value. Click on the Property
name and the dropdown will appear. From that dropdown select the Privacy - Photo Library Usage Description
. Then enter a description into the Value
column for why the application wants to access that given feature.
With the project created, the references added, and the license set, coding can begin.
In Solution Explorer, right-Click on the base C# project and select Add -> New Item. Select the Content Page
option and name the class ImageViewerPage.xaml
Open ImageViewer.xaml
and add the following code inside the ContentPage
to add a load image button and Image Viewer container.
<ContentPage.Content>
<Grid x:Name="imageViewerContainer" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" BackgroundColor="Black">
<Grid.RowDefinitions>
<RowDefinition x:Name="row0" Height="*"/>
<RowDefinition x:Name="loadButtonRow" Height="50"/>
</Grid.RowDefinitions>
<Grid Margin="10,5" Grid.Row="1" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Button x:Name="_loadFromGallery" Text="Load" BorderColor="LightCoral" Clicked="_loadFromGallery_Clicked" HorizontalOptions="FillAndExpand"/>
</Grid>
</Grid>
</ContentPage.Content>
Right click on the page and select View Code to bring up the code behind ImageViewerPage.xaml
. Add the following statements to the using
block at the top of ImageViewerPage.xaml.cs
.
// Using block at the top
using System;
using System.IO;
using System.Threading.Tasks;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
using Leadtools;
using Leadtools.Codecs;
using Leadtools.Controls;
using DataService;
Add the below code to initialize the Image Viewer.
// Add this global variable
private ImageViewer _imageViewer;
private RasterImage _image;
Add a new method called InitImageViewer()
and call it inside the ImageViewerPage()
method after InitializeComponent();
call.
private void InitImageViewer()
{
_imageViewer = new ImageViewer
{
ViewHorizontalAlignment = ControlAlignment.Center,
ViewVerticalAlignment = ControlAlignment.Center,
BackgroundColor = Color.FromHex("#1E1E1E"),
VerticalOptions = LayoutOptions.FillAndExpand,
HorizontalOptions = LayoutOptions.FillAndExpand,
Margin = new Thickness(0, 0),
AutoDisposeImages = true,
};
Grid.SetRow(_imageViewer, 0);
Grid.SetRowSpan(_imageViewer, 1);
ImageViewerPanZoomInteractiveMode panZoom = new ImageViewerPanZoomInteractiveMode();
_imageViewer.InteractiveModes.Add(panZoom);
imageViewerContainer.Children.Add(_imageViewer);
}
Add a new namespace to the bottom of ImageViewerPage.xaml.cs
called DataService
. This namespace will be the dependency service for the picture picker.
// Dependency service
namespace DataService
{
public interface IPicturePicker
{
Task<Stream> GetImageStreamAsync();
}
}
Right-click on <Project>.Android
and select Add -> New Item. Select the Class
option and name the class PicturePickerImplementation.cs
, then click Add.
Add the below using
statements to the new class:
using System.IO;
using System.Threading.Tasks;
using Xamarin.Forms;
using Android.Content;
using <Project_Namespace>.Droid; // Replace with project's namespace
using DataService;
Add a new method to the PicturePickerImplementation
class called GetImageStreamAsync()
that returns a Task<Stream>
. Add the below code to implement the PicturePicker
on an Android device.
[assembly: Dependency(typeof(PicturePickerImplementation))]
namespace <Project_Namespace>.Droid // Replace with project's namespace
{
class PicturePickerImplementation : IPicturePicker
{
public Task<Stream> GetImageStreamAsync()
{
Intent intent = new Intent();
intent.SetType("image/*");
intent.SetAction(Intent.ActionGetContent);
MainActivity activity = MainActivity.Instance;
if (activity.PickImageTaskCompletionSource == null || activity.PickImageTaskCompletionSource.Task.IsCompleted || activity.PickImageTaskCompletionSource.Task.IsCanceled)
{
activity.StartActivityForResult(Intent.CreateChooser(intent, "Select Picture"), MainActivity.PickImageId);
activity.PickImageTaskCompletionSource = new TaskCompletionSource<Stream>();
return activity.PickImageTaskCompletionSource.Task;
}
else
{
return activity.PickImageTaskCompletionSource.Task;
}
}
}
}
Make sure to add the [assembly: Dependency(typeof(PicturePickerImplementation))]
line of code so that it can find the implementation of the DependencyService
.
In the Solution Explorer, open MainActivity.cs
. Add the below code to the OnActivityResult
method to handle the image selected with the picture picker, gather the stream, and hand the stream to the task completion source.
// Add these variables used in `OnActivityResult()`
public TaskCompletionSource<Stream> PickImageTaskCompletionSource { set; get; }
public static readonly int PickImageId = 1000;
protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
{
base.OnActivityResult(requestCode, resultCode, data);
if (requestCode == PickImageId)
{
if (resultCode == Result.Ok && data != null)
{
Android.Net.Uri uri = data.Data;
Stream stream = ContentResolver.OpenInputStream(uri);
PickImageTaskCompletionSource.SetResult(stream);
}
else
{
PickImageTaskCompletionSource.SetResult(null);
}
}
}
Right-click on <Project>.iOS
and select Add -> New Item. Select the Class
option and name the class PicturePickerImplementation.cs
, then click Add.
Add the below using
statements to the new class:
using System;
using System.IO;
using System.Threading.Tasks;
using Xamarin.Forms;
using Foundation;
using UIKit;
using Display_Images_in_an_Image_Viewer.iOS;
using DataService;
Add a new method to the PicturePickerImplementation
class called GetImageStreamAsync()
that returns a Task<Stream>
. Add the below code to implement the PicturePicker
on an iOS device.
[assembly: Dependency(typeof(PicturePickerImplementation))]
namespace Display_Images_in_an_Image_Viewer.iOS
{
[Xamarin.Forms.Internals.Preserve(AllMembers = true)]
class PicturePickerImplementation : IPicturePicker
{
TaskCompletionSource<Stream> taskCompletionSource;
UIImagePickerController imagePicker;
public Task<Stream> GetImageStreamAsync()
{
try
{
// Create and define UIImagePickerController
imagePicker = new UIImagePickerController
{
SourceType = UIImagePickerControllerSourceType.PhotoLibrary,
MediaTypes = UIImagePickerController.AvailableMediaTypes(UIImagePickerControllerSourceType.PhotoLibrary)
};
// Set event handlers
imagePicker.FinishedPickingMedia += OnImagePickerFinishedPickingMedia;
// Present UIImagePickerController;
UIWindow window = UIApplication.SharedApplication.KeyWindow;
var viewController = window.RootViewController;
viewController.PresentModalViewController(imagePicker, true);
// Return Task object
taskCompletionSource = new TaskCompletionSource<Stream>();
return taskCompletionSource.Task;
}
catch (Exception ex) { Console.WriteLine(ex.Message); return null; }
}
void OnImagePickerFinishedPickingMedia(object sender, UIImagePickerMediaPickedEventArgs args)
{
UIImage image = args.EditedImage ?? args.OriginalImage;
if (image != null)
{
// Convert UIImage to .NET Stream object
NSData data = image.AsJPEG(1);
Stream stream = data.AsStream();
// Set the Stream as the completion of the Task
taskCompletionSource.SetResult(stream);
}
else
{
taskCompletionSource.SetResult(null);
}
imagePicker.DismissModalViewController(true);
}
}
}
Make sure to add the [assembly: Dependency(typeof(PicturePickerImplementation))]
line of code so that it can find the implementation of the DependencyService
.
In the Solution Explorer, open AppDelegate.cs
. Add the below line to the FinishedLaunching()
method:
Leadtools.Controls.iOS.Assembly.Use();
In the Solution Explorer, open ImageViewerPage.xaml.cs
. Add a new Clicked
event handler for loadFromGallery
, if it does not already exist. Add the below code inside the handler to load the image from the device's gallery.
private async void _loadFromGallery_Clicked(object sender, EventArgs e)
{
try
{
Stream imageStream = await DependencyService.Get<IPicturePicker>().GetImageStreamAsync();
if (imageStream != null)
{
using (RasterCodecs codecs = new RasterCodecs())
{
codecs.Options.Load.XResolution = 200;
codecs.Options.Load.YResolution = 200;
_image = codecs.Load(imageStream);
_imageViewer.Image = _image;
}
}
}
catch (Exception ex)
{
await DisplayAlert("Error", ex.ToString(), "OK");
}
}
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 runs and it will ask to allow Storage
permissions which is required. For testing, click the Load button at the bottom of the device's screen and select any of the images in the device's gallery. The image will then be loaded as a RasterImage and will be displayed in the ImageViewer.
Android:
iOS:
This tutorial showed how to load and display images. In addition, it showed how to use the ImageViewer
and RasterCodecs
classes.