Код
#Руководства

Не Windows единой: как писать кросс-платформенные приложения с GUI на C#

На C# можно создавать красивые приложения, но до недавних пор — не для всех платформ. Рассказываем, как писать одно приложение для всех ОС сразу.

 vlada_maestro / shutterstock

Microsoft выпустила уже третью версию кросс-платформенного .NET Core с открытым исходным кодом, но мы до сих пор не получили инструмента для создания графических интерфейсов.

Пока есть Xamarin, который можно использовать только для Windows 10 и мобильных устройств. Но что делать тем, кто хочет создавать графические интерфейсы для Linux или Mac OS?

Тут помогут фреймворки от сторонних разработчиков.

Какой фреймворк выбрать

Мне удалось найти два более-менее популярных фреймворка (оба основаны на Skia):

  1. SpaceVIL. Он привлёк меня тем, что в нём элементы GUI отрисовываются, а не берутся из API операционной системы. Поэтому приложение будет выглядеть одинаково на всех устройствах. В нём даже можно создавать собственные элементы любого вида. Однако он пока довольно сырой — об этом говорит уже то, что в официальной документации есть ошибки в инструкции для новичков.
  2. AvaloniaUI. Это более популярный и проработанный фреймворк, который позволяет создавать интерфейсы как для мобильных устройств, так и для Linux и OS X. Также в нём используется диалект XAML, что будет плюсом для тех, кто пробовал создавать приложения для Windows. В нём даже есть поддержка MVVM.

Я попробовал оба, и второй показался мне более удобным: в нём есть язык разметки, поддержка MVVM, быстрая установка, лёгкий переход с WPF. Поэтому я выбрал его.

Как начать использовать AvaloniaUI

Я буду создавать приложение в Linux Ubuntu, но эта инструкция подойдёт всем, кто использует .NET Core. Создавать приложения можно и в Visual Studio, скачав для него расширение, но так как его нет на Linux, я буду пользоваться терминалом.

Для начала клонируйте себе на компьютер этот репозиторий:

В нём находятся шаблоны для создания приложения с AvaloniaUI. Если вы не умеете пользоваться Git, то просто скачайте содержимое и распакуйте куда-нибудь на компьютере. Затем откройте консоль и введите следующую команду:

dotnet new --install [путь к скачанному репозиторию]

Она установит шаблоны для создания приложения. Чтобы проверить, добавились ли шаблоны, используйте команду:

dotnet new --list

Вы увидите список всех установленных шаблонов. Среди них должны быть Avalonia Window, Avalonia .NET Core MVVM App, Avalonia UserControl и Avalonia .NET Core App. Если они на месте, можно продолжать.

Откройте в консоли папку, в которой хотите создать проект, и введите:

dotnet new avalonia.mvvm

Будет создано приложение с использованием MVVM. Практически вся документация по AvaloniaUI написана с использованием этого паттерна, поэтому проще будет разрабатывать на нём.

Теперь можно приступать к работе над приложением.

Создаём калькулятор на AvaloniaUI

У вас будут созданы следующие папки

  • Assets — сюда можно загружать различные компоненты программы вроде иконок, изображений, звуков и прочего.
  • Models — эта папка предназначена для классов, которые будут выступать в роли модели.
  • ViewModels — здесь находятся классы-посредники между видом и моделью.
  • Views — все окна будут находиться здесь.

Сначала посмотрим в файл Program.cs в корневом каталоге:

using System;
using Avalonia;
using Avalonia.Logging.Serilog;
using AvaloniaMVVM.ViewModels;
using AvaloniaMVVM.Views;

namespace AvaloniaMVVM
{
	class Program
	{
    	// Initialization code. Don't use any Avalonia, third-party APIs or any
    	// SynchronizationContext-reliant code before AppMain is called: things aren't initialized
    	// yet and stuff might break.
    	public static void Main(string[] args) => BuildAvaloniaApp().Start(AppMain, args);

    	// Avalonia configuration, don't remove; also used by visual designer.
    	public static AppBuilder BuildAvaloniaApp()
        	=> AppBuilder.Configure<App>()
            	.UsePlatformDetect()
            	.LogToDebug()
            	.UseReactiveUI();

    	// Your application's entry point. Here you can initialize your MVVM framework, DI
    	// container, etc.
    	private static void AppMain(Application app, string[] args)
    	{
        	var window = new MainWindow
        	{
            	DataContext = new MainWindowViewModel(),
        	};

        	app.Run(window);
    	}
	}
}

Нас интересует метод AppMain (). В нём создаётся окно (MainWindow) с указанием DataContext (используется для привязки данных), а потом это окно запускается.

В этом методе можно определить свою логику инициализации приложения. Например, объявить экземпляр модели и передать его в конструктор MainWindowViewModel (). Однако перед этим нужно определить конструктор, который будет принимать такой аргумент.

У нас очень простое приложение, поэтому мы реализуем всю логику прямо в MainWindowViewModel.cs. Там будут необходимые свойства и методы.

Для начала нужно подключить пространство имён ReactiveUI, которое в AvaloniaUI используется для реализации паттерна MVVM:

using ReactiveUI;

Затем можно писать сам код:

private string _message = "";

private string _num1 = "0";
private string _num2 = "0";
private string _result = "0";

public void Click()
{
    int num1 = 0;
    int num2 = 0;
    int result = 0;
    Message = "";

    try
    {
   	 num1 = Convert.ToInt32(Num1);
   	 num2 = Convert.ToInt32(Num2);
    }
    catch(Exception e)
    {
   	 Message = "Wrong input!";
   	 Console.WriteLine(e.Message);
    }

    result = num1 + num2;

    Result = result.ToString();
}

public string Message //Так свойства используются для привязки в ReactiveUI
{
    get => _message;
    set => this.RaiseAndSetIfChanged(ref _message, value);
}

public string Num1 
{
    get => _num1; 
    set => this.RaiseAndSetIfChanged(ref _num1, value);
}


public string Num2
{
    get => _num2;
    set => this.RaiseAndSetIfChanged(ref _num2, value);
}

public string Result
{
    get => _result;
    set => this.RaiseAndSetIfChanged(ref _result, value);
}

Теперь нужно написать код интерфейса для окна в файле MainWindow.xaml:

<Window xmlns="https://github.com/avaloniaui"
   	 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   	 xmlns:vm="clr-namespace:AvaloniaMVVM.ViewModels;assembly=AvaloniaMVVM"
   	 xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
   	 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
   	 mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
   	 x:Class="AvaloniaMVVM.Views.MainWindow"
   	 Icon="/Assets/avalonia-logo.ico"
   	 Title="AvaloniaMVVM">

    <Design.DataContext>
   	 <vm:MainWindowViewModel/>
    </Design.DataContext>

    <Grid>
   	 <Grid.RowDefinitions>
   		 <RowDefinition Height="1*"/>
   		 <RowDefinition Height="2*"/>
   		 <RowDefinition Height="1*"/>
   		 <RowDefinition Height="2*"/>
   		 <RowDefinition Height="1*"/>
   	 </Grid.RowDefinitions>

   	 <Grid.ColumnDefinitions>
   		 <ColumnDefinition Width="1*"/>
   		 <ColumnDefinition Width="2*"/>
   		 <ColumnDefinition Width="1*"/>
   		 <ColumnDefinition Width="2*"/>
   		 <ColumnDefinition Width="1*"/>
   		 <ColumnDefinition Width="1*"/>
   	 </Grid.ColumnDefinitions>

   	 <TextBox Text="{Binding Num1, Mode=TwoWay}" Grid.Row="1" Grid.Column="1"/>
   	 <TextBlock Text="+" HorizontalAlignment="Center" VerticalAlignment="Center" Grid.Row="1" Grid.Column="2"/>
   	 <TextBox Text="{Binding Num2, Mode=TwoWay}" Grid.Row="1" Grid.Column="3"/>

   	 <Button Content="=" Grid.Row="2" Grid.Column="2" HorizontalAlignment="Center" VerticalAlignment="Center" Command="{Binding Click}"/>

   	 <TextBlock Text="{Binding Result}" HorizontalAlignment="Center" VerticalAlignment="Center" Grid.Row="3" Grid.Column="2"/>
   	 <TextBlock Text="{Binding Message}" HorizontalAlignment="Center" VerticalAlignment="Center" Foreground="Red" Grid.Row="4" Grid.Column="2"/>
    </Grid>
</Window>

Вы можете заметить, что это практически тот же XAML, который используется в WPF: тут такие же компоненты и свойства, так же используются Grid и привязка данных. Однако здесь в качестве команды можно указывать обычные методы, чего нельзя делать в WPF.

Чтобы скомпилировать и запустить приложение, введите в консоли следующую команду:

dotnet run

Результат должен получиться таким:

Теперь приложение можно просто скомпилировать для разных ОС — никаких дополнительных манипуляций для переноса на новую платформу не требуется.

Заключение

10–15 лет назад это показалось бы извращением, но сейчас мы можем писать программы для Linux на языке, который изначально был предназначен для разработки приложений под Windows.


Жизнь можно сделать лучше!
Освойте востребованную профессию, зарабатывайте больше и получайте от работы удовольствие.
Каталог возможностей
Понравилась статья?
Да

Пользуясь нашим сайтом, вы соглашаетесь с тем, что мы используем cookies 🍪

Ссылка скопирована