МИНОБРНАУКИ РОССИИ
ФЕДЕРАЛЬНОЕ ГОСУДАРСТВЕННОЕ БЮДЖЕТНОЕ ОБРАЗОВАТЕЛЬНОЕ УЧРЕЖДЕНИЕ ВЫСШЕГО ПРОФЕССИОНАЛЬНОГО ОБРАЗОВАНИЯ
Анапский филиал «Московский государственный гуманитарный университет имени М.А. Шолохова»
http://www.vr-online.ru/blog/osvaivaem-wpf-vmeste-chast3-ispolzuem-resursy-i-kisti-brushes-6778
Предмет: Устройство и функционирование информационной системы
Практическая работа №4. Работа с WPF.
Тема: Знакомство с WPF.
Цель занятия: приобретение первичных навыков признакомстве с WPF в VS 2013 на C#.
Максимальное время выполнения задания: 4 часа.
Теория. Общие сведения о WPF
В предыдущем уроке читатели должны были получить базовые знания по работе с Xaml, большое внимание было уделено контейнерам компоновки. Обилие теоретического материала, данного в Части 1 необходимо подкрепить практическими навыками. А потому данная часть будет ориентирована на кодинг (работу с Xaml в большей степени). В качестве примера, мы будем создавать простецкий текстовый редактор, он отлично подходит для того, чтобы как следует разобраться с контейнерами компоновки на практике, а так же рассмотреть принципы работы с Xaml.
Будем надеяться, вы уже создали новое WPF приложение для этого примера? (если нет, то создайте).
Немного включим фантазию, и представим: из чего состоит любой "уважающий" себя текстовый редактор. Наверно из меню в верхней части? Верно, но так как мы разрабатываем приложение с использованием WPF, то и меню у нас будет не стандартное выпадающее, а поинтереснее. Ну, меню это хорошо, но наверно текстовый редактор должен ещё содержать и само поле, куда необходимо вводить текст.
Порядок выполнения работы
Создаём WPF приложение.
Примечание: во всех скриншотах должна присутствовать фамилия исполнителя, дабы другие не смогли воспользоваться вашим отчетом!!!
Приступим непосредственно к созданию приложения. Как уже выше говорилось, окно приложения будет состоять из меню и поля для ввода текста. Таким образом, у нас будет две строки, которые как то надо определить в контейнере компоновки. Кто читал первую часть, те должны знать, что базовым контейнером компоновки является Grid, соответственно, в нём необходимо определить те пресловутые две строки, выглядеть это в Xaml разметке будет примерно так:
<Grid.RowDefinitions> <RowDefinition></RowDefinition> <RowDefinition></RowDefinition> </Grid.RowDefinitions>Опять же, те кто читал первую часть, знают, что по умолчанию, если у RowDefinitions не указывать Height, то эта Height равна «*», что опять же значит, что эти две строки возьмут равное пространство. Выглядить это будет примерно так, как на рис.1.
Рисунок 1. Grid двумя строками (Height="*").
На рисунке чётко видно, что дизайнер Xaml Visual Studio ровно поделил пространство формы на две части и отдал это пространство двум строкам.
Однако, наше приложение должно выглядеть немного не так: строка меню (та, что сверху) должна занимать далеко не пол формы, а лишь то пространство, которое необходимо кнопкам внутри этой строки. Дабы не утомлять вас, скажу, что свойство Height у первой RowDefinitions должно стоять в «auto».
Приступим к созданию панели меню в верхней части окна приложения. Для этих целей замечательно подойдёт StackPanel. Кто не читал первую часть – читайте, там написано зачем она нужна и какие даёт преимущества. Если коротко, то она позволяет выстраивать элементы, находящиеся внутри по вертикали, или по горизонтали. Меню у нас будет горизонтальное, потому Orientation надо поставить в Horizontal.
В нашем случае, Xaml разметка примет вид:
Обратите внимание, что так как мы поставили высоту первой строки в auto, эта строка берёт именно такую высоту, которая необходима элементам, содержащимся в этой строке. На рис.2. виден результат.
Рисунок 2. Меню с плоскими кнопками.
В данный момент всё это выглядит весьма безлико, а потому добавим картинок и симпатичный Border вокруг панели. Картинки можете загрузить отсюда. Для иконок в проекте лучше создать отдельную папку, для этого надо кликнуть на проекте, и выбрать Add-New folder (рис.3.)
Рисунок 3. создание новой папки в проекте.
Border может окружать любой элемент или контейнер компоновки бордюром, у которого имеется масса настроек: начиная от цвета линии и толщины, и заканчивая степенью скругления углов, который задаётся через CornerRadius.
<Border BorderBrush="#FFCBBEBE" BorderThickness="2" CornerRadius="10" > <StackPanel Orientation="Horizontal" Margin="4" > <Button ToolTip="Открыть" Margin="4" Background="White" BorderBrush="Transparent" > <Image Source="Icons\open.png" ></Image> </Button> <Button ToolTip="Копировать" Margin="4" Background="White" BorderBrush="Transparent" > <Image Source="Icons\copy.png" ></Image> </Button> <Button Margin="4" ToolTip="Очистить" Background="White" BorderBrush="Transparent" > <Image Source="Icons\clear.png" ></Image> </Button> <Button Margin="4" ToolTip="Вставить" Background="White" BorderBrush="Transparent" > <Image Source="Icons\paste.png" ></Image> </Button> </StackPanel> </Border>
Если вы всё сделали верно, то Border окружит StackPanel рамкой, со скруглёнными углами. CornerRadius у нас установлен в 10. Но можно задать его и для каждого отдельного угла в отдельности, например так: «10,0,20,10». Сначала идёт левый верхний угол (10), потом правый верхний (0), следом – правый нижний (20), и затем левый нижний (10). С помощью Border’a можно придавать весьма солидный вид многим панелям. BorderThickness у Border’a отвечает за толщину рамки, ну а BorderBrush устанавливает цвет рамки.
Как мы видим, внутрь кнопки мы помещаем Image, где в Source устанавливаем путь до картинки в проекте. WPF тем и хорош, что путём таких махинаций (я имею ввиду то, что в любой компонент можно вставлять всё что угодно, будь то TextBox, или что-то иное) программист может настраивать компоненты как ему угодно.
Так же, если вы были внимательны, у многих компонентов устанавливается свойство Margin, которое отвечает за отступ, в данном случае, мы устанавливаем Margin для кнопок, чтобы они не сливались друг с другом.
Результат этой разметки виден на рис.4.
Ads by Radio CanyonAd Options
Рисунок 4. Меню приложения.
Ну, теперь подобие меню у нас готово, надо в нижней части вставить текстовое поле. Немного усложним задачу: под текстовым полем у нас в правом углу должна быть кнопка, без разницы какая, просто должен быть в том углу какой то элемент. Приступим…
Будем использовать опять же Border, а внутри него расположим поле и кнопку. Поле должно менять свой размер с изменением размера формы, кнопка же должна всегда оставаться на месте и не менять своих размеров. Для такой задачи чудесно подойдёт Grid в две строки, в котором во второй строке будет DockPanel с выравниванием по правую сторону, где и будет содержаться та самая кнопка. Border надо добавить снизу Border’a в котором содержалось меню. Xaml разметка будет наподобие этой:
На рис.5. можно увидеть внешний вид готового приложения.
Рисунок 5. Работа с дизайном приложения завершена.
Здесь большая часть разметки должна быть вам знакома, DockPanel мы используем для той самой кнопки, которая должна быть в правом нижнем углу. При растягивании формы, поле ввода должно увеличиваться, а DockPanel, в которой содержится кнопка, должны оставаться на месте. Как раз для таких случаев и служит DockPanel.
Мы создали внешний вид текстового редактора. Я вас не учу C#, а потому мы не описывали действия при нажатии кнопок, к тому же у WPF имеются привязки команд, с помощью которых мы сможем реализовать обработчики событий для основных кнопок.
Используем ресурсы и кисти (Brushes)
Одной из отличительных черт WPF-приложений является уникальный пользовательский дизайн.Конечно, на этот дизайн уходит не мало времени, и можно обойтись и без него, но если уж WPF даёт такую возможность в руки программисту, то грех ею не воспользоваться. Ведь первое что бросается в глаза пользователю – это именно дизайн приложения, и именно он зачастую влияет на мнение заказчика/покупателя ваших программ.
Создание уникального дизайна форм, оформления кнопок и прочих пользовательских элементов в WPF неразрывно связано с такими понятиями как «ресурсы» и «стили». В данной статье речь пойдёт о ресурсах.
В прошлой части моих статей мы начали создавать простейший текстовый редактор, он в данный момент пока не наделён никакими функциями. В данной части мы продолжим совершенствовать данное приложение.
Для начала, мы хотим немного приукрасить верхнюю панель, на которой располагаются кнопки главного меню. На рис.1. показано то, что я имею в виду.
Рисунок 1. Главное меню приложения.
Допустим, мы хотим раскрасить панель, содержащую кнопки, в какой – то цвет. Заливку сплошным цветом можно выполнить с помощью свойства Background, но данный вариант нам не подходит, у нас будет не сплошная заливка, а заливка с помощью линейного градиента.
Вот отрывок из Xaml разметки, где в свойство Background StackPanel’и устанавливается линейный градиент, который заливает эту панель весьма интересным цветом:
<Border BorderBrush="#FFCBBEBE" BorderThickness="2" CornerRadius="10,0,15,20" >
<StackPanel Orientation="Horizontal" Margin="4" >
<StackPanel.Background>
<LinearGradientBrush>
<GradientStop Color="AliceBlue" Offset="0.0" />
<GradientStop Color="Aqua" Offset="0.5" />
<GradientStop Color="Aquamarine" Offset="0.6" ></GradientStop>
<GradientStop Color="BlanchedAlmond" Offset="0.7" ></GradientStop>
<GradientStop Color="Chartreuse" Offset="0.9" ></GradientStop>
<GradientStop Color="Gainsboro" Offset="1.0" ></GradientStop>
</LinearGradientBrush>
</StackPanel.Background>
<Button ToolTip="Открыть" Margin="4" Background="White" BorderBrush="Transparent" >
<Image Source="Icons\open.png" ></Image>
</Button>
Здесь вас интересует то, что начинается с StackPanel.Background и заканчивается соответствующим закрывающим тэгом… всё остальное у вас должно было остаться от предыдущей статьи.
Как я уже говорил, для градиентной заливки пространства используется LinearGradientBrush, образно говоря, это кисть с помощью которой происходит раскраска поверхности нашей панели. Для того чтобы цвета были разные, т.е один цвет плавно переходил в другой, необходимо для кисти установить GradientStop’ы. У нас, если внимательно присмотреться к разметке этих GradientStop’ов 6 штук, а это значит что наша панель будет разукрашена в 6 разных цветов, который будут плавно перетекать из одного в другой. Свойство Offset позволяет задать позицию, откуда будет начинаться тот или иной градиент. Offset может быть установлен в промежутке от 0.0 до 1.1. Соответственно, цвет AliceBlue у нас начинается от 0.0, что означает что данным цветом будет заливаться поверхность с самого начала. Далее у нас идёт цвет Aqua, которым поверхность заливается от 0.5. Ну и таким образом всем цветам устанавливается свой Offset. Итог применения линейного градиента можно увидеть на рис.2.
Рисунок 2. Применяем LinearGradientBrush.
Вообще говоря, свойство Background имеется у большинства элементов, а потому любой пользовательский элемент можно разукрасить как душе угодно. Кстати, у читателя может сложиться ложное впечатление, что в WPF имеется только градиентная заливка, это далеко не так.
Заливка происходит с помощью кистей (Brushes), потому LinearGradientBrish и имеет такое название, так вот, этих кистей несколько разновидностей, кроме линейной градиентной есть ещё SolidColorBrush (сплошная заливка цветом), RadialColorBrush (из названия понятно, что это радиальная заливка), ImageBrush – заливка при помощи рисунка из файла. Имеется так же DrawingBrush, VisualBrush. Но данные две кисти весьма редко мною используются, потому вполне возможно, они редко будут использованы и вами. В любом случае, если вас заинтересовала работа с кистями, то можете смело идти в конец статьи, там для вас имеется полезная ссылочка.
Допустим, что мы хотим раскрасить такой же заливкой панель, в которой находится поле для ввода. Вроде бы ничего сложного: можно добавить такую Xaml разметку для Grid’a.
<Border Background="Beige" BorderBrush="#FFCBBEBE" Grid.Row="1" BorderThickness="2" CornerRadius="5,5,0,0" >
<Grid Margin="5" >
<Grid.Background>
<LinearGradientBrush>
<GradientStop Color="AliceBlue" Offset="0.0" />
<GradientStop Color="Aqua" Offset="0.5" />
<GradientStop Color="Aquamarine" Offset="0.6" ></GradientStop>
<GradientStop Color="BlanchedAlmond" Offset="0.7" ></GradientStop>
<GradientStop Color="Chartreuse" Offset="0.9" ></GradientStop>
<GradientStop Color="Gainsboro" Offset="1.0" ></GradientStop>
</LinearGradientBrush>
</Grid.Background>
Опять же, здесь вас интересует то, что начинается с Grid.Background и заканчивается соответствующим закрывающим тэгом… всё остальное у вас должно было остаться от предыдущей статьи.
Результат можно посмотреть на рис.3.
Рисунок 3. Применяем LinearGradientBrush для Grid'a.
Всё бы ничего, но вас должно смущать то, что в двух разных местах у нас используется абсолютно одинаковая разметка, которая неплохо удлиняет код. Представим, что таких панелей у нас было бы не две, а десять… Десять раз вставлять одно и то же. В общем, для таких целей в WPF имеются ресурсы (Resources). У каждого окна имеется свойство Resources, которое имеет свой набор ресурсов. У каждого ресурса есть свой уникальный идентификатор, и именно по этому идентификатору к этим ресурсам можно обратиться и взять оттуда значение. То есть выходит, что нам сейчас надо определить градиентную заливку в ресурсах, а потом из разных мест обращаться к этому ресурсу для того чтобы взять оттуда то что нужно. Это весьма удобно, по крайней мере удобней, чем каждый раз тупо копировать разметку и вставлять её в другое место. Начнём…
В верхней части разметки создадим ресурс:
<Window x:Class="WPF2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525" WindowStartupLocation="CenterScreen" >
<Window.Resources>
<LinearGradientBrush x:Key="linearPanelBrush" >
<GradientStop Color="AliceBlue" Offset="0.0" />
<GradientStop Color="Aqua" Offset="0.5" />
<GradientStop Color="Aquamarine" Offset="0.6" ></GradientStop>
<GradientStop Color="BlanchedAlmond" Offset="0.7" ></GradientStop>
<GradientStop Color="Chartreuse" Offset="0.9" ></GradientStop>
<GradientStop Color="Gainsboro" Offset="1.0" ></GradientStop>
</LinearGradientBrush>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
Как видите, мы определили в качестве ресурса нашу градиентную заливку, где в свойство x:key задали название для этого ресурса. После этого можно удалить те места в StackPanel и в Grid’e,где мы явно задавали в качестве BackGround’a линейную заливку, и вместо этого в Background указать наш созданный ресурс. Выглядеть это будет так:
<Border BorderBrush="#FFCBBEBE" BorderThickness="2" CornerRadius="10,0,15,20" >
<StackPanel Orientation="Horizontal" Margin="4" Background="{StaticResource linearPanelBrush}" >
..
И для Grid’a:
<Grid Margin="5" Background="{StaticResource linearPanelBrush}" >
<Grid.RowDefinitions>
Здесь мы в Background задаём наш ресурс. Ресурсы, так же как и стили, привязки (об этом всём позже..) устанавливаются в фигурных скобках. Сначала у нас идёт StaticResource, а потом собственно само название ресурса. Ресурсы бывают статическими (StaticResource) и динамическими (DynamicResource). Наш ресурс – статический, он задаётся в единственном месте и после этого не меняется. После внесённых изменений результат должен быть тот же, что и прежде, когда мы для каждого свойства Background создавали свой объект LinearGradientBrush.
В данной части моего цикла статей по WPF основное внимание было уделено ресурсам, которые позволяют централизованно хранить объекты для их последующего применения. Так же были рассмотрены базовые кисти (Brushes), среди которых мы на практике применили LinearGradientBrush.