Настройка нестандартных окон
В большинстве случаев фиксированная графика в WPF для создания окон необычной формы не используется. Вместо этого в таких окнах просто применяется совершенно прозрачный фон, на котором затем размещается уже имеющее нужную форму содержимое. (Примером этого является показанная ниже кнопка, которая «нависает» над совершенно прозрачной областью.)
Преимущество такого подхода заключается в том, что он является модульным. Окно может состоять из множества отдельных компонентов, представляющих собой первоклассные элементы WPF. Но даже еще более важно то, что такой подход позволяет пользоваться другими функциональными возможностями WPF и создавать по-настоящему динамические пользовательские интерфейсы. Например, он позволяет создавать содержимое необычной формы с возможностью изменения его размеров или применять анимацию для обеспечения непрерывно выполняющихся эффектов прямо внутри окна. Сделать такое очень не просто, если графика находится в одном статическом файле.
Ниже показан пример. Здесь окно содержит элемент Grid с единственной ячейкой. Эту ячейку совместно используют два элемента. Первый — элемент Path, который прорисовывает границу окна нестандартной формы и заливает ее градиентным узором, а второй — контейнер макета, в котором находится предназначенное для окна содержимое, перекрывающее элемент Path.
В данном случае в качестве контейнера макета служит элемент StackPanel, но в принципе это может быть и какой-то другой элемент (например, еще один Grid или Canvas для абсолютного позиционирования на основе координат). В этом элементе StackPanel находится кнопка закрытия (со знакомым значком X) и текст:
Ключевым компонентом в данном примере является элемент Path, который создает фон. Он представляет собой простую векторную фигуру, которая состоит из ряда линий и дуг.
В текущий момент элемент Path имеет фиксированный размер (так же, как и окно), однако его размер несложно сделать изменяемым, поместив элемент в контейнер Viewbox. Еще улучшить этот пример можно, придав кнопке для закрытия окна более убедительный внешний вид — например, с помощью векторного значка X, прорисовываемого на красной поверхности. Хотя для представления кнопки и обработки связанных с ней событий мыши можно было бы воспользоваться и отдельным элементом Path, лучше поступить следующим образом: изменить стандартный элемент управления Button путем применения шаблона и затем сделать элемент Path, прорисовывающий значок X, частью этой измененной кнопки.
Перемещение окон нестандартной формы
Одним из ограничений окон нестандартной формы является то, что в них отсутствует неклиентская область со строкой заголовка, позволяющая пользователю легко перетаскивать окно по рабочему столу. В Windows Forms это причиняло определенные неудобства — приходилось либо обеспечивать реакцию на события мыши вроде MouseDown, MouseUp и MouseMove и перемещать окно вручную при выполнении пользователем щелчка и перетаскивания, либо переопределять метод WndProc() и обрабатывать низкоуровневое сообщение WM_NCHITTEST.
В WPF эта задача решается гораздо легче. Здесь в любое время можно инициировать режим перетаскивания окна путем вызова метода Window.DragMove().
Итак, для того чтобы позволить пользователю перетаскивать окно необычной формы, которое было показано в предыдущем примере, необходимо просто добавить и обработать для окна (или того элемента в этом окне, который затем будет выполнять ту же роль, что и строка заголовка) событие MouseLeftButtonDown:
Теперь окно будет следовать за курсором мыши по экрану до тех пор, пока пользователь не отпустит кнопку мыши.
Изменение размеров окон нестандартной формы
Изменение размеров окна нестандартной формы — задача не из простых. Если форма окна хотя бы отчасти напоминает прямоугольник, наиболее простым подходом будет добавить в правом нижнем углу элемент захвата и изменения размера путем установки для свойства ResizeMode значения CanResizeWithGrip. Однако при размещении такого элемента предполагается, что окно имеет прямоугольную форму.
Например, в случае создания окна с эффектом скругленных краев за счет использования объекта Border, такой прием может и сработать. Элемент захвата и изменения размера появится в правом нижнем углу и, в зависимости от того, насколько скругленным был сделан этот угол, разместится в пределах поверхности окна, которому принадлежит. Но в случае создания окна более экзотической формы с применением, например, элемента Path, такой подход точно не сработает — элемент захвата и изменения размера «зависнет» в пустой области рядом с окном.
Если добавление элемента захвата и изменения размера не подходит для окна данной формы или если требуется разрешить пользователю изменять размеры окна путем перетаскивания его краев, придется приложить немного дополнительных усилий. В принципе в таком случае существует два основных подхода. Первый — использовать .NET-функцию вызова платформы (P/Invoke) для отправки сообщения Win32, изменяющего размер окна, а второй — просто отслеживать позицию курсора мыши при перетаскивании пользователем окна в одну сторону и изменять размер вручную установкой свойства Width. Ниже рассматривается пример применения второго подхода.
Прежде чем воспользоваться любым из этих подходов, нужно придумать способ для определения момента наведения пользователем курсора мыши на край окна. В WPF это легче всего сделать, разместив вдоль каждого края окна специальный элемент. Быть видимым этому элементу вовсе необязательно — на самом деле он даже может быть полностью прозрачным и позволять окну проглядывать сквозь него. Его единственная задачей будет перехват событий мыши.
Лучше всего на эту роль подходит элемент Rectangle толщиной в 5 единиц. Ниже приведен пример размещения этого элемента так, чтобы он позволял изменять размер с правой стороны окна со скругленными углами:
В данном случае элемент Rectangle размещается в верхней строке, но для свойства RowSpan получает значение 3. Благодаря этому он растягивается на все три строки и занимает всю правую сторону окна. В свойстве Cursor указывается тот курсор мыши, который должен появляться при наведении мыши на этот элемент. В данном случае это курсор изменения размера «запад-восток» — он имеет знакомую всем форму двухконечной стрелки, которая указывает вправо и влево.
Обработчики событий элемента Rectangle переключают окно в режим изменения размера, когда пользователь щелкает на краю. Здесь необходимо захватить мышь для обеспечения уверенности в том, что события будут продолжать поступать даже в случае перемещения мыши за счет перетаскивания с поверхности прямоугольника в какую-нибудь сторону. Захват мыши снимается, когда пользователь отпускает левую кнопку мыши:
Источник
Шаблон окна
С помощью показанного до сих пор кода можно довольно легко создавать окна особой формы. Однако если нужно использовать новый стандарт окон для всего приложения, придется вручную стилизовать каждое окно, оснащая его границей одинаковой формы, областью заголовка, кнопками для закрытия и т.д. В такой ситуации удобнее поместить код разметки в шаблон элемента управления и затем использовать этот шаблон в отношении любого окна.
Первым делом нужно изучить предлагаемый по умолчанию шаблон элемента управления для класса Window. В основном он довольно прост, но включает в себя одну, возможно, неожиданную деталь — элемент AdornerDecorator. Этот элемент создает поверх остальной части клиентского содержимого окна специальную область рисования, называемую . Элементы управления WPF могут использовать этот слой для прорисовывания содержимого, которое должно налагаться поверх других элементов.
К такому содержимому относятся небольшие графические индикаторы, отражающие фокус, помечающие ошибки проверки достоверности и указывающие путь при операциях перетаскивания. При создании специального окна необходимо позаботиться о наличии декоративного слоя, чтобы использующие его элементы управления могли продолжать функционировать.
После этого можно приступать к определению базовой структуры, которую должен иметь шаблон окна. Ниже показан стандартный пример разметки, в которой предполагается создание окна, подобного показанному в предыдущей статье:
Элементом самого верхнего уровня в данном шаблоне является объект Border, который отвечает за рамку окна. Внутри него размещается элемент Grid с тремя строками. Содержимое элемента Grid распределяется следующим образом:
В самой верхней строке размещается строка заголовка, которая состоит из обычного элемента TextBlock, отображающего заголовок окна и кнопку закрытия. Привязка шаблона извлекает заголовок окна из свойства Window.Title.
В средней строке размещается вложенный элемент Border с остальной частью содержимого окна. Это содержимое вставляется с использованием элемента Content Presenter. Элемент ContentPresenter помещается в оболочку AdornerDecorator, что гарантирует размещение поверх него декоративного слоя.
В третьей строке размещается еще один элемент ContentPresenter. Он извлекает свое содержимое не из свойства Window.Content с помощью стандартной привязки, а получает его явно из свойства Window.Tag. Обычно это содержимое представляет собой обычный текст, но может включать любой другой элемент.
Свойство Tag используется потому, что в классе Window не предусмотрено свойства, предназначенного для указания текста нижнего колонтитула. Другим возможным вариантом является создание специального класса, унаследованного от Window, и добавление к нему дополнительного свойства Footer.
В третьей строке также размещается элемент захвата и изменения размера. Он отображается триггером, когда свойство Window.ResizeMode установлено в CanResizeWithGrip.
И, наконец, последними идут два невидимых прямоугольника, которые размещаются вдоль правого и нижнего края элемента Grid (и, соответственно, самого окна). Они позволяют пользователю изменять размеры окна щелчком и перетаскиванием.
В этом коде разметки не показаны малоинтересные стили элемента захвата и изменения размера (просто создает небольшой узор из точек) и кнопки закрытия окна (рисует небольшой символ X в красном квадрате). Разметка также не включает детали форматирования наподобие градиентной кисти, отвечающей за прорисовку фона, и свойств, отвечающих за создание скругленных углов границы окна.
Шаблон окна применяется с использованием простого стиля. Стиль также устанавливает три ключевых свойства класса Window, которые делают его прозрачным. Это позволяет создать границу окна и фон с помощью элементов WPF.
Здесь имеется лишь одна проблема. В настоящий момент окно не обладает большей частью базового поведения, которое требуется окнам. Например, его нельзя ни перетаскивать по рабочему столу, ни изменять в размерах, ни закрывать с помощью соответствующей кнопки. Для выполнения этих действий необходим соответствующий код.
Существует два возможных способа для добавления необходимого кода: расширение примера до специального класса, производного от Window, либо создание класса отделенного кода для словаря ресурсов. Подход с созданием специального класса лучше с точки зрения инкапсуляции и позволяет расширять общедоступный интерфейс окна (например, добавлять полезные методы и свойства, пригодные для использования в приложении).
Подход с созданием класса отделенного кода является относительно облегченной альтернативой и позволяет расширить возможности шаблона элемента управления, сохранив за приложением возможность продолжать пользоваться базовыми классами элементов управления.
Источник
Окна нестандартной формы
Окна необычной формы часто являются товарным знаком современных прикладных приложений вроде редакторов фотографий, программ для создания кинофильмов и МРЗ-проигрывателей; скорее всего, они будут встречаться в WPF-приложениях даже более часто.
В создании базового приложения нестандартной формы в WPF нет ничего сложного. Однако создание привлекательного профессионально выглядящего окна необычной формы требует немалых усилий — и, нередко, привлечения талантливого дизайнера графики для создания эскизов и фоновой графики.
Базовая процедура для создания окна нестандартной формы подразумевает выполнение следующих шагов:
Установите для свойства Window.AllowsTransparency значение true.
Установите для свойства Window.WindowStyle значение None, чтобы скрыть не клиентскую область окна (рамку голубого цвета). Если этого не сделать, при попытке показать окно появится ошибка InvalidOperationException.
Установите для фона (свойства Background) прозрачный цвет (цвет Transparent, значение альфа-канала которого равно нулю). Или же сделайте так, чтобы для фона использовалось изображение, имеющее прозрачные области (с нулевым значением альфа-канала).
Эти три шага эффективно удаляют стандартный внешний вид окна. Для обеспечения эффекта окна необычной формы далее необходимо предоставить какое-то непрозрачное содержимое, имеющее нужную форму. Здесь возможны перечисленные ниже варианты:
Предоставить фоновую графику, используя файл такого формата, который поддерживает прозрачность. Например, для фона можно использовать файл PNG. Это простой прямолинейный подход, и он очень удобен, если приходится работать с дизайнерами, которые не разбираются в XAML. Однако из-за того, что окно будет визуализироваться с большим количеством пикселей и более высокими системными параметрами DPI фоновая графика может приобрести искаженный вид. Это также может представлять проблему и в случае разрешения пользователю изменять размеры окна.
Использовать доступные в WPF функции для рисования формы, чтобы создать фон с векторным содержимым. Такой подход исключает потерю качества, какими бы ни были размеры окна и настройка DPI системы. Однако в этом случае наверняка потребуется использовать средство проектирования, поддерживающее XAML, такое как Expression Blend.
Использовать более простой WPF-элемент, имеющий необходимую форму. Например, окно с замечательными скругленными углами можно создать с помощью элемента Border. Такой подход позволяет создавать окна с современным внешним видом в стиле Office без применения каких-либо дизайнерских навыков.
Ниже в качестве примера приведен код создания пустого прозрачного окна с применением первого подхода и предоставлением файла PNG для прозрачных областей:
Это окно необычной формы (состоящее из окружности и квадрата) имеет не только пробелы, сквозь которые может просматриваться находящееся за ним содержимое, но кнопки, которые выходят за границы изображения и накладываются на прозрачную область, из-за чего кажется, будто бы они существуют сами по себе, без окна:
Те, кому приходилось программировать с использованием Windows Forms ранее, наверняка заметят, что окна нестандартной формы в WPF имеют более четкие края, особенно на изгибах. Все дело в том, что WPF умеет выполнять сглаживание между фоном окна и находящимся за ним содержимым для создания более гладкого края.
Ниже показано другое, более простое окно необычной формы. В этом окне используется элемент Border со скругленными углами для придания окну отчетливого внешнего вида. Компоновка тоже является упрощенной, поскольку исключает случайный выход содержимого за пределы границы, а размер границы может легко изменяться без наличия элемента Viewbox:
В этом окне содержится элемент Grid с тремя строками, которые используются для строки заголовка, строки нижнего колонтитула и размещаемого между ними содержимого. В строке с содержимым находится еще один элемент Grid, который имеет другой фон и может содержать другие необходимые элементы (в текущий момент в нем находится только один единственный элемент TextBlock).
Для завершения внешнего вида этого окна осталось создать только кнопки, имитирующие размещаемые в правом верхнем углу стандартные кнопки для разворачивания, сворачивания и закрытия окна.
Источник