Первый взгляд на написание и развертывание приложений в Windows следующего поколения

Автор: Дино Эспозито, копирайт Microsoft.com

Эта статья написана по версии Microsoft Windows с кодовым названием «Longhorn», выпущенной перед конференцией Microsoft Professional Developers Conference (PDC), поэтому любая информация, изложенная в этой статье, может оказаться устаревшей.

Примечание: эта статья опубликована до официального выпуска продукта, ввиду чего точность приведенных здесь сведений к моменту появления финальной версии не гарантируется. Данная статья отражает состояние продукта на момент ее публикации и предназначена лишь для ознакомительных целей. Информация может быть изменена без предварительного уведомления.

Microsoft Windows следующего поколения под кодовым названием «Longhorn» приведет к серьезным переменам не только в том, как работает сама операционная система, но и в том, как создаются приложения. Longhorn-версия Windows включает новую подсистему внешней памяти, технологию поиска на естественном языке (natural search technology) и более надежные средства защиты. В ней также уделяется особое внимание концепции доверительных вычислений (trustworthy computing). В этой статье дается обзор Longhorn, в том числе ее модели приложений «создается один раз, развертывается многократно» (build-once, deploy n-times application model). Кроме того, автор обсуждает новый язык программирования под кодовым названием «XAML» для создания UI-элементов и рассматривает примеры его применения на практике.

Следующий выпуск Microsoft Windows под кодовым названием «Longhorn» станет важной вехой по многим причинам. Это первая операционная система, построенная на управляемом коде, и первая, где введена новая подсистема внешней памяти (storage subsystem) под кодовым названием «WinFS», которая революционным образом изменит концепцию файловой системы.

Примечание от 09/2004: WinFS больше не входит в первый выпуск Longhorn 2006 года, основные элементы платформы WinFX API будут выпущены для Windows XP и Windows Server 2003.

Это и первая операционная система с поддержкой технологии поиска на естественном языке (natural search technology) (Natural UI), которая автоматически справляется со многими случаями неоднозначности в тексте запросов. Кроме того, Longhorn — первая система, изначально рассчитанная на обеспечение максимальной безопасности и поддержку (на уровне ядра) концепции доверительных вычислений (trustworthy computing). Эти и другие новшества подразумевают, что в Longhorn изменится принцип разработки приложений, а такие события случаются не каждый день. За все время существования Windows я припоминаю лишь два близких по своей значимости события: переход на 32-разрядную Windows и появление инфраструктуры управляемого кода — Microsoft .NET Framework.

Одно из наиболее важных изменений в Longhorn заключается в том, что эта операционная система позволяет писать приложение лишь раз, а потом использовать его во множестве сценариев развертывания. Для достижения столь амбициозной цели Longhorn-приложения создаются полностью объектно-ориентированными и базируются на центральном объекте Application, который предоставляет все ключевые сервисы, необходимые для выполнения приложения. В этой статье я рассмотрю модель приложений в Longhorn и применю ее в нескольких примерах, в том числе в классическом Hello World.

Модель приложений в Longhorn

Объект Application — сердцевина модели приложений в Longhorn. Его набор свойств, методов и событий позволяет упорядочить набор страниц разметки (markup pages) — своего рода усовершенствованную версию HTML — в связанное приложение. Объект Application является корневым объектом приложения, доступным в Longhorn. Он предоставляет базовую поддержку и, как правило, будет применяться в тех приложениях, которые не должны требовать много ресурсов, не используют навигацию по страницам и управление состоянием. Более сложные Longhorn-приложения будут опираться на объект NavigationApplication, который наследует от Application и добавляет поддержку навигации.

Типичное Longhorn-приложение можно рассматривать как набор страниц, содержащих некий процедурный код. Объект Application контролирует выполнение программы и генерирует события для пользовательского кода. Страницы пишутся на новом языке декларативной разметки (declarative markup language) под кодовым названием «XAML» (Extensible Application Markup Language). С помощью XAML-элементов вы управляете разметкой каждой страницы, в том числе выводом текста и изображений, а также вставкой интерактивных компонентов вроде кнопок и текстовых полей. Если коротко, то XAML — это язык, применяемый для декларативного рендеринга пользовательского интерфейса страниц, из которых состоит приложение. Помимо XAML, вы сможете писать Longhorn-приложения полностью на процедурном коде. В принципе, успешные Longhorn-приложения будут включать два ингредиента: XAML-страницы и управляемый процедурный код. Как именно вы будете комбинировать их — дело ваше, но пропорции возможны любые.

Используя XAML в сочетании с C# (или Visual Basic .NET), вы сможете создавать различные типы выходных файлов, в том числе традиционные исполняемые файлы, DLL-библиотеки и консольные программы. А если ваше приложение сравнительно простое, оно может быть построено в форме автономного XAML-файла, что дает вам еще один тип приложения. Автономные XAML-файлы можно запускать в оболочке Longhorn и в браузере, если только они ни ссылаются на какой-либо класс отделенного кода (codebehind class). Наконец, стоит отметить, что хостом для исполняемых файлов Longhorn могут служить окна (по умолчанию), а также браузер. В обоих случаях код одинаков, его нужно лишь перекомпилировать с соответствующим значением свойства проекта (project property).

Исполняемые файлы настольных приложений Longhorn представляют собой следующую версию нынешних клиентских приложений Windows Forms. С другой стороны, XAML и приложения, выполняемые в браузере, отражают эволюцию современной модели программирования на клиентской стороне в сторону Web. Прямо сейчас существующие клиентские приложения редко развертываются через Web. Если вы хотите встроить форму Windows Forms в страницу браузера, вы получите сокращенный набор функциональности, и вам придется оптимизировать свой код до последнего бита. В случае Longhorn общая модель приложений (common application model) позволит вам написать одно универсальное приложение и развертывать его через Web. Однако это приложение будет специфичным для Longhorn — оно сильно отличается от традиционного Web-приложения вроде ASP.NET.

При компиляции приложения следующая версия Visual Studio и .NET Framework под кодовым названием «Whidbey» (или нижележащий инструмент MSBuild.exe) сгенерирует EXE-файл, манифест приложения (.manifest) и манифест развертывания (.deploy). Если щелкнуть EXE-файл, приложение запустится, как было задано в его проекте. Если приложение сконфигурировано на выполнение в браузере, запускается экземпляр Internet Explorer, который служит хостом для этого приложения. В качестве альтернативы вы можете развертывать приложение с удаленного сервера. Для этого вы должны, во-первых, скопировать манифест развертывания в соответствующее место на своем сервере. Оно может быть задано FTP- или HTTP-путем. Во-вторых, вы должны скопировать на сервер скомпилированные файлы приложения и его манифест. Каталоги для манифеста развертывания и манифеста приложения со скомпилированными файлами могут быть разными. Если они разные, вы вручную редактируете манифест развертывания, чтобы он указывал местонахождение манифеста приложения. Манифесты представляют собой обычные XML-файлы. Когда пользователь переходит в браузере в указанный каталог развертывания, Longhorn автоматически скачивает и устанавливает приложение и его манифесты на клиентском компьютере, а затем создает ярлык для файла с расширением .deploy. По окончании этого процесса пользователь запускает приложение щелчком файла с расширением .deploy.

У всех Longhorn-приложений общая структура - XAML-страницы с процедурным кодом (встроенным или отделенным) и корневой объект, производный от Application. Объект Application действует как контроллер; его срок жизни совпадает со сроком жизни приложения. Application позволяет обрабатывать события верхнего уровня (top-level events) и иногда разделять код и состояние между несколькими страницами. Он также отвечает за поддержку навигации по страницам в соответствии с логикой приложения. В типичной Longhorn-программе пользователь выполняет задачи и «продвигается по приложению», переходя со страницы на страницу. Навигация обычно реализуется заменой старой страницы новой. Однако вы можете открывать новую страницу в новом всплывающем окне. Навигация нужна не во всех Longhorn-приложениях; простые программы могут состоять из единственной страницы.

Как уже упоминалось, XAML-страницы могут содержать процедурный код в дополнение к элементам разметки. Процедурный код нужен, например, для обработки события, генерируемого одним из XAML-элементов на странице. Этот код размещается в теле XAML-файла или в файле отделенного кода.

Программирование в Longhorn основано на управляемом коде. Однако лишь немногие из .NET-совместимых языков пригодны для написания XAML-приложений. Сегодня этот список состоит из C#, Visual Basic .NET и JScript .NET. К моменту выпуска Longhorn ожидается расширение этого списка. Нынешнее ограничение тремя языками вызвано тем, что исходный код XAML-файла нужно анализировать и компилировать «на лету», поэтому компилятор и соответствующая DOM-модель кода (code document object model) должны быть доступны заранее. Но заметьте: если вы пишете приложение исключительно на процедурном коде, то можете использовать любой .NET-совместимый язык — ограничение до трех языков распространяется лишь на приложения на основе XAML. Если процедурный код встроен в XAML-страницу, тогда вы должны заранее, до запуска, скомпилировать приложение; если же процедурного кода в XAML-странице нет, она отображается двойным щелчком точно так же, как HTML-страница. Longhorn не обрабатывает некомпилированный код и не умеет компилировать код «на лету».

Вас интересует, как будет выглядеть Longhorn-версия «Hello World»? Вот пример простейшего XAML-кода:

<Canvas
    xmlns="http://schemas.microsoft.com/2003/xaml"
    Background="LightCyan"
    Width="100%" Height="100%">

  <Image Source="lh.bmp" Canvas.Left="5" Canvas.Top="5" />
  <Text Canvas.Left="90" Canvas.Top="20" FontSize="36">Hello, Longhorn!
  </Text>
</Canvas>

Сохраните этот фрагмент кода в текстовом файле с расширением .xaml и откройте его в браузере Longhorn или просто дважды щелкните в оболочке. Результаты показаны на рис. 1. Узел <Canvas> определяет область пользовательского интерфейса приложения — по сути, область вывода. Атрибут Background задает цвет фона этой области, а атрибуты Width и Height указывают ее размер. Элементы <Image> и <Text> определяют содержимое страницы. Оба элемента задаются по абсолютной позиции через атрибуты Left и Top родительского объекта Canvas.

Рис. 1. Простая XAML-страница

Более глубокое знакомство с Longhorn-приложениями

Longhorn предоставляет набор инфраструктурных классов (framework classes), которые расширяют .NET Framework 1.1 Extensions, доступные в Longhorn. Эти классы поддерживают XAML и подсистему внешней памяти, модель приложений и концепцию доверительных вычислений, а также более совершенные Web-сервисы. Поставки Whidbey планируется начать за несколько месяцев до выпуска Longhorn, чтобы к моменту ее выхода включить в эту операционную систему обновленную версию Whidbey, которая будет предоставлять базовые сервисы в Longhorn.

Давайте поближе посмотрим на общий субстрат всех Longhorn-приложений — на язык XAML. Он основан на XML и предназначен специально для описания UI ваших приложений. Программисты, знакомые с Win32 и .NET Framework, заметят явное сходство между тэгами XAML и традиционными элементами управления Windows. Однако набор XAML-тэгов разметки более абстрактен и шире набора стандартных элементов управления Win32 или Windows Forms. Чтобы понять, что такое XAML, представьте себе ASP.NET-страницу. Точнее, ASP.NET-страницу, в которой есть только серверные элементы управления (runat="server") и никакого литерального текста.

Каждый XAML-тэг соответствует какому-либо классу .NET Framework и обладает набором методов, свойств и событий. Вы можете настраивать свойства и подключать события к обработчикам декларативным способом в XAML-сценарии (script), а также использовать процедурный код, упакованный в класс отделенного кода. Экземпляр элемента управления, стоящего за каждым тэгом, создается в период выполнения и получает некую область на экране для визуализации своего вывода. На высшем уровне абстракции эта модель во многом напоминает ASP.NET, но придает платформе Windows более богатую обобщенную функциональность.

Каждый элемент в XAML использует нижележащий класс, но у многих классов нет соответствующих XAML-элементов. Эти классы зачастую являются абстрактными и в основном предназначены для наследования. Все, что можно создать на XAML, можно написать и с помощью процедурного кода. Вот как, например, создается элемент Button в XAML:

<Canvas xmlns="http://schemas.microsoft.com/2003/xaml">
   <Button Canvas.Left="10"
           Canvas.Top="10"
           Width="90px"
           Height="32px">Click Me</Button>
</Canvas>

В процедурном C#-коде то же самое можно сделать так:

Button btn = new Button();
btn.Width = new Length(90);
btn.Height = new Length(32);
Canvas.SetTop(btn, new Length(10));
Canvas.SetLeft(btn, new Length(10));
btn.Content = "Click Me";

В XAML-файлах методы неприменимы, но можно использовать атрибуты. Атрибуты обычно соответствуют свойствам, но некоторые свойства класса не сопоставлены XAML-атрибутам. Вы можете задавать свойства класса по отдельности или положиться на стили. Стиль — это именованный набор атрибутов, который компилятор автоматически применяет к элементу управления, использующему стиль. Во всех XAML-страницах есть по крайней мере один элемент-панель (panel element), который служит контейнерным окном и управляет позиционированием дочернего контента и глобальными свойствами вроде шрифта и цвета фона. Элементы XAML-страницы организуются в иерархию с единственным корневым элементом. Обычно в его роли выступает класс, производный от Panel, например DockPanel или Canvas, либо класс, производный от Decorator, скажем, Border. Элемент Canvas применяется для позиционирования контента по абсолютным координатам. Элементы, как правило, визуализируются по уникальным координатам; если какой-то элемент занимает те же координаты, то порядок, в котором такие элементы встречаются в разметке, определяет порядок их отрисовки.

Кроме свойств и методов, многие XAML-элементы предоставляют события. При наличии обработчиков событий страница способна динамически реагировать на соответствующие уведомления. Способ создания обработчиков событий и их подключения к элементам в Longhorn практически идентичен тому, как это делается сейчас в .NET-приложениях. Обработчик события объявляется через атрибут и включается в приложение с помощью класса отделенного кода:

<Button Width="90px" Height="25px" Click="OnHandleClick">
Click Me
</Button>

Элементы на странице образуют дерево приложения (application tree), которое является объектной моделью; она охватывает все компоненты периода выполнения и доступна программно. Каждый элемент управления поддерживает свой способ доступа к дочерним элементам. Основные способы выглядят так: panel.Children.Add(element), listbox.Items.Add(object), button.Content = object и textbox.TextRange.Text = string.

В качестве дочерних поддерживаются произвольные объекты — строки, элементы или любой объект (в последнем случае вы должны вызывать ToString). У каждого элемента управления есть свойство Parent, обеспечивающее доступ к своему предку в древовидной иерархии. (Заметьте, что свойства такого рода возвращают лишь прямых предков или потомков.) Объектная модель позволяет манипулировать любыми характеристиками элементов на странице. Она также предоставляет дополнительные возможности, которые нельзя реализовать средствами XAML. Например, вы можете создавать элементы не только динамически, но и «на лету» в зависимости от условий в период выполнения.

Чтобы прояснить хотя бы часть из того, о чем я говорил, рассмотрим второй пример приложения. Как уже упоминалось, любое серьезное Longhorn-приложение включает процедурный код наряду с XAML-сценарием. Давайте попробуем выйти за рамки элементарной программы Hello World и ввести обработку некоторых событий.

Полный исходный код нового приложения показан на рис. 2; в нем XAML связан с процедурным кодом, который хранится в классе отделенного кода. Сравнивая XAML-решение с ASP.NET-страницей Web Forms, вы не заметите значимых отличий, кроме синтаксиса. Атрибуты def:CodeBehind и def:Class служат оболочкой для C#-кода примерно так же, как класс отделенного кода в Visual Studio — оболочкой для ASP.NET-кода. Пусть на рис. 2 XAML-файл называется events.xaml, а класс отделенного кода — events.xaml.cs. Если вы откроете файл events.xaml в браузере, возникнет ошибка периода выполнения, как показано на рис. 3. Сообщение об ошибке в сборке системы, которую я использовал, не очень-то внятное, но причина ошибки совершенно ясна. Она связана с тем, что XAML-код не компилируется динамически. В отличие от ASP.NET в Longhorn требуется явная компиляция любого процедурного кода, встроенного в XAML-сценарий. Такая компиляция нужна и при простой ссылке из сценария на процедурный код. Все это может радикально перемениться в будущих сборках, но пока придется позаботиться об упорядочении файла проекта для Longhorn в Visual Studio или — в качестве альтернативы — освоить утилиту MSBuild (msbuild.exe). Файлы проектов и решений Longhorn имеют те же расширения и структуру, что и в Visual Studio 2003. Пример файла проекта для Longhorn приведен на рис. 4.

Рис. 2. XAML и процедурный код

<!-- XAML-файл -->

<Window
    xmlns="http://schemas.microsoft.com/2003/xaml"
    xmlns:def="Definition"
    def:Class="Application1.Window1"
    def:CodeBehind="Window1.xaml.cs"
    Text="Application1" Visible="True"
    >

    <DockPanel xmlns="http://schemas.microsoft.com/2003/xaml"
        xmlns:def="Definition" def:Language="C#" Background="White">
        <FlowPanel ID="sourceFlowPanel"
            MouseLeftButtonDown="OnLeftMouseDown"
            Margin="10,10,0,0" Background="cyan" Width="150px"
            Height="150px">
            <Text ID="caption" Margin="10,10,20,20" FontSize="16">
                Click on Me</Text>
        </FlowPanel>
    </DockPanel>
</Window>

// C#-класс отделенного кода

using System;
using MSAvalon.Windows;
using MSAvalon.Windows.Input;
using MSAvalon.Windows.Controls;
using MSAvalon.Windows.Documents;
using MSAvalon.Windows.Navigation;
using MSAvalon.Windows.Shapes;
using MSAvalon.Windows.Data;

namespace Application1
{
    public partial class Window1 : Window
    {
        private bool expanded = false;

        private void OnLeftMouseDown(object sender,
            MouseButtonEventArgs e)
        {
            if (expanded)
            {
                sourceFlowPanel.Width = new
                    Length(sourceFlowPanel.Width.Value/2);
                sourceFlowPanel.Height = new
                    Length(sourceFlowPanel.Height.Value/2);
                caption.TextRange.Text = "Click on Me";
                expanded = false;
            }
            else
            {
                sourceFlowPanel.Width = new Length(2 *
                    sourceFlowPanel.Width.Value);
                sourceFlowPanel.Height = new Length(2 *
                    sourceFlowPanel.Height.Value);
                caption.TextRange.Text = "Thanks for clicking";
                expanded = true;
            }
        }
    }
}

Рис. 3. Ошибка XAML

Рис. 4. Файл проекта для Longhorn

<Project DefaultTargets="Build">
    <PropertyGroup>
        <Property Language="C#" />
        <Property DefaultClrNameSpace="LonghornApp" />
        <Property TargetName="Events" />
    </PropertyGroup>

    <!-- Импортирует мишень (target), которая содержит
        все общие мишени -->
    <Import Project="$(LAPI)\WindowsApplication.target" />

    <ItemGroup>
        <!-- Разметка приложения -->
        <Item Type="ApplicationDefinition"
            Include="eventsApplication.xaml" />

        <!-- Список скомпилированных XAML-файлов -->
        <Item Type="Pages" Include="events.xaml"/>
    </ItemGroup>

</Project>

Файл проекта содержит важную информацию о проекте. Тэг указывает тип пакета (package), который должен сгенерировать компилятор. Свойства и элементы проекта сгруппированы во вложенных тэгах — <ProjectGroup> и <ItemGroup>. Каждому элементу присвоено описательное имя. Файл проекта передается утилите msbuild.exe через командную строку. MSBuild находится в подкаталоге Framework каталога Windows\Microsoft .NET. Следующая простая команда генерирует исполняемый файл на основе файла проекта Events.proj:

msbuild.exe Events.proj

Не стоит уделять особого внимания утилите MSBuild, если только вы не хотите попрактиковаться в работе с ней. Как я уже говорил, Whidbey-версия Visual Studio полностью интегрирована с Longhorn SDK и позволяет компилировать Longhorn-приложения щелчком кнопок на панели управления и нажатием клавиши F5. На рис. 5 показано, к чему приводит запуск кода с рис. 2. В дереве приложения видима лишь голубая панель (panel) с текстом. На самом деле дерево приложения сложнее, чем можно было бы подумать только по рис. 5. Корневой узел — это окно, которое содержит объект DockPanel. Последний является самым внешним контейнером (outermost container) для всех UI-элементов и содержит один дочерний объект, FlowPanel, который в свою очередь включает объект Text. Текст представляется экземпляром класса Text. Обратите внимание, что у XAML-тэгов есть несколько атрибутов для управления их внешних видом. Еще важнее, что они поддерживают атрибут ID, который обеспечивает идентификацию в период выполнения (runtime identification). Событие MouseLeftButtonDown, сопоставленное с классом FlowPanel, генерируется всякий раз, когда вы щелкаете внутри области, назначенной элементу управления. Как только возникает это событие, вызывается код обработчика OnLeftMouseDown. В результате его выполнения ширина и высота панели увеличиваются вдвое, а надпись на ней меняется. Повторный щелчок приводит к восстановлению исходного вида панели.

Рис. 5. Приложение в действии

Хотя ничто не мешает создавать чисто процедурные приложения, XAML-файл упрощает описание и «шаблонизацию» пользовательских интерфейсов ваших приложений.

Язык XAML

Существует четыре основные категории элементов в «словарном запасе» языка XAML: панели (panels), интерактивные элементы управления (interactive controls), относящиеся к документам элементы (document-related elements) и графические фигуры (graphic shapes). Элемент Border также имеет важное значение, хотя он не попадает ни в одну из этих категорий. С технической точки зрения, это «декоратор» («decorator»), который обеспечивает тривиальную разметку, располагая лишь одним дочерним элементом, добавляющим эффекты визуализации. В XAML панели обрабатывают разметку страницы и выступают в роли контейнеров для других элементов. Панели — это компоненты, которые управляют рендерингом (визуализацией) включенных в них элементов, их размером и позицией, а также размещением их контента. В Longhorn поддерживается шесть встроенных классов панелей, но разработчики могут создавать собственные панели, обеспечивающие нестандартное поведение при визуализации. К предопределенным панелям относятся классы Canvas, DockPanel, FlowPanel, GridPanel, Table и TextPanel (табл. 1).

Табл. 1. Классы панелей

Класс Описание
Canvas Определяет область, в которой вы можете размещать дочерние элементы, используя систему координат, относительную области Canvas
DockPanel Определяет область, в которой вы можете размещать дочерние элементы как смежные (состыкованные)
FlowPanel В этой области дочерние элементы визуализируются один за другим в указанном вами направлении. По достижении границы следующий элемент выводится вновь в начальной позиции
GridPanel Определяет область сетки (grid area), состоящей из столбцов и строк. Предоставляет ограниченную функциональность
Table Определяет область, где отображаются сложные данные в табличном формате. Поддерживает «продвинутую» функциональность вроде заголовков в верхней и нижней строках (header and footer), а также группирования столбцов и строк
TextPanel Определяет область, оптимизированную для вывода текста. Поддерживает многострочный текст и его сложное форматированиеь
Text Облегченный аналог TextPanel (только неформатированный текст)

Панель Canvas — единственная в группе, которая поддерживает явное позиционирование. Визуализация всех ее дочерних элементов начинается с фиксированной позиции. Если два элемента перекрываются, «побеждает» тот, кто визуализируется последним. Заметьте, что координаты относительны области, заданной Canvas, т. е. (0,0)  указывает верхний левый пиксел области. Вот пример Canvas-объекта:

<Canvas xmlns="http://schemas.microsoft.com/2003/xaml"
    Height="600" Width="800">
    <Border Background="red"
       Canvas.Top="0px" Canvas.Left="0px"
       Height="100px" Width="100px" />
    <Border Background="green "
       Canvas.Top="100px" Canvas.Left="100px"
       Height="100px" Width="100px" />
    <Border Background="blue"
       Canvas.Top="50px" Canvas.Left="50px"
       Height="100px" Width="100px" />
</Canvas>

Та же концепция вложения реализуется компонентом DockPanel иначе. Все дочерние элементы этой панели стыкуются друг с другом по горизонтали или вертикали. В данном случае позиционирование осуществляется относительно предыдущего элемента управления, и никакие координаты не нужны. DockPanel идеален для создания полоски кнопок вроде панели инструментов (toolbar). Вот пример:

<Border xmlns="http://schemas.microsoft.com/2003/xaml" Background="black">
    <DockPanel>
        <Button DockPanel.Dock="Left" Width="50px" Height="32px">
            Open
        </Button>
        <Button DockPanel.Dock="Left" Width="50px" Height="32px">
            Save
        </Button>
        <Button DockPanel.Dock="Left" Width="50px" Height="32px">
            Print
        </Button>
    </DockPanel>
</Border>

Первая кнопка размещается по левому краю панели, а каждая последующая стыкуется с предыдущей по горизонтали (рис. 6).

Рис. 6. Размещение кнопок

Элемент FlowPanel позволяет размещать дочерние компоненты в доступной области в различных направлениях. Эта панель также содержит логику для обработки ситуаций, в которых контент не умещается по ширине панели. В таком случае лишний контент переносится на следующую строку или обрезается в зависимости от конфигурации объекта панели. Следующий пример на XAML демонстрирует, как FlowPanel логически разбивает контент, не умещающийся в одну строку. Четыре элемента-квадрата содержатся в одном контейнере большего размера. Совокупная ширина малых квадратов превышает ширину контейнера. Поскольку эти квадраты не умещаются в одной строке, последний квадрат переносится на вторую строку. Заметьте, что направление по умолчанию — слева направо и сверху вниз:

<FlowPanel xmlns="http://schemas.microsoft.com/2003/xaml"
                 Width="250px" Height="250px">
    <Border Background="red" Width="75px" Height="75px" />
    <Border Background="green" Width="75px" Height="75px" />
    <Border Background="blue" Width="75px" Height="75px" />
    <Border Background="orange" Width="75px" Height="75px" />
</FlowPanel>

GridPanel — облегченный элемент для разметки контента объектов в виде сравнительно простых таблиц (сеток). GridPanel позволяет заполнять сетку из столбцов и строк любой комбинацией элементов управления, но обладает ограниченной функциональностью. Более сложные таблицы создаются с помощью панели Table. Структура Table состоит из групп строк. Вы можете определять группу строк для верхнего или нижнего заголовка и вставлять группы строк с разными параметрами. Возможности этой панели в разметке позволяют создавать весьма изощренные табличные представления данных.

Наконец, панель TextPanel идеальна для сложной разметки текста. Однако для базового оформления текста лучше использовать элемент Text, который является облегченной версией TextPanel.

Пространство имен MSAvalon.Windows.Controls группирует все пользовательские элементы управления, отвечающие за взаимодействие с пользователем. Примеры классов, принадлежащих этому пространству имен, — старые знакомые вроде Button, ComboBox, ListBox и CheckBox, а также новые классы наподобие ContextMenu, PageViewer и RadioButtonList. Базовым классом для интерактивных элементов служит Control, который обеспечивает общий базовый набор свойств и методов.

В частности, элемент управления PageViewer предоставляет UI для просмотра электронных документов и функциональность для разбиения контента на страницы и навигации по ним. В следующем примере создается средство просмотра страниц (рис. 7), которое занимает всю клиентскую область. Эта программа разбивает текст на страницы и выводит его. В качестве источника используется файл, заданный в атрибуте Source тэга PageViewer:

<DockPanel ID="root" xmlns="http://schemas.microsoft.com/2003/xaml"
  xmlns:def="Definition">
  <Text DockPanel.Dock="Top">See a PageViewer control in action below.
  </Text>
  <PageViewer DockPanel.Dock="Top"
    Source="Sample.xaml" Height="80%" Width="80%"/>
  <Button DockPanel.Dock="Top" Width="50%">OK</Button>
</DockPanel>

Рис. 7. Средство просмотра страниц

Любопытно, что файл-источник, показываемый через элемент управления PageViewer, не может быть чисто текстовым, RTF или HTML. По крайней мере на данный момент он должен быть XAML-файлом, содержащим текст в кодировке ASCII и какой-либо Avalon-элемент, поддерживающий разбиение на страницы. Avalon — это кодовое название новой презентационной подсистемы (presentation sybsystem) в Longhorn (см. статью Чарльза Петцольда в этом номере). Например, взгляните на класс TextPanel:

<TextPanel ID="root" xmlns="http://schemas.microsoft.com/2003/xaml">
Сюда помещается отображаемый текст
</TextPanel>

Сохраните этот фрагмент кода в файле sample.xaml, чтобы запустить предыдущий пример. При этом следует помнить о необходимости пространства имен xmlns.

Классы в пространстве имен MSAvalon.Windows.Documents обрабатывают отображение документов. В совокупности эти классы могут показаться вам чем-то вроде надмножества дополнительных HTML-тэгов. К этим классам относятся Block, Column, Heading, Footer, ColumnGroup, RowGroup, HyperLink, List и многие другие.

Класс Text, с которым вы недавно познакомились, принадлежит пространству имен System.Windows.Controls (во всяком случае, в этой предварительной версии Longhorn). Он поддерживает многострочный текст, вложенные дочерние элементы и такие параметры форматирования, как размер шрифта и его полужирное или курсивное начертание. Если вас устраивает сравнительно простое оформление текста, всегда предпочтительнее использовать класс Text, так как он требует меньше ресурсов.

Презентационная модель Longhorn визуализирует графику с использованием Panel-элементов XAML. Панели предоставляют ряд свойств, связанных с размерами и выравниванием, а также управляют границами, цветом фона и заливкой.

Графический контент в Longhorn визуализируется через Windows Vector Graphics, которая обладает рядом преимуществ по сравнению с GDI и GDI+. Windows Vector Graphics — это система графической разметки на основе XML, простая в использовании. Если вы — фанат Scalable Vector Graphics, вам понравится и эта система. Windows Vector Graphics поддерживает набор предопределенных фигур, в том числе Ellipse, Line, Path, Polygon, Polyline и Rectangle, которые наследуют от класса Shape. Эти элементы получают от базового класса группу общих атрибутов, включая Stroke, StrokeThickness, Fill и некоторые атрибуты, определяющие координаты и вершины. Применяя трансформации, фигуры можно вращать, масштабировать и т. д. Заливка может быть не только обычной, но и градиентной. В следующем примере в свойстве Fill фигуры Rectangle указывается горизонтальный градиент, где начальным цветом является красный (Red), а конечным — голубой (Blue):

<Rectangle xmlns="http://schemas.microsoft.com/2003/xaml"
    Canvas.Top="10" Canvas.Left="10"
    Fill="HorizontalGradient Red Blue"
    RectangleLeft="0" RectangleTop="0"
    Width="50" Height="50"/>

Создание приложения-примера

Используя язык XAML, можно быстро подготовить прототип UI. Уверен, что к моменту начала поставок Longhorn множество инструментов разработки будет обновлено или создано заново для полной поддержки XAML со средствами WYSIWYG. Longhorn в редакции Tech Preview уже включает некоторые расширения для Whidbey-версии Visual Studio .NET. Рассмотрим более серьезное интерактивное приложение. Вы быстро обнаружите, что его разработка не сильно отличается от написания приложения Windows Forms, и приятно удивитесь тому, что все ваши навыки, приобретенные в работе с .NET, в полной мере пригодятся в Longhorn. На рис. 8 показан UI для небольшого приложения-примера, включающий текстовое поле и контекстное меню. Текстовое поле определено на XAML; контекстное меню создается «на лету». Полный исходный код этого приложения см. на рис. 9.

Рис. 8. Контекстное меню

Рис. 9. Приложение с контекстным меню

<Window
    xmlns="http://schemas.microsoft.com/2003/xaml"
    xmlns:def="Definition"
    def:Class="Application2.Window1"
    def:CodeBehind="Window1.xaml.cs"
    Text="Application2" Visible="True">

    <FlowPanel DockPanel.Dock="Fill">
        <TextBox ID="input" Margin="10,10"
                 Background="LightCyan"
                 BorderBrush="black" FontFamily="verdana"
                 FontSize="16"
                 BorderThickness="1"
                 ContextMenuEvent="OnContextMenu"
                 Height="40" Width="360">Right-click to see a
                     context menu</TextBox>
    </FlowPanel>
</Window>

// Файл исходного кода на C# для приложения

namespace MsdnLHSample
{
    public partial class MyApp : Application
    {
        void AppStartingUp(object sender, StartingUpCancelEventArgs e)
        {
            Window mainWindow = new Window1();
            mainWindow.Show();
        }
    }
}

// Файл исходного кода на C# для окна

using System;
using MSAvalon.Windows;
using MSAvalon.Windows.Media;
using MSAvalon.Windows.Controls;
using MSAvalon.Windows.Documents;
using MSAvalon.Windows.Navigation;
using MSAvalon.Windows.Shapes;
using MSAvalon.Windows.Data;

namespace MsdnLHSample
{

    // Ввод осуществляется через текстовое поле, объявленное в XAML

    public partial class Window1 : Window
    {
        private void OnContextMenu(object sender,
            ContextMenuEventArgs e)
        {
            CreateMenu();
        }

        private void CreateMenu()
        {

            ContextMenu cm = new ContextMenu();
            cm.FontFamily = "verdana";
            cm.Background = Brushes.LightYellow;

            MenuItem mi0 = new MenuItem();
            mi0.Header = "Lower case";
            mi0.FontSize = new FontSize(16);
            mi0.Click += new ClickEventHandler(LowerCase);

            cm.Items.Add(mi0);

            MenuItem mi1 = new MenuItem();
            mi1.FontSize = new FontSize(16);
            mi1.Header = "Upper case";
            mi1.Click += new ClickEventHandler(UpperCase);

            cm.Items.Add(mi1);

            MenuItem mi2 = new MenuItem();
            mi2.Header = "Select All";
            mi2.FontSize = new FontSize(16);
            mi2.Click += new ClickEventHandler(SelectAll);

            cm.Items.Add(mi2);

            input.ContextMenu = cm;
        }

        public void LowerCase(Object sender, ClickEventArgs args)
        {
            MenuItem mi = (MenuItem) args.Source;
            ContextMenu menu = (ContextMenu) mi.Parent;
            TextBox thisTextBox = (TextBox) menu.PlacementTarget;

            thisTextBox.Text = thisTextBox.Text.ToLower();
        }

        public void UpperCase(Object sender, ClickEventArgs args)
        {
            MenuItem mi = (MenuItem)args.Source;
            ContextMenu menu = (ContextMenu)mi.Parent;
            TextBox thisTextBox = (TextBox)menu.PlacementTarget;

            thisTextBox.Text = thisTextBox.Text.ToUpper();
        }

        public void SelectAll(Object sender, ClickEventArgs args)
        {
            MenuItem mi = (MenuItem)args.Source;
            ContextMenu menu = (ContextMenu)mi.Parent;
            TextBox thisTextBox = (TextBox)menu.PlacementTarget;

            thisTextBox.SelectAll();
        }
    }
}

В Longhorn все приложения представляются экземпляром класса Application. Этот объект является сердцевиной модели приложений в Longhorn. Однако объект Application предоставляет лишь базовую поддержку для приложения и, как правило, будет применяться только в приложениях, которым не нужна навигация между страницами и которые должны занимать минимум ресурсов. На практике большинство Longhorn-приложений будут использовать в основном объект NavigationApplication, производный от Application и поддерживающий навигацию.

Объект NavigationApplication предоставляет множество методов, свойств и событий, которые позволяют собирать набор XAML-страниц в единое приложение. В некотором смысле Longhorn-приложения на основе более простого класса Application сравнимы с Win32-приложениями на основе диалога, а приложения с поддержкой навигации — с полнофункциональными Win32-программами. Следующий код демонстрирует навигацию:

myApp = NavigationApplication.Current;
win = (Navigation.NavigationWindow) myApp.Windows[0];
...
private void Button_Back(Object sender, ClickEventArgs e)
{
    // Если возможно, переходим к предыдущему окну
    if(win.CanGoBack())
       win.GoBack();
}

Ссылку на объект приложения вы получаете через статическое свойство Current объекта Application (или NavigationApplication). Предыдущий пример иллюстрировал, как получить ссылку на первое окно. Обработчик Button_Back проверяет, существует ли объект предыдущего окна, и, если да, переходит к нему в стиле браузера.

Следующий пример показывает, как настроить начальное поведение приложения с поддержкой навигации, создав производный класс и переопределив метод OnStartingUp. В данном случае переопределенный метод OnStartingUp создает и отображает окно навигации (navigation window) сразу после запуска приложения:

public class MyApp : NavigationApplication
{
    NavigationWindow win;
    ...
    protected override void OnStartingUp(StartingUpCancelEventArgs e)
    {
        win = new NavigationWindow();

        // Добавляем элементы в окно
        ...
        navWin.Show();
    }
    ...
}

Вернемся к приложению-примеру на рис. 8. В свете только что высказанных соображений вы могли бы использовать в качестве базового класса либо Application, либо NavigationApplication, поскольку это приложение имеет дело с одним окном, т. е. фактически основано на диалоге. Рабочий класс, названный Sample1, перегружает OnStartingUp и определяет ряд обработчиков событий. Перегрузка метода OnStartingUp нужна потому, что он отвечает за инициализацию приложения при запуске и ввиду этого является идеальным местом для выполнения собственных операций.

В данном случае в OnStartingUp создается окно. Его содержимое описывается в XAML-файле и классе отделенного кода. Сама XAML-страница состоит из элементов управления и других компонентов, организованных в иерархическое дерево. Именно взаимосвязь всех этих частей, обобщенно называемых элементами, в наибольшей мере определяет, как именно визуализируется страница и как она ведет себя в дальнейшем. В страницу-пример на рис. 8 встроен элемент управления TextBox, для которого установлен определенный шрифт и размер. Текстовое поле связано с событием ContextMenuEvent. Это событие генерируется всякий раз, когда пользователь щелкает правой кнопкой мыши клиентскую область элемента управления.

Обработчик события создает объект ContextMenu (рис. 9). Контекстное меню заполняется, настраивается и сопоставляется со своим родительским объектом, которым в данном случае является TextBox. Стоит заметить, что Longhorn позволяет легко настраивать внешний вид меню (так же, как и любых других элементов управления). Для этого вы просто выбираете цвет фона, основной цвет, границы и шрифты. Пусть вас не вводит в заблуждение тот факт, что такое было возможно и в предыдущих версиях Windows или в .NET Framework. В Win32 подобный вид настройки требовал от программиста массы усилий; эту операцию можно было называть какой угодно, но только не простой. В .NET Framework классы-оболочки инкапсулировали лишь необходимый код и предоставляли программисту всего несколько свойств.

Элементы контекстного меню создаются из класса MenuItem. Способ их подключения к обработчикам событий почти идентичен тому, как это делается в Framework-приложении:

mia = new MenuItem[3];
for (int i=0; i<3; i++)
{
    mia[i] = new MenuItem();
    cm.Items.Add(mia[i]);
    mia[i].Foreground = Brushes.Black;
}
mia[0].Header = "Lower Text";
mia[1].Header = "Upper case";
mia[2].Header = "Select all";
mia[0].Click += new ClickEventHandler(LowerCase);
mia[1].Click += new ClickEventHandler(UpperCase);
mia[2].Click += new ClickEventHandler(SelectAll);

При щелчке конкретного элемента меню запускается обработчик события, который получает ссылку на объект-источник из первого аргумента, передаваемого этому обработчику:

public void LowerCase(Object sender, ClickEventArgs args)
{
    MenuItem mi = (MenuItem) args.Source;
    ContextMenu menu = (ContextMenu) mi.Parent;
    TextBox thisTextBox = (TextBox) menu.PlacementTarget;
    thisTextBox.Text = thisTextBox.Text.ToLower();
}

Чтобы получить TextBox, вы должны пройти соответствующий путь вверх по дереву. Сначала получить MenuItem, потом ContextMenu и, наконец, TextBox. После этого вы можете легко модифицировать содержимое TextBox.

Заключение

Longhorn — важная веха в истории операционных систем Windows. Она станет первой версией Windows, оптимизированной под управляемый код. Еще одно значимое достижение в Longhorn — новая модель приложений; архитектура и реализация Longhorn вобрали в себя весь предыдущий опыт в моделировании приложений на базе .NET Framework и ASP.NET в частности.

В Longhorn приложения строятся на основе объекта Application, и набор страниц разметки упорядочивается в связанное приложение. Введение языка XAML создает новый уровень абстракции, на который опирается модель приложений в Longhorn. Конечная цель Longhorn — дать возможность писать приложения однократно, а развертывать их самыми разными способами, в том числе через Web.

Разумеется, многие характеристики новой операционной системы скорее всего претерпят существенные изменения, поэтому моя статья — не более чем «моментальный снимок» того состояния, в котором Longhorn находится сейчас.


Дино Эспозито (Dino Esposito) — преподаватель и консультант из Рима. Автор книги «Programming ASP.NET» (Microsoft Press). Основное время посвящает преподаванию курсов по ADO.NET и ASP.NET; часто выступает на конференциях. С ним можно связаться по адресу cutting@microsoft.com.

Исходные коды

Первый взгляд на написание и развертывание приложений в Windows следующего поколения (4 КБ)


Опубликовал admin
20 Окт, Среда 2004г.



Программирование для чайников.