This is Xamarin's bug tracking system. For product support, please use the support links listed in your Xamarin Account.
Bug 53515 - F#: Loading images from files causes a null reference exception
Summary: F#: Loading images from files causes a null reference exception
Status: RESOLVED FIXED
Alias: None
Product: Forms
Classification: Xamarin
Component: Android (show other bugs)
Version: 2.3.3
Hardware: PC Windows
: --- normal
Target Milestone: ---
Assignee: Bugzilla
URL:
Depends on:
Blocks:
 
Reported: 2017-03-20 10:52 UTC by Rob Lyndon
Modified: 2017-04-12 11:08 UTC (History)
2 users (show)

See Also:
Tags:
Is this bug a regression?: ---
Last known good build:


Attachments

Description Rob Lyndon 2017-03-20 10:52:54 UTC
1. Create a new project, of type "Visual F# -> Android -> Blank App (Android)"

2. Add the Xamarin.Forms Nuget package.

3. Replace the code in MainActivity.fs with

namespace <<MyProjectName>>

open System

open Xamarin.Forms.Platform.Android

open Android.App
open Android.Content
open Android.OS
open Android.Runtime
open Android.Views
open Android.Widget

type MainPage() =
    inherit Xamarin.Forms.ContentPage()
    let layout = new Xamarin.Forms.StackLayout()
    do layout.Children.Add(new Xamarin.Forms.Button(Image = new Xamarin.Forms.FileImageSource(File = "Icon.jpg")))
    do base.Content <- layout

type App() =
    inherit Xamarin.Forms.Application()
    do base.MainPage <- new MainPage()

[<Activity (Label = "FSharpResourcesDemo", MainLauncher = true)>]
type MainActivity () =
    inherit FormsApplicationActivity()
    override this.OnCreate (bundle) =
        base.OnCreate (bundle)
        Xamarin.Forms.Forms.Init(this, bundle)
        this.LoadApplication(new App())

Run the app.

Boom.

Note that ImageSource.FromFile() breaks in exactly the same way.
Comment 1 Rob Lyndon 2017-03-20 11:00:34 UTC
Also, ImageSource.FromResource() seems to be working correctly.

For convenience, I have added a project called FSharpResourcesDemo to my open source project, https://github.com/SpiegelSoft/Astrid. If you clone this solution, you can set this project to be your startup project.

I'm giving a presentation at the F# Exchange on the 7th of April, so a proposed fix or a valid workaround by then would be very useful.
Comment 2 Rob Lyndon 2017-03-20 11:02:27 UTC
There is a partial workaround: by adding

type Drawable() = inherit Resource_Drawable()
type Id() = inherit Resource_Id()

to your MainActivity file, the error happens further down the stack, but it doesn't come anywhere near solving the problem.
Comment 3 Rob Lyndon 2017-03-20 11:36:26 UTC
I've found the cause.

In the Resource.designer.fs project, members are defined as follows:

    static member abc_ab_share_pack_mtrl_alpha = 2130837504

In F#, these are interpreted as properties, rather than fields.

So here is the fix for the Xamarin Forms project:

In the ResourceManager class in the Xamarin.Forms.Core project, replace the method

public static void Init(Assembly masterAssembly)
{
	DrawableClass = masterAssembly.GetTypes().FirstOrDefault(x => x.Name == "Drawable");
	ResourceClass = masterAssembly.GetTypes().FirstOrDefault(x => x.Name == "Id");
}


with

public static void Init(Assembly masterAssembly)
{
	DrawableClass = masterAssembly.GetTypes().FirstOrDefault(x => x.Name == "Drawable" || x.Name == "Resource_Drawable");
	ResourceClass = masterAssembly.GetTypes().FirstOrDefault(x => x.Name == "Id" || x.Name == "Resource_Id");
}

and then replace the GetId() method

static int GetId(Type type, string propertyName)
{
	FieldInfo[] props = type.GetFields();
	FieldInfo prop = props.Select(p => p).FirstOrDefault(p => p.Name == propertyName);
	if (prop != null)
		return (int)prop.GetValue(type);
	return 0;
}

with

static int GetId(Type type, string propertyName)
{
	FieldInfo[] props = type.GetFields();
	FieldInfo prop = type.GetFields().FirstOrDefault(p => p.Name == propertyName) || type.GetProperties().FirstOrDefault(p => p.Name == propertyName);
	if (prop != null)
		return (int)prop.GetValue(type);
	return 0;
}

I'll submit a pull request.
Comment 4 Rob Lyndon 2017-03-20 11:37:16 UTC
Apologies -- in the code above, replace || with the null propagation operator ??
Comment 5 Rob Lyndon 2017-03-20 18:06:02 UTC
The final implementation is 

static int GetId(Type type, string propertyName)
{
	object value = type.GetFields().FirstOrDefault(p => p.Name == propertyName)?.GetValue(type)
		?? type.GetProperties().FirstOrDefault(p => p.Name == propertyName)?.GetValue(type);
	if (value != null)
		return (int)value;
	return 0;
}

The pull request is https://github.com/xamarin/Xamarin.Forms/pull/825
Comment 6 Rui Marinho 2017-04-12 11:08:11 UTC
Should be fixed on 2.3.5-pre1

Note You need to log in before you can comment on or make changes to this bug.