Темы и скины в ASP.NET Web Forms: оформление сайта без копипасты

РазделASP.NETУровеньСредний Чтение9 минОпубликовано ТегиASP.NETWeb FormsТемыCSS

Темы ASP.NET позволяют задать вид всех GridView, кнопок и полей ввода в одном месте и переключать оформление сайта целиком. Разбираем устройство тем, скины и смену темы на лету.

Главное. Скин задаёт серверным элементам свойства и классы, CSS темы их оформляет; StyleSheetTheme уступает разметке страницы, Theme её перекрывает, а менять тему из кода можно только в PreInit.

Какую задачу решают темы

На крупном сайте Web Forms десятки страниц с GridView, кнопками и полями ввода. Если цвет шапки таблицы или ширина поля прописаны в разметке каждого элемента, любое изменение оформления превращается в поиск и замену по всему проекту. Темы, появившиеся в ASP.NET 2.0, выносят эти свойства в отдельное место.

Тема состоит из двух видов файлов. Обычные CSS-файлы описывают стили HTML, как и на любом сайте. Файлы скинов (.skin) задают значения свойств серверных элементов управления: CssClass, BorderWidth, стили строк GridView, шаблон PagerSettings. Скин срабатывает на сервере до формирования HTML, поэтому настраивает то, до чего CSS не дотянется.

Структура папки App_Themes

Темы хранятся в специальной папке App_Themes в корне приложения. Каждая подпапка это отдельная тема, её имя и есть имя темы. Внутри лежат файлы .skin, .css и картинки, на которые ссылаются стили.

Все CSS-файлы темы подключаются к странице автоматически, в алфавитном порядке имён. Если порядок важен (сначала сброс стилей, потом основные), называйте файлы с числовым префиксом: 01-reset.css, 02-site.css. Для автоматического подключения нужен элемент head с атрибутом runat=«server».

Структура проектаКомандная строка
App_Themes\
  Blue\
    01-reset.css
    02-site.css
    Controls.skin
    GridView.skin
    images\header.png
  Dark\
    01-reset.css
    02-site.css
    Controls.skin
    GridView.skin

Скин по умолчанию и именованные скины

Файл скина похож на разметку ASPX, только без ID. Элемент без атрибута SkinID становится скином по умолчанию: его свойства получат все элементы этого типа на страницах с темой. Элемент с SkinID это именованный скин, он применяется только к тем элементам, у которых указан такой же SkinID.

Для каждого типа элемента допустим один скин по умолчанию и сколько угодно именованных. Скины распределяют по нескольким файлам ради удобства, для ASP.NET все .skin в папке темы объединяются в один набор.

App_Themes/Blue/Controls.skinASPX
<%-- скин по умолчанию: все TextBox на сайте --%>
<asp:TextBox runat="server" CssClass="input" />

<%-- именованный скин: только кнопки с SkinID="Primary" --%>
<asp:Button runat="server" SkinID="Primary" CssClass="btn btn-primary" />
<asp:Button runat="server" CssClass="btn" />

<%-- единый вид всех таблиц --%>
<asp:GridView runat="server" CssClass="grid" GridLines="None"
    AllowPaging="True" PageSize="20">
  <HeaderStyle CssClass="grid-head" />
  <AlternatingRowStyle CssClass="grid-alt" />
  <PagerStyle CssClass="grid-pager" />
</asp:GridView>
В скинах можно задавать только свойства оформления, помеченные атрибутом Themeable. Попытка задать в скине, например, DataSourceID или Text для TextBox вызовет ошибку разбора.

Theme и StyleSheetTheme: кто кого перекрывает

Тему подключают двумя атрибутами, и разница между ними принципиальна. Атрибут Theme применяется после свойств, заданных в разметке страницы, и перекрывает их: если в скине CssClass=«input», а у элемента на странице CssClass=«wide», победит скин. Атрибут StyleSheetTheme применяется до разметки, и свойства страницы его перекрывают, как обычный каскад CSS.

Для общего оформления сайта обычно удобнее StyleSheetTheme: он задаёт разумные значения по умолчанию, а отдельная страница может их уточнить. Theme подходит, когда оформление должно быть строго единым и разработчики страниц не должны его менять.

  • на одной странице: атрибут Theme=«Blue» или StyleSheetTheme=«Blue» в директиве @Page;
  • для всего сайта: элемент pages с атрибутом theme или styleSheetTheme в web.config;
  • для отдельного элемента отключение: EnableTheming=«false».

Смена темы во время работы

Дать пользователю выбрать оформление (светлое, тёмное, контрастное) можно программно. Свойство Page.Theme разрешено устанавливать только в событии PreInit, позже ASP.NET уже применил скины, и попытка вызовет InvalidOperationException. Выбор пользователя хранят в cookie или профиле и читают в PreInit.

Чтобы не повторять код на каждой странице, его выносят в базовый класс, от которого наследуются все страницы сайта. Перед установкой проверяйте, что такая папка темы существует, иначе получите ошибку при запросе.

App_Code/BasePage.vbVB.NET
Public Class BasePage
    Inherits System.Web.UI.Page

    Private Shared ReadOnly Allowed As String() = {"Blue", "Dark", "Contrast"}

    Protected Overrides Sub OnPreInit(e As EventArgs)
        Dim cookie = Request.Cookies("theme")
        If cookie IsNot Nothing AndAlso Array.IndexOf(Allowed, cookie.Value) >= 0 Then
            Me.Theme = cookie.Value   ' только здесь, в PreInit
        End If
        MyBase.OnPreInit(e)
    End Sub
End Class

Ловушки при работе с темами

Темы удобны для оформления самих серверных элементов, но верстку сетки страницы, отступы и типографику по-прежнему держите в CSS. Хорошее разделение: скин задаёт только CssClass, а все визуальные значения живут в стилях темы.

  • нет head runat=«server», и CSS-файлы темы молча не подключаются;
  • CSS из темы выводится после ваших link в head, поэтому стили темы перекрывают стили, подключённые вручную;
  • браузер кэширует CSS темы, после правки стилей добавьте версию в имя файла или очистите кэш;
  • в Master Page атрибут Theme не задаётся, тема ставится у страницы или в web.config;
  • скины применяются только к серверным элементам, обычный HTML-тег input скин не получит.

Как это делают сегодня

Сейчас темы делают средствами самого CSS: набор пользовательских свойств (--color-bg, --color-accent) переключается атрибутом data-theme на html или медиазапросом prefers-color-scheme. Роль скинов играют дизайн-токены и компоненты: в Blazor это CSS isolation и общие компоненты-обёртки, в React компонентная библиотека с темой.

Вопросы и ответы

Почему стили темы не подключаются к странице?

Скорее всего, у head нет атрибута runat=«server». ASP.NET добавляет ссылки на CSS темы только в серверный head.

Чем Theme отличается от StyleSheetTheme?

Theme применяется после свойств страницы и перекрывает их, StyleSheetTheme применяется до них и уступает разметке. Для оформления по умолчанию удобнее StyleSheetTheme.

Где менять тему из кода?

Только в событии PreInit страницы или в OnPreInit базового класса. Позже установка свойства Theme вызывает исключение.

Читайте также