Using Forms9Patch.ImageSource
Xamarin Forms implementation of resolution specific image sourcing relies upon native APIs and schema for iOS, Android, and Windows UWP multi-screen image management described here. This requires storing your iOS images in your iOS platform project using the native iOS schema, storing your Android images in your Android platform project using the Android native schema, and storing your UWP images in your UWP platform project using the UWP native schema. In other words, duplicative efforts to get the same results on both Android, iOS, and UWP. Forms9Patch.ImageSource simplifies Xamarin.Forms.ImageSource capabilities by bringing multi-screen image management to your Shared Library, PCL Assemblies, and .NetStandard projects - so you only have to generate and configure your app's image resources once.
Embedded Resource Id Naming Convention
To use Forms9Patch multi-platform image management, you will need to store your image resource files as Embedded Resources. You can do this by:
VisualStudio Mac: Right clicking on image file and selecting Build Action / EmbeddedResource
VisualStudio 2017: Right clicking on the image file and selecting Properties and then, in the "Properties" panel, modify the Build Action field to EmbeddedResource
Each embedded resources has an EmbeddedResourceId string that is used to reference it at runtime. An EmbeddedResourceId is a sequence of strings joined by a period (.)as a separator. This series of strings starts with the assembly name for the project in which the embedded resource is in, appended by the names of each folder in that project's folder structure for the embedded resource file, and ends with the embedded resource's file name.
project_assembly_name.project_path.base_image_file_name.extension
For example, if your project's assembly name is PizzaMaker
and has an image file named slice.png
in its Resources/Images
folder, the EmbeddedResourceId
of that file would be PizzaMaker.Resources.Images.slice.png
. If you're using VisualStudio Mac, don't worry too much about this because the EmbeddedResourceId is in the Properties panel in the ResourceID field. You can find the Properties panel for a file by, in the Solution Explorer pad, right clicking on a file and selecting Properties.
To facilitate runtime selection (for device resolution and type) between multiple renditions of an image, Forms9Patch prescribes how to name each rendition of your image files (and thus their EmbeddedResourceId).
File Name: base_image_name[resolution_modifier][device_modifier].extension
EmbeddedResourceId: project_assembly_name.project_path.base_image_name[resolution_modifier][device_modifier].extension
Notice the addition of the optional [resolution_modifier]
and [device_modifier]
modifiers. These modifiers will be familiar to iOS developers. Likewise, Android developers can see analogs in Android's Resources
sub-folder naming conventions. Valid values for the optional modifiers are:
Resolution Modifiers
Forms9Patch | notes | iOS analog | Android analog |
---|---|---|---|
none | fallback image if no other is available | none (default) | none (default) |
@¾x |
low density screens (~120 dpi) | n/a | -ldpi |
@1x |
explicit medium density (~160 dpi) | none (default) | -mdpi |
@1½x |
(~240 dpi) | n/a | -hdpi |
@2x |
most common (~320) | @2x |
-xhdpi |
@3x |
rare (~480 dpi) | @3x |
-xxhdpi |
@4x |
future proof (~640 dpi) | @4x |
-xxxhdpi |
Device Modifiers
Forms9Patch | notes | iOS analog | Android analog |
---|---|---|---|
none | fallback image if no other is available | none (default) | none (default) |
~phone |
maps to Xamarin Forms' TargetIdiom.Phone |
~iPhone |
none (default) |
~tablet |
maps to Xamarin Forms' TargetIdiom.Tablet |
~iPad |
sw600dp |
~desktop |
maps to Xamarin Forms' TargetIdiom.Desktop |
n/a | n/a |
~tv |
maps to Xamarin Forms' TargetIdiom.TV |
n/a | n/a |
~unsupported |
maps to Xamarin Forms' TargetIdiom.Unsupported |
n/a | n/a |
Forms9Patch.ImageSource
Now that you've saved your image files for each device type and resolution rendition you want to support, next comes using those images in your app. This is where Forms9Patch.ImageSource
comes in. Forms9Patch.ImageSource
extends Xamarin.Forms.ImageSource
by adding the FromMultiResource
static method, adding the following functionality to Xamarin.Forms.ImageSource.FromResource:
- Finds the best fit image among the EmbeddedResource image renditions
- Eliminates the need for explicitly specifying the image file's extension
This is not without compromise. Because Xamarin.Forms.ImageSource.FromResource
is intended to be multi-platform, it only supports the following image file
formats: NinePatch (.9.png
), .png
, .jpg
, .jpeg
, .gif
, .bmp
, .bmpf
and .svg
. Note that, unlike the other image file formats, NinePatch and .svg
images can only be used with Forms9Patch image elements.
Code Example
The following is a very simple app to demonstrate how Forms9Patch.ImageSource
uses Forms9Patch's Embedded Resource ResourceID naming convention to provide the best Embedded Resource image to Xamarin.Forms.Image or Forms9Patch.Image.
First, we start with a set of multi-device / multi-resolution images:
50x50 | 100x100 | 150x150 |
---|---|---|
image.png | image@2x.png | image@3x.png |
image~tablet.png | image@2x~tablet.png | image@3x~tablet.png |
Next, we create our app:
- Create a new Xamarin Forms cross-platform (.Netstandard or PCL) application named
MyDemoApp
with theMyDemoApp
assembly namespace - Create a Resources directory in the app's cross-platform project (the .NetStandard or PCL project)
- Save the above six images in the cross-platform project's
Resources
directory - Set the
Build Action
toEmbeddedResource
for those images - Modify your app code
MyDemoApp.cs
as shown, below:
Forms9Patch.ImageSource.FromMultiResource
will choose among the available embedded resource renditions in the cross-platform assembly and select the rendition that works best with the current device (tablet or phone; low / medium / high resolution).
XAML Example
In Xamarin.Forms, access to embedded resources from XAML requires some additional work. Unfortunately, Forms9Patch is no different. As with Xamarin.Forms, you will need (in the same assembly as your embedded resource images) a simple custom XAML markup extension to load images using their ResourceID.
[ContentPropert ("Source")]
public class ImageMultiResourceExtension : IMarkupExtension
{
public string Source { get; set; }
public object ProvideValue (IServiceProvider serviceProvider)
{
if (Source == null)
return null;
// Do your translation lookup here, using whatever method you require
var imageSource = Forms9Patch.ImageSource.FromMultiResource(Source);
return imageSource;
}
}
Once you have the above, you can load your embedded resource images as shown in the below example. Be sure to add a namespace to your XAML for the assembly that contains both the above MarkupExtension and your EmbeddedResources (named local
in the below example).
<?xml version="1.0" encoding="UTF-8"?>
<ContentPage
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:MyXamlDemo;assembly=MyXamlDemo"
x:Class="MyXamlDemo.MyPage"
Padding="5, 20, 5, 5">
<ScrollView>
<ScrollView.Content>
<StackLayout>
<Label Text="Xamarin.Image"/>
<Image Source="{local:ImageMultiResource Forms9PatchDemo.Resources.image}"/>
</StackLayout>
</ScrollView.Content>
</ScrollView>
</ContentPage>