Предыдущая лекция |   | Следующая лекция :----------------:|:----------:|:----------------: [Библиотеки классов](./t7_dll.md) | [Содержание](../readme.md#тема-8-оконные-приложения) | [Ресурсы](./wpf_resource.md) # Обзор типов оконных приложений в `C#`. Знакомство со структурой проекта `WPF/Avalonia`. Компоновка. Image. >Содрано [отсюда](https://metanit.com/sharp/wpf/1.php) * [Технологии создания оконных приложений](#технологии-создания-оконных-приложений) * [Особенности платформ WPF/Avalonia](#особенности-платформ-wpfavalonia) * [Установка Avalonia](#установка-avalonia) * [Создание оконного приложения](#создание-оконного-приложения) * [Структура проекта](#структура-проекта) * [Компоновка](#компоновка) * [Image. Ресурсы](#image-ресурсы) ## Технологии создания оконных приложений В **C#** есть несколько технологий для созданий оконных приложений: * **Windows Forms** - разработка "классических" приложений Windows, считается устаревшей **Windows Forms** — интерфейс программирования приложений (API), отвечающий за графический интерфейс пользователя и являющийся частью *Microsoft .NET Framework*. Данный интерфейс упрощает доступ к элементам интерфейса Microsoft Windows за счет создания обёртки для существующего **Win32 API** в управляемом коде. Причём управляемый код — классы, реализующие API для **Windows Forms**, не зависят от языка разработки. * **WPF** (Window Presentation Foundation) - более современный фреймворк для **.NET**, но заточен только под Windows (отрисовку реализует через **DirectX**) * **UWP** (Universal Windows Platform) - вроде как "последний писк", рассчитанный на разработку универсальных приложений под Windows Phone, Windows 8 и.т.д * **Avalonia** являеся практически калькой с **WPF**. Он совместим на уровне разметки, но отрисовка реализована через **OpenGL**. ## Особенности платформ WPF/Avalonia Если при создании традиционных приложений на основе **Windows Forms** за отрисовку элементов управления и графики отвечали такие части ОС Windows, как **User32** и **GDI+**, то приложения **WPF** основаны на **DirectX** (**Avalonia**, соответственно, на **OpenGL**). В этом состоит ключевая особенность рендеринга графики: используя **WPF/Avalonia**, значительная часть работы по отрисовке графики, как простейших кнопочек, так и сложных 3D-моделей, ложиться на графический процессор на видеокарте, что также позволяет воспользоваться аппаратным ускорением графики. Одной из важных особенностей является использование языка декларативной разметки интерфейса **XAML**, основанного на **XML**: вы можете создавать насыщенный графический интерфейс, используя или декларативное объявление интерфейса, или код **C#**, либо совмещать и то, и другое. ### Преимущества WPF/Avalonia Что вам, как разработчику, предлагает **WPF/Avalonia**? * Использование традиционных языков **.NET**-платформы - **C#** для создания логики приложения * Возможность декларативного определения графического интерфейса с помощью специального языка разметки **XAML**, основанном на **XML** и представляющем альтернативу программному созданию графики и элементов управления, а также возможность комбинировать **XAML** и **C#** * Независимость от разрешения экрана: поскольку в WPF/Avalonia все элементы измеряются в независимых от устройства единицах, приложения на WPF/Avalonia легко масштабируются под разные экраны с разным разрешением. * Новые возможности, которых сложно было достичь в **Windows Forms**, например, создание трехмерных моделей, привязка данных, использование таких элементов, как стили, шаблоны, темы и др. * Хорошее взаимодействие с **Windows Forms** (только **WPF**), благодаря чему, например, в приложениях **WPF** можно использовать традиционные элементы управления из **Windows Forms**. * Богатые возможности по созданию различных приложений: это и мультимедиа, и двухмерная и трехмерная графика, и богатый набор встроенных элементов управления, а также возможность самим создавать новые элементы, создание анимаций, привязка данных, стили, шаблоны, темы и многое другое * Аппаратное ускорение графики - вне зависимости от того, работаете ли вы с **2D** или **3D**, графикой или текстом, все компоненты приложения транслируются в объекты, понятные **Direct3D/OpenGL**, и затем визуализируются с помощью процессора на видеокарте, что повышает производительность, делает графику более плавной. ## Установка Avalonia Avalonia мы использовать не будем, поэтому этот раздел можно пропустить 1. В основном окне **Rider** выбрать вкладку *Configure -> Plugins*: ![](../img/rider12.png) И установите `AvaloniaRider` (в строке поиска введите "avalonia") При установке может выдать сообщение, что плагин разработан не в **JetBrains** и использовать на свой страх - соглашаемся (**Accept**) 1. После установки плагина перезагрузите IDE и установите шаблоны проектов для **Avalonia**. В консоли выполните команду: ``` dotnet new install Avalonia.Templates ``` >Для .NET 6.0 и более ранних версий замените install на --install. >Версию .NET можно узнать выполнив в консоли команду `dotnet --version` ## Создание оконного приложения ### Microsoft Visual Studio Создайте новый проект "Приложение WPF (Майкрософт)" ![](../img/wpf_01.png) По-умолчанию IDE Visual Studio разбито на 3 части: ![](../img/task013.png) * слева панель элементов - список визуальных элементов (кнопки, токстовые поля и т.п.) * в центре основное окно, предназначенное для редактирования исходного кода. При отображении файлов XAML (читается как "замл") разбито на две части: визуальное отображение и текст разметки * справа Обозреватель решений и структура проекта: Properties (Свойства); Ссылки (Зависимости); App.config - настройки проекта; App.xaml - разметка проекта и MainWindow.xaml - разметка окна. Если каких-то панелей нет на экране, то можно их найти в меню Вид. ### Rider Запустите **Rider** и создайте новое решение: Если шаблоны установлены нормально, то в окне создания нового проекта появится секция *Other*: Выберите **Avalonia .NET Core App** ### Настройка проекта При создании задаете *Название решения*, *Имя проекта* и, если нужно, *Расположение*. Остальные параметры оставляем по-умолчанию. >Название проекта должно отражать предметную область или название компании (за это есть отдельные баллы на чемпионате и демо-экзамене) **Основные типы файлов проекта:** * **XAML** eXtended Application Markup Languale - язык разметки, очень похож на XML. В таких файлах хранится описание внешнего вида окна. * **.cs** - файлы с исходным кодом на C# для окна. ## Структура проекта В структуре проекта **WPF** следует выделить следующие моменты. **Во-первых**, в проекте имеется файл `App.xaml` и связанный с ним файл кода `App.xaml.cs` - это глобальные файлы для всего приложения, позже мы о них поговорим подробнее. А пока только следует знать, что `App.xaml` задает файл окна программы, которое будет открываться при запуске приложения. Если вы откроете этот файл, то можете найти в нем строку `StartupUri="MainWindow.xaml"` - то есть в данном случае, когда мы запустим приложение, будет создаваться интерфейс из файла `MainWindow.xaml`. Далее в структуре определены файл разметки `MainWindow.xaml` и файл связанного кода `MainWindow.xaml.cs`. Файл `MainWindow.xaml` и представляет определение окна приложение, которое мы увидим при запуске. ### Введение в язык XAML **XAML** (eXtensible Application Markup Language) - язык разметки, используемый для инициализации объектов в технологиях на платформе **.NET**. Применительно к **WPF** данный язык используется прежде всего для создания пользовательского интерфейса декларативным путем, наподобие **HTML** в веб-программировании. **XAML** - не является обязательной частью приложения, мы вобще можем обходиться без него, создавая все элементы в файле связанного с ним кода на языке **C#**. Однако использование **XAML** все-таки несет некоторые преимущества: * Возможность отделить графический интерфейс от логики приложения, благодаря чему над разными частями приложения могут относительно автономно работать разные специалисты: над интерфейсом - дизайнеры, над кодом логики - программисты. * Компактность, понятность, код на XAML относительно легко поддерживать. При компиляции приложения в Visual Studio код в xaml-файлах также компилируется в бинарное представление кода xaml, которое называется BAML (Binary Application Markup Language). И затем код baml встраивается в финальную сборку приложения - exe или dll-файл. #### Структура и пространства имен AXAML При создании нового проекта **WPF** он уже содержит файлы с кодом xaml. Так, создаваемый по умолчанию в проекте файл `MainWindow.xaml` будет иметь следующую разметку: ```xml ``` Если вы совершенно не знакомы с **xaml** и с **xml**, то даже этот небольшой минимальный код окна может вызывать затруднения. Подобно структуре веб-страничке на **html**, здесь есть некоторая иерархия элементов. Элементом верхнего уровня является тег **Window**, который представляет собой окно приложения. При создании других окон в приложении нам придется всегда начинать объявление интерфейса с элемента **Window**, поскольку это элемент самого верхнего уровня. Кроме **Window** существует еще два элемента верхнего уровня: * Page * Application Элемент **Window** имеет вложенный пустой элемент **Grid**, а также подобно html-элементам ряд атрибутов (_Title_, _Width_, _Height_) - они задают заголовок, ширину и высоту окна соответственно. #### Пространства имен XAML При создании кода на языке **C#**, чтобы нам были доступны определенные классы, мы подключаем пространства имен с помощью директивы `using`, например, `using System.Windows;`. Чтобы задействовать элементы в **XAML**, мы также подключаем пространства имен. Вторая и третья строчки как раз и представляют собой пространства имен, подключаемые в проект по умолчанию. А атрибут _xmlns_ представляет специальный атрибут для определения пространства имен в **XML**. Так, пространство имен `http://schemas.microsoft.com/winfx/2006/xaml/presentation` содержит описание и определение большинства элементов управления. Так как является пространством имен по умолчанию, то объявляется без всяких префиксов. `http://schemas.microsoft.com/winfx/2006/xaml` - это пространство имен, которое определяет некоторые свойства **XAML**, например свойство _Name_ или _Key_. Используемый префикс `x` в определении `xmlns:x` означает, что те свойства элементов, которые заключены в этом пространстве имен, будут использоваться с префиксом `x` - `x:Name` или `x:Key`. Это же пространство имен используется уже в первой строчке `x:Class="XamlApp.MainWindow"` - здесь создается новый класс **MainWindow** и соответствующий ему файл кода, куда будет прописываться логика для данного окна приложения. Это два основных пространства имен. Рассмотрим остальные: `xmlns:d="http://schemas.microsoft.com/expression/blend/2008"`: предоставляет поддержку атрибутов в режиме дизайнера. Это пространство имен преимущественно предназначено для другого инструмента по созданию дизайна на XAML - Microsoft Expression Blend `xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"`: обеспечивает режим совместимости разметок XAML. В определении объекта Window двумя строчками ниже можно найти его применение: `xmlns:local="clr-namespace:XamlApp"`: пространство имен текущего проекта. Так как в нашем случае проект называется **XamlApp**, то простраство имен называется аналогично. И через префикс `local` я смогу получить в XAML различные объекты, которые я определил в проекте. Важно понимать, что эти пространства имен не эквивалентны тем пространствам имен, которые подключаются при помощи директивы `using` в **c#**. #### Элементы и их атрибуты XAML предлагает очень простую и ясную схему определения различных элементов и их свойств. Каждый элемент, как и любой элемент XML, должен иметь открывающий и закрывающий теги, как в случае с элементом `Window`: ```xml ``` Либо элемент может иметь сокращенню форму с закрывающим слешем в конце, наподобие: ```xml ``` Но в отличие от элементов xml каждый элемент в XAML соответствует определенному классу **C#**. Например, элемент `Button` соответствует классу `System.Windows.Controls.Button`. А свойства этого класса соответствуют атрибутам элемента `Button`. Например, добавим кнопку в создаваемую по умолчанию разметку окна: ```xml ``` Здесь свойство *Content* задается неявно в виде содержимого между тегами ``. Но несмотря на то, что у нас несколько пробелов между словами "Hello" и "World", XAML по умолчанию будет убирать все эти пробелы. И чтобы сохранить пробелы, нам надо использовать атрибут `xml:space="preserve"`: ```xml ``` ### Файлы отделённого кода При создании нового проекта в дополнение к создаваемому файлу `MainWindow.xaml` создается также файл отделённого кода `MainWindow.xaml.cs`, где, как предполагается, должна находится логика приложения связанная с разметкой из `MainWindow.xaml`. Файлы XAML позволяют нам определить интерфейс окна, но для создания логики приложения, например, для определения обработчиков событий элементов управления, нам все равно придется воспользоваться кодом **C#**. По умолчанию в разметке окна используется атрибут `x:Class`: ```xml Здесь проявляется основное отличие **Windows Forms** и **WPF/Avalonia**: в **Windows Forms** код первичен, т.е. при старте приложения **императивно** создаётся интерфейс (вызовами Windows API), а в **WPF** первичен интерфейс, в котором **декларативно** описан внешний вид и при запуске приложения запускается класс окна, который мы можем расширить (инициализировать источники данных и описать логику работы). #### Взаимодействие кода C# и XAML В приложении часто требуется обратиться к какому-нибудь элементу управления. Для этого надо установить у элемента в XAML свойство **Name**. Еще одной точкой взаимодействия между **xaml** и **C#** являются события. С помощью атрибутов в **XAML** мы можем задать события, которые будут связанны с обработчиками в коде **C#**. Итак, в разметке главного окна определим два элемента: кнопку и текстовое поле. >В теге **Window** может быть только один вложенный элемент. Обычно используют **Grid** (подробнее о нём поговорим ниже) ```xml ``` Так как пространство имен проекта проецируется на префикс `local`, то все классы проекта можно использовать в форме `local:Название_Класса`. Так в данном случае в качестве содержимого кнопки устанавливается объект **Phone**. ![](../img/wpf_02.png) Для вывода содержимого любого объекта используется метод *ToString*. Этот метод объявлен в классе **Object** и по-умолчанию выводит просто название класса. Для того, чтобы получить нужную нам информацию об объекте необходимо переопределить метод *ToString*. Мы можем подключить любые другие пространства имен, классы которых мы хотим использовать в приложении. Например: ```cs Понедельник Вторник Среда Четверг Пятница Суббота Воскресенье ``` Здесь определены два дополнительных пространства имен: ``` xmlns:col="clr-namespace:System.Collections;assembly=mscorlib" xmlns:sys="clr-namespace:System;assembly=mscorlib" ``` Благодаря этому нам становятся доступными объекты из пространств имен **System.Collections** и **System**. И затем используя префикс, мы можем использовать объекты, входящие в данные пространства имен: ` ``` Каждая строка задается с помощью вложенного элемента *RowDefinition*. При этом задавать дополнительную информацию необязательно. То есть в данном случае у нас определено в гриде 3 строки. Каждая столбец задается с помощью вложенного элемента *ColumnDefinition*. Таким образом, здесь мы определили 3 столбца. То есть в итоге у нас получится таблица 3х3. Для визуального отображения ячеек сетки можно добавить свойство `ShowGridLines="True"` ![](../img/rider15.png) Чтобы задать позицию элемента управления с привязкой к определенной ячейке **Grid**-а, в разметке элемента нужно прописать значения свойств *Grid.Column* и *Grid.Row*, тем самым указывая, в каком столбце и строке будет находиться элемент. Кроме того, если мы хотим растянуть элемент управления на несколько строк или столбцов, то можно указать свойства *Grid.ColumnSpan* и *Grid.RowSpan*, как в следующем примере: ```xml