Writing Tizen .NET Applications in F#

4 minute read

Although C# is the only language officially supported by Tizen .NET, you can write applications in other languages such as F# and VB.NET. Ideally, applications written in interoperable .NET languages are compiled into common IL (intermediate language) code, which can be executed by any CLI-compliant runtime.

Note: Any examples in this article are for tech demo purposes only. Further tests and investigation on limitations are required to ensure operability in general cases.

F# (VB.NET) Templates

Find the projects in the following repository and compare them with the original template projects shipped with Visual Studio Tools for Tizen.

The first three applications target Xamarin.Forms UI frameworks, and the fourth is built on the Tizen.NUI framework. You can copy the .fsproj or .vbproj project files, the ` tizen-manifest.xml` , and other resources directly from the original projects.

I converted C# code into F# manually, line by line. F# comes with wide support for imperative programming features out of its functional features, so C# code can be readily switched into F#.

When it comes to XAML-based applications, however, it becomes a bit complicated, because the XAML code generator of Xamarin.Forms only supports generating code (.g.cs) in C#. I had to load XAML pages manually at runtime using LoadFromXaml() instead of InitializeComponent(), as follows:

C# (MainPage.xaml.cs in TizenXamlApp)

public partial class MainPage : ContentPage
{
    public MainPage()
    {
        InitializeComponent();
    }
}

F# (MainPage.xaml.fs in CrossXamlTemplate)

type MainPage() as this = inherit ContentPage()

    do this.LoadFromXaml(typeof<MainPage>) |> ignore

Screenshots

CrossTemplate (F#) CrossTemplate (VB) CrossXamlTemplate (F#) NUITemplate (F#)
CrossTemplate.FSharp CrossTemplate.VB CrossXamlTemplate.FSharp NUITemplate.FSharp

It becomes complicated when you have any reference in your program code to XAML elements. I investigate such a case in the following XStopWatch F# example.

XStopWatch (F#)

XStopWatch is one of my favorite applications for testing .NET on Tizen. It’s a good example for demonstrating Xamarin.Forms feature compatibility with F#.

Note: Only the comments in F# code were removed from the original C# code. Otherwise, the two projects have the same logic and resources.

How XStopWatch is different from the previous helloworld examples:

  • Use of P/Invoke to access native platform functions
  • Access to XAML elements from application code
  • Data binding with BindableProperty
  • Events

For the P/Invoke usage in F# code, see the instructions here.

To reference XAML elements in the program logic, we must first inflate the layout of the XAML code. In C#, InitializeComponent() worked for us, so we didn’t need something such as findViewById() in Android.

StopWatch.xaml.cs

public StopWatch()
{
    _mainStopWatch = new NStopWatch();
    _subStopWatch = new NStopWatch();

    InitializeComponent();

    Stop();
}

In F#, we have to instantiate every single element manually:

StopWatch.xaml.fs

type StopWatch() as this = inherit CirclePage()

    let _mainStopWatch = NStopWatch()
    let _subStopWatch = NStopWatch()

    do
        this.LoadFromXaml(typeof<StopWatch>) |> ignore
        Self <- this.FindByName<CirclePage>("Self")
        Timebar <- this.FindByName<CircleProgressBarSurfaceItem>("Timebar")
        RootView <- this.FindByName<AbsoluteLayout>("RootView")
        RedBar <- this.FindByName<Image>("RedBar")
        BlueBar <- this.FindByName<Image>("BlueBar")
        StateLabel <- this.FindByName<Label>("StateLabel")
        ResetOrLapLabel <- this.FindByName<Label>("ResetOrLapLabel")
        CueBtn <- this.FindByName<Image>("CueBtn")

        this.Stop()

No problem so far. However, what if we have to use data binding for dynamic interaction with XAML elements? Don’t worry. We can use BindableProperty to create any data binding, as we did in C#.

StopWatch.xaml.cs

public partial class StopWatch : CirclePage
{
    public static BindableProperty StateProperty = BindableProperty.Create(nameof(State), typeof(State), typeof(StopWatch), State.Stopped);
    public static BindableProperty AllTimeProperty = BindableProperty.Create(nameof(AllTime), typeof(TimeSpan), typeof(StopWatch), TimeSpan.Zero);

    public State State { get => (State)GetValue(StateProperty); set => SetValue(StateProperty, value); }
    public TimeSpan AllTime { get => (TimeSpan)GetValue(AllTimeProperty); set => SetValue(AllTimeProperty, value); }

    ...
}

StopWatch.xaml.fs

type StopWatch() as this = inherit CirclePage()

    static let stateProperty = BindableProperty.Create("State", typeof<State>, typeof<StopWatch>, State.Stopped)
    static let allTimeProperty = BindableProperty.Create("AllTime", typeof<TimeSpan>, typeof<StopWatch>, TimeSpan.Zero)

    static member StateProperty = stateProperty
    static member AllTimeProperty = allTimeProperty

    member this.State
        with get() : State = this.GetValue(stateProperty) :?> State
        and set(value : State) = this.SetValue(stateProperty, value)

    member this.AllTime
        with get() : TimeSpan = this.GetValue(allTimeProperty) :?> TimeSpan
        and set(value : TimeSpan) = this.SetValue(allTimeProperty, value)

    ...

However, I was unable to make the following element parse properly with the XAML compiler. The compiler was unable to locate a property (Time) in another page (LapsPage).

StopWatchApplication.xaml

<CarouselPage x:Name="RootPage">
    ...
    <local:LapsPage x:Name="Laps" Time="{Binding AllTime, Source={x:Reference StopWatch}}"/>
</CarouselPage>

Instead, I had to manually create a binding in the program.

StopWatchApplication.xaml.fs

type StopWatchApplication() as this = inherit Application()

    do
        Laps.SetBinding(LapsPage.TimeProperty, "AllTime")
        Laps.BindingContext <- StopWatch

Finally, we can also use Events in F#. To create and then invoke a custom event handler, follow the instructions in this guide or refer to the following code.

C# (StopWatch.xaml.cs)

// Define an EventHandler.
public event EventHandler StopPressed;

void Stop()
{
    // Invoke it.
    StopPressed?.Invoke(this, EventArgs.Empty);
}

F# (StopWatch.xaml.fs)

let stopPressed = Event<_>()

// Define a CLIEvent.
[<CLIEvent>]
member this.StopPressed = stopPressed.Publish

member this.Stop() =
    // Invoke it.
    stopPressed.Trigger(EventArgs.Empty)

Now, let’s install the application on a device. It works just like the original C# application.

XStopWatch (C#) XStopWatch (F#)
XStopWatch (F#) XStopWatch.FSharp

Advantages and Disadvantages

I went over a simple benchmark of the above XStopWatch application on my TW2 (Gear S3) device. Every assembly was fully compiled into a native image. The result is as follows:

Application Mean startup time
XStopWatch (C#) 1120 ms
XStopWatch (F#) 1896 ms

The F# version of XStopWatch had a significant overhead when launching (169% of the original application). Unfortunately, I have no real evidence about the root cause. There also was a large memory regression (up to 5 Mbytes), although it’s not mentioned in the above table. Therefore, I believe that F# in Tizen development has the following pros and cons:

Pros

  • More development options (opportunity for F# developers)

Cons

  • Performance
  • Limited support and learning materials
  • Potential unknown bugs

Conclusion

Although it’s possible to use F# in Tizen application development, I don’t recommend that you do so unless you are expert in F# use.

If you need help, please don’t hesitate to contact me.