DEV Community

Masui Masanori
Masui Masanori

Posted on

[C#][Uno Platform] Create desktop applications on Ubuntu 2

Intro

This time, I will try installing NuGet packages, using DI, firing life-cycle events.

Environments

  • Ubuntu ver.21.10
  • .NET ver.6.0.101
  • Uno Platform ver.4.0.11

Installing NuGet packages

To log, using DI, etc., I want to install NuGet packages.
When I install packages by NuGet Package Manager, I have to select the target project.

To use the packages in all projects, I have to install into "UnoSolutionTemplate.net6.csproj".

If the package is only for specific platform, I have to add in specific projects.

Life-cycle events

Sometimes, I want to execute something on loading the page, closing the page, and so on.

Shared project

In "UnoSample.Shared", I can use "Loaded" event in MainPage.xaml.cs to execute on the page loaded.

MainPage.xaml.cs

using Windows.UI.Xaml.Controls;
using UnoSample.ViewModels;

namespace UnoSample
{
    public sealed partial class MainPage : Page
    {
        public MainPage()
        {
            this.Loaded += (sender, e) => Console.WriteLine("Loaddeded");
            this.InitializeComponent();
            this.DataContext = new MainPageViewModel();
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

How about closing applications event?
I can't find any events what are called on closing.

So I think I shall use closing events in every specific platform projects.
For example, WPF can use "OnClosing" and "OnClosed" methods.

Executing on closing

When I open MainPage, these classes are instantiated in sequence.

Image description

In "UnoSample.Skia.Gtk" project, there is only "Main" method.
So I can execute closing operations after "host.Run()".

Program.cs

using GLib;
using Uno.UI.Runtime.Skia;
using UnoSample.Skia.Gtk.MotionControllers;

namespace UnoSample.Skia.Gtk
{
    class Program
    {
        static void Main(string[] args)
        {
            // I also can use "using statement"
            using(var motionController = new MotionController())
            {
                ExceptionManager.UnhandledException += delegate (UnhandledExceptionArgs expArgs)
                {
                    expArgs.ExitApplication = true;
                };
                var host = new GtkHost(() => new App(), args);
                host.Run();
                // execute closing operations.
            }
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Call methods in UnoSample.Shared classes

How shall I call methods in UnoSample.Shared classes?

I can access App.xaml.cs like below.

App.xaml.cs

...
namespace UnoSample
{
    public sealed partial class App : Application
    {
        private Window _window;
        public void Greet(string message)
        {
            Console.WriteLine($"SharedApp {message}");
        }
...
Enter fullscreen mode Exit fullscreen mode

Program.cs

...
    class Program
    {
        static void Main(string[] args)
        {
            using(var motionController = new MotionController())
            {
...
                var host = new GtkHost(() => new App(), args);
                host.Run();
                // Because I can't get instances by "App.Current" before executing "host.Run()",
                // I can only execute closing operations from Program.cs. 
                ((App)App.Current).Greet("Call from Gtk");
            }
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

DI (Dependency Injection)

Because I want to avoid the classes are coupled tightly, I try using DI framework.
In this time, I tried using "Microsoft.Extensions.DependencyInjection(ver.6.0.0)"

Because I couldn't find the way to inject View and their code behinds, I put IServiceProvider into App.xaml.cs of the shared project and get injected instances from it.

Program.cs

using GLib;
using Microsoft.Extensions.DependencyInjection;
using Uno.UI.Runtime.Skia;
using UnoSample.ViewModels;

namespace UnoSample.Skia.Gtk
{
    class Program
    {
        static void Main(string[] args)
        {
            var servicesProvider = BuildDi(args);
            using (servicesProvider as IDisposable)
            {
...
                var host = new GtkHost(() => new App(servicesProvider), args);
                host.Run();
            }
        }
        private static IServiceProvider BuildDi(string[] args)
        {
            var services = new ServiceCollection();
            services.AddScoped<MainPageViewModel>();
            return services.BuildServiceProvider();
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

App.xaml.cs

...
namespace UnoSample
{
    public sealed partial class App : Application
    {
        private Window? _window;
        public IServiceProvider ServiceProvider { get; }

        public App(IServiceProvider serviceProvider)
        {
            this.ServiceProvider = serviceProvider;
            InitializeLogging();

            this.InitializeComponent();
...
Enter fullscreen mode Exit fullscreen mode

MainPage.xaml.cs

using Windows.UI.Xaml.Controls;
using UnoSample.ViewModels;

namespace UnoSample
{
    public sealed partial class MainPage : Page
    {
        public MainPage()
        {
            this.InitializeComponent();
            this.DataContext = ((App)App.Current).ServiceProvider.GetService(typeof(MainPageViewModel));
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

I'm not sure if this is the best method.
Maybe it's better to use another DI framework。

If I will find more better one, I will edit this post or write new post.

Discussion (0)