Настройка нестандартных окон
В большинстве случаев фиксированная графика в 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, которое использует набор элементов управления Telerik, и все работает и выглядит нормально. К сожалению, недавно нам пришлось заменить базовый класс всех наших диалогов, заменив RadWindow стандартным окном WPF (причина не имеет отношения к этому обсуждению). Таким образом, мы получили приложение, которое все еще выглядело красиво на всех компьютерах разработчика (Windows 7 с включенным Aero), но было уродливо при использовании в среде нашего клиента (терминальные службы под Windows Server 2008 R2).
RadWindow Telerik-это стандартный пользовательский элемент управления, который имитирует поведение диалога, поэтому стилизация не была проблемой. С окном WPF, однако, мне трудно изменить его «границу». То, что я подразумеваю под «границей» здесь, — это как строка заголовка со значком, так и 3 стандартные кнопки (свернуть, развернуть/восстановить, закрыть) и ручка изменения размера вокруг окна.
Как я могу изменить внешний вид этих элементов:
- цвет строки заголовка
- 3 стандарт кнопки
- реальный цвет границы окна
С закругленными углами, если это возможно.
6 ответов:
Это» неклиентские » области, которые управляются Windows. Вот документы MSDN по этому вопросу (соответствующая информация находится вверху).
В основном, вы устанавливаете WindowStyle вашего окна= «None», а затем создаете свой собственный интерфейс окна. (аналогичный вопрос по SO )
WindowStyle=»None» , AllowsTransparency=»True» и необязательно ResizeMode=»NoResize»
а затем задайте свойству Style окна свой собственный стиль окна, в котором вы создадите внешний вид окна (заголовок, кнопки, границы) на все, что хотите, и отобразите содержимое окна в виде ContentPresenter .
Эта кажется хорошей статьей о том, как вы можете достичь этого, но в интернете есть много других статей.
Я нашел более прямое решение из комментария @ DK в этот вопрос, решение написано Алексом и описано здесь с исходным кодом, Чтобы сделать настраиваемое окно:
- загрузите образец проекта здесь
- отредактируйте общее.xaml-файл для настройки макета.
- Наслаждайтесь :).
В этом примере показано, как полностью настроить стиль / внешний вид окна, включая неклиентские области: панель заголовка, границы, кнопки max, min и close, сохраняя при этом все ожидаемые функциональные возможности.
Я предлагаю вам начать с базового решения и настроить его в соответствии с вашими потребностями, это лучше, чем начинать с нуля!
Я искал то же самое, и я попадаю на это решение с открытым исходным кодом, я надеюсь, что это поможет.
Если кто-то говорит вам, что вы не можете, потому что это будет не клиентская область, и только Windows может контролировать это, они ошибаются!
Это только полуправда, потому что Windows позволяет задавать размеры для неклиентской области. Дело только в том, что это возможно во всех методах ядра Windows, и вы находитесь в .NET, а не C++. В любом случае, не волнуйся! P / Invoke существует только для такой вещи! Таким образом, вы можете вызвать методы ядра для настройки неклиентской области.
Однако это это действительно сложное решение, которое я придумал много раз назад. К счастью, начиная с .NET 4.5, вы можете использовать класс WindowChrome для настройки неклиентской области по своему усмотрению. Здесь Вы можете войти в MSDN.
Чтобы сделать вещи проще и чище, я перенаправлю вас сюда, руководство по изменению размера границы окна на то, что вы хотите. Установив его равным 0, вы сможете реализовать свою пользовательскую границу окна вместо границы системы.
Приношу свои извинения за то, что не опубликовал четкую информацию. пример, но позже я обязательно это сделаю.
Источник