- PVSM.RU - https://www.pvsm.ru -

Введение в XWT

Снова приветствую всех читателей данного ресурса! В данный момент разработано множество решений для обеспечения кросс-платформенности GUI-приложений Microsoft .NET/Mono. В этой статье я ознакомлю читателей с одним из них, официальным проектом от разработчиков Mono — XWT Widget Toolkit (Xwt). Скачать его можно на данный момент только в виде исходников из репозитория github.com/mono/xwt/ [1]. К сожалению, грамотных и полных статей об этом тулките в Интернете найти не удалось, поэтому пришлось разнюхивать всё самому. Т.к. эта статья не рекламная и не обзорная, я не буду объяснять, чем XWT лучше или хуже того же Eto Forms [2] или System.Windows.Forms (последнее на Mono работает вполне сносно). В этой статье будет рассказано о принципе работы XWT и азах работы с ним.

Внимание! Использование XWT может нанести вред вашему здоровью! Данный тулкит пока что достаточно сырой; пытаясь понять, почему оно не работает можно заработать съезд крыши или спиться от обилия кружек кофе.

Введение

XWT Widget Toolkit (вроде так расшифровывается название, а не Xamarin Widget Toolkit, как многие думают) устроен по схеме, где имеется абстрактный loader по имени Xwt.dll, который грузит бэкенды, построенные по стандартным интерфейсам Xwt, из библиотек Xwt.Gtk.dll, Xwt.Wpf.dll, Xwt.Mac.dll и т.п. Подробнее про устройство XWT можно почитать в хабра-топике от 11 ноября 2013 года [3]. По структуре API, Xwt очень похож на GTK#, что неудивительно — Xwt создавался для портирования MonoDevelop на отличные от Linux платформы (в первую очередь MacOS X).

Инициализация

Xwt может быть инициализирован как хост (для обыкновенных приложений), так и как гость, когда виджеты Xwt встраиваются в нативное приложение. Оба метода в целом похожи, поэтому будут рассматриваться одновременно.
Сначала инициализируется сам XWT и бэкенд:

Xwt.Application.Initialize(Xwt.ToolkitType.Wpf);
//или Xwt.Application.InitializeAsGuest(Xwt.ToolkitType.Wpf);

Оперируя аргументом, можно загружать различные бэкенды. Для того, чтобы бэкенд загрузился, необходимо положить файл Xwt.*.dll в каталог в Вашим приложением (так разработчики передают пламенный привет маководам без возможности компиляции Xwt.Wpf.dll и виндузятникам, не способным собрать Xwt.Mac.dll ;-) ). Можно задавать в качестве аргумента и просто строку с сигнатурой dll-ки бэкенда, но так поступать не рекомендуется, т.к. может случиться «DLL hell» [4]. Внимательный читатель сразу заметит, что бэкенд задаётся вручную. Хотя в API XWT и присутствует вызов Xwt.Application.Initialize() без аргументов, он не работает как ожидается. Поэтому придётся городить велосипеды с определением ОС и выбором ToolkitType самостоятельно. Имейте в виду, что MacOSX просто так не определяется [5].

После инициализации необходимо создать окно (Xwt.Window) и вывести его на экран

//MainWindow - наследник Xwt.Window, являющийся главным окном приложения
new MainWindow().Show();

Итак, загрузили мы XWT, вывели окно, оно мигнуло, и процесс завершился. А всё потому, что забыли запустить поток событий UI. Это делается одной строчкой:

Xwt.Application.Run();

На этом месте код функции Main() зависнет, так что всё остальное необходимо выполнять в потоке UI, то есть в созданной строчкой выше форме.

Формы, диалоги и виджеты

Элементы управления (user controls) в данном тулките называются виджетами (widgets). Виджеты вкладываются в другие виджеты (только так), а виджеты в окна. Окна представляют собой классы, наследуемые от Xwt.WindowFrame. Правда, как правило, они всё-таки представляют экземпляры или наследуются от классов Window или Dialog. Диалоги от форм отличаются возможностью вывода в модальном режиме (функция Run(WindowFrame parent)), а также разделением рабочей области на отдел для вложенного виджета (Content) и кнопок управления (Buttons). Формы обладают лишь заголовком, отделом для вложенного виджета (Content) и строкой меню (MainMenu). Последней нет в диалогах.
И формы, и диалоги имеют базовый набор событий и свойств, в целом аналогичный другим тулкитам GUI, разве что немного сокращённый. Ругая разработчиков ООО Xamarin не забывайте, что за каждым пунктом интерфейсов Xwt стоит долгая доработка сразу четырёх бэкендов сразу.

Виджеты представляют из себя классы, наследуемые от Xwt.Widget. Существуют виджеты — контейнеры и виджеты — элементы управления. Сами по себе окна могут содержать в себе столько один виджет, чтобы перебороть данное ограничение и были созданы виджеты-контейнеры. Контейнеры, как звучит из названия, могут содержать в себе неограниченное количество других виджетов (включая контейнерные), и сами отвечают за их размещение на собственном хозяине (parent), например, окне или другом контейнере. Простые виджеты, я думаю, в описании не нуждаются, хотя документация на них явно слабовата. Будьте готовы к тому, что известные свойства и события могут называться по другому. Например, клик мышкой, в т.ч. двойной, вызывает событие ButtonPressed.

Для закрепления, разберём пример из предыдущей статьи:

class Program
    {
        [STAThread] //для избежания глюков под WPF
        static void Main(string[] args)
        {
            //иницализация XWT и создание главного окна
            Xwt.Application.Initialize(Xwt.ToolkitType.Wpf);
            Xwt.Window MainWindow = new Xwt.Window()
            {
                Title = "Xwt Test"
            };
            MainWindow.CloseRequested += (o, e) =>
            {
                Xwt.Application.Exit(); //без этого exe'шник сам не закроется
            };
            //создание виджетов и меню
            Xwt.Menu MainMenu = new Xwt.Menu();
            Xwt.RichTextView TextView = new Xwt.RichTextView();
            Xwt.MenuItem FileOpenMenuItem = new Xwt.MenuItem("Открыть");
            Xwt.Menu FileMenu = new Xwt.Menu();
            FileOpenMenuItem.Clicked += (o,e) => {
                Xwt.OpenFileDialog Dialog = new Xwt.OpenFileDialog("Открыть файл");
                if (Dialog.Run(MainWindow)) {
                    TextView.LoadFile(Dialog.FileName, Xwt.Formats.TextFormat.Markdown);
                }
            };
            Xwt.MenuItem FileMenuItem = new Xwt.MenuItem("Файл") { SubMenu = FileMenu };
            FileMenu.Items.Add(FileOpenMenuItem);
            MainMenu.Items.Add(FileMenuItem);
            //формировка окна
            MainWindow.MainMenu = MainMenu;
            MainWindow.Content = TextView;
            //запуск приложения
            MainWindow.Show();
            Xwt.Application.Run();
        }
    }

Виджеты-контейнеры

Данный тулкит содержит множество виджетов-контейнеров, нагло спионеренных из GTK. Для тех, кто незнаком с GTK#, приведу обзор данных баков контейнеров.

Canvas — холст

Самый простой из контейнеров. Позволяет как выкладывать в произвольном порядке виджеты на себя (AddChild(Xwt.Widget w)), так и поддерживает какое-никакое рисование (OnDraw(Xwt.Drawing.Context ctx, Xwt.Rectangle dirtyRect)). Однако к «повседневному» применению он не рекомендуется, т.к. средства отрисовки в Xwt далеки от совершенства, а жёсткое позиционирование элементов может привести с полному разъёзду виджетов при изменении их внутреннего содержимого (а, значит, и размеров).

Box'ы — книжная полка (стойка)

Виджеты HBox и VBox предназначены для размещения виджетов по линейке, с автоматическим управлением размерами и положением других виджетов. Добавление виджетов осуществляется вызовом функций PackStart и PackEnd. Манипулируя аргументами можно задавать режим вмещения и растяжения виджетов. Аргументы margin* задаются в непонятной величине, при стандартном разрешении дисплея в 96dpi равной строго 1px, но продолжающей адекватно работать и при повышенном DPI (с масштабом).

Table — таблица

Для знакомых с классическим построением web-страниц это, пожалуй, будет самым удобным контейнером из существующих. Да, это самая обыкновенная таблица. Каждой ячейке можно задавать по виджету. Для больших приложений с фиксированной разметкой это, наверное, самый лучший из контейнеров. Однако он не позволяет делать разметку «резиновой», т.е. при изменении размеров виджета в ячейке автоматом меняются и размеры столбца/строки, но просто взять, и перетащить границу нельзя (пока?).

Paned — панели

Для создания интерфейсов из двух панелей с поддержкой изменения пропорций между ними, имеется контейнеры VPaned и HPaned. На данный момент в Xwt нельзя программно менять пропорции, хотя для этого есть даже два неработающих свойства, правда, не понял, чем они отличаются — Position и PositionFraction.

Notebook — вкладки

«Записная книжка» предназначена для создания вкладочного интерфейса, как в допотопных окнах настроек. Имейте в виду, что в качестве «детей» (child) вкладок необходимо указывать контейнеры, например, VBox, иначе Вы сможете поместить только один виджет.

Примеры использования виджетов XWT можно найти в прилагаемых примерах и тестах. Больше информации Вы не найдёте, т.к. документирован данный тулкит плохо, если не сказать, что он вообще не документирован. Известных приложений на XWT пока что мало, что неудивительно, однако поиск по Github (тыц [6]) и др. публичным репозиториям выдаёт массу интересных стартапов.

В следующей статье я попытаюсь рассказать о «плюшках» XWT, таких как внедрение в нативные приложения .NET, встроенном парсере Markdown, работе с TreeView и ListView, потокоопасности XWT и методах борьбы с этим, а также некоторых существенных недоделках (мелкими багами это язык не поворачивается назвать) данного тулкита. Если знаете и другие важные подробности XWT, просьба упомянуть в комментариях — будет, что упомянуть в статье №2.

P.S. Если не удаётся открыть sln-файл XWT под Visual Studio 2010, установите SP1 или VS 2012+.

Автор: ATauenis

Источник [7]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/net/63331

Ссылки в тексте:

[1] github.com/mono/xwt/: https://github.com/mono/xwt/

[2] Eto Forms: https://github.com/picoe/Eto

[3] хабра-топике от 11 ноября 2013 года: http://habrahabr.ru/post/201460/

[4] «DLL hell»: http://ru.wikipedia.org/wiki/DLL_hell

[5] просто так не определяется: https://www.google.ru/search?q=mono%20detecting%20macosx

[6] тыц: https://github.com/search?q=%22using+Xwt%3B%22&type=Code&ref=searchresults

[7] Источник: http://habrahabr.ru/post/226695/