Xamarin.Forms for UWP has a peculiarity which is bugging me since the beginning – if you reference local images they must all be stored in the root folder of the UWP project as docs confirm. This is an odd choice if you consider that on iOS you can use Asset Catalog Image Sets and on Android you customarily use Resources/drawable-xx folders. The appropriate location on UWP would be the Assets folder or any other folder for that matter. Having all images in root makes the project quite messy. So what can we do about that?
Quick workarounds
Many developers choose quick workarounds for this issue. A pretty frequent one is using the OnPlatform
functionality to declare a specific path for UWP:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<Image> | |
<Image.Source> | |
<OnPlatform x:TypeArguments="FileImageSource"> | |
<On Platform="iOS, Android" Value="image.png" /> | |
<On Platform="UWP" Value="Assets/image.png" /> | |
</OnPlatform> | |
</Image.Source> | |
</Image> |
The problem with this is that it gets old pretty quickly. It is just too much boilerplate for a simple difference in the path, and it must be done everywhere.
The second solution is to use a Shared project for UWP. This allows us to keep the images separate, and they will then be “merged” with the project files during compilation. This is a tad nicer but introduces another project node in your solution.
My workaround
The workaround I have decided to use is to introduce a custom markup extension which will provide the appropriate image sources in XAML more straightforwardly. In the shared project add the following:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[ContentProperty(nameof(Path))] | |
public class FileImageExtension : IMarkupExtension<FileImageSource> | |
{ | |
public string Path { get; set; } | |
public FileImageSource ProvideValue(IServiceProvider serviceProvider) => Convert(Path); | |
public static FileImageSource Convert(string path) | |
{ | |
if (path == null) throw new InvalidOperationException($"Cannot convert null to {typeof(ImageSource)}"); | |
if ( Device.RuntimePlatform == Device.UWP ) | |
{ | |
path = System.IO.Path.Combine("Assets/", path); | |
} | |
return (FileImageSource)ImageSource.FromFile(path); | |
} | |
object IMarkupExtension.ProvideValue(IServiceProvider serviceProvider) | |
{ | |
return ProvideValue(serviceProvider); | |
} | |
} |
This markup extension is merely checking if the runtime platform is UWP and in such case combines the Assets
folder with the provided relative path.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!– add XML namespace declaration to page root –> | |
<ContentPage … | |
xmlns:xaml="clr-namespace:YourApp.ExtensionNamespace;assembly=YourApp"> | |
<Image Source="{xaml:FileImage image.png}" /> |
Future
The great news is that this is being worked on and will be built into the platform as UWP platform-specific. The pull request for this change is almost finished, and you can follow the progress here. The only thing you will need to do is to set the default image directory, ideally when your app initializes:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Application.Current.On<Windows>().SetImageSearchDirectory("Assets"); |
Further
I will post a continuation of this article which will add yet another improvement to the FileImageExtension
we have written. This will allow us to create “pseudo-folder” structure to make image management even simpler and will make using the extension instead of simple path even more worthwhile.
1 thought on “How to keep local images out of root folder of Xamarin.Forms UWP project”