Привязка (Binding) | Содержание | Каркас приложения. Модель данных. Привязка данных. Табличный вывод.
Чтобы как-то взаимодействовать с пользователем, получать от пользователя ввод с клавиатуры или мыши и использовать введенные данные в программе, нам нужны элементы управления. WPF предлагает нам стандартный набор элементов управления
Все элементы управления могут быть условно разделены на несколько подгрупп:
Элементы управления содержимым, например кнопки (Button), метки (Label)
Специальные контейнеры, которые содержат другие элементы, но в отличие от элементов Grid или Canvas не являются контейнерами компоновки - ScrollViewer, GroupBox
Декораторы, чьё предназначение создание определенного фона вокруг вложенных элементов, например, Border или Viewbox.
Элементы управления списками, например, ListBox, ComboBox.
Текстовые элементы управления, например, TextBox, RichTextBox.
Элементы, основанные на диапазонах значений, например, ProgressBar, Slider.
Элементы для работ с датами, например, DatePicker и Calendar.
Остальные элементы управления, которые не вошли в предыдущие подгруппы, например, Image.
Рассмотрим некоторые из основных свойств, которые наследуются элементами управления.
Name
Наверное важнейшее свойство. По установленному имени впоследствии можно будет обращаться к элементу, как в коде, так и в xaml разметке. Например, в xaml-разметке у нас определена следующая кнопка:
<Button
x:Name="button1"
Width="60"
Height="30"
Content="Текст"
Click="button1_Click" />
Здесь у нас задан атрибут Click с названием метода обработчика button1_Click, который будет определен в файле кода C# и будет вызываться по нажатию кнопки. Тогда в связанном файле кода C# мы можем обратиться к этой кнопке:
private void button1_Click(object sender, RoutedEventArgs e)
{
button1.Content = "Привет!";
}
Поскольку свойство Name имеет значение button1, то через это значение мы можем обратиться к кнопке в коде.
Visibility
Это свойство устанавливает параметры видимости элемента и может принимать одно из трех значений:
Различия между Collapsed и Hidden можно продемонстрировать на примере:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<StackPanel
Grid.Column="0"
Background="Lavender">
<Button
Visibility="Collapsed"
Content="Панель Collapsed" />
<Button
Height="20"
Content="Visible Button" />
</StackPanel>
<StackPanel
Grid.Column="1"
Background="LightGreen">
<Button
Visibility="Hidden"
Content="Панель Hidden" />
<Button
Height="20"
Content="Visible Button" />
</StackPanel>
</Grid>
Свойства настройки шрифтов
FontFamily - определяет семейство шрифта (например, Arial, Verdana и т.д.)
FontSize - определяет высоту шрифта
FontStyle - определяет наклон шрифта, принимает одно из трех значений - Normal, Italic, Oblique.
FontWeight - определяет толщину шрифта и принимает ряд значений, как Black, Bold и др.
FontStretch - определяет, как будет растягивать или сжимать текст, например, значение Condensed сжимает текст, а Expanded - растягивает.
Например:
<Button
Content="Hello World!"
FontFamily="Verdana"
FontSize="13"
FontStretch="Expanded" />
Cursor
Это свойство позволяет нам получить или установить курсор для элемента управления в одно из значений, например, Hand, Arrow, Wait и др. Например, установка курсора в коде c#: button1.Cursor=Cursors.Hand;
FlowDirection
Данное свойство задает направление текста. Если оно равно RightToLeft, то текст начинается с правого края, если - LeftToRight, то с левого.
<StackPanel>
<TextBlock FlowDirection="RightToLeft">
RightToLeft
</TextBlock>
<TextBlock FlowDirection="LeftToRight">
LeftToRight
</TextBlock>
</StackPanel>
Цвета фона и шрифта
Свойства Background и Foreground задают соответственно цвет фона и текста элемента управления.
Простейший способ задания цвета в коде xaml: Background="#ffffff"
. В качестве значения свойство Background (Foreground) может принимать запись в виде шестнадцатеричного значения в формате #rrggbb
, где rr - красная составляющая, gg - зеленая составляющая, а bb - синяя. Также можно задать цвет в формате #aarrggbb
.
Либо можно использовать названия цветов напрямую:
<Button
Width="60"
Height="30"
Background="LightGray"
Foreground="DarkRed"
Content="Цвет" />
Элементы управления содержимым (content controls) представляют такие элементы управления, которые содержат в себе другой элемент.
К элементам управления содержимым относятся такие элементы как Button, Label, ToggleButton, ToolTip, RadioButton, CheckBox, GroupBox, TabItem, Expander, ScrollViewer. Также элементом управления содержимым является и главный элемент окна - Window.
Отличительной чертой всех этих элементов является наличие свойства Content, которое и устанавливает вложенный элемент. В этом элементы управления содержимым схожи с контейнерами компоновки. Только контейнеры могут иметь множество вложенных элементов, а элементы управления содержимым только один.
Свойство Content может представлять любой объект, который может относиться к одному из двух типов:
Объект класса, не наследующего от UIElement. Для такого объекта вызывается метод ToString(), который возвращает строковое преставление объекта. Затем эта строка устанавливается в качестве содержимого.
Объект класса, наследующего от UIElement. Для такого объекта вызывается метод UIElement.OnRender(), который выполняет отрисовку внутри элемента управления содержимым.
Рассмотрим на примере кнопки, которая является элементом управления содержимым:
<Button Content="Hello World!" />
В качестве содержимого устанавливается обычная строка. Этот же пример мы можем в XAML прописать иначе:
<Button>
<Button.Content>
Hello World!
</Button.Content>
</Button>
Либо мы можем использовать сокращенное неявное определения свойства Content:
<Button>
Hello World!
</Button>
Возьмем другой пример. Определим кнопку с именем button1:
<Window
x:Class="ControlsApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:ControlsApp"
mc:Ignorable="d"
Title="Элементы управления"
Height="250"
Width="300"
>
<StackPanel>
<Button
x:Name="button1" />
</StackPanel>
</Window>
А в файле кода MainWindow.xaml.cs
присвоим её свойству Content какой-либо объект:
using System;
using System.Windows;
namespace ControlsApp
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
double d = 5.6;
button1.Content = d;
}
}
}
В итоге число конвертируется в строку и устанавливается в качесте содержимого.
Иначе все будет работать, если мы в качестве содержимого используем объект, унаследованный от UIElement:
<Button x:Name="button1">
<Button Content="Hello" />
</Button>
Теперь в качестве содержимого будет использоваться другая кнопка, для которой при визуализации будет вызываться метод OnRender():
В отличие от контейнеров компоновки для элементов управления содержимым мы можем задать только один вложенный элемент. Если же нам надо вложить в элемент управления содержимым несколько элементов, то мы можем использовать те же контейнеры компоновки:
<Button x:Name="button1">
<StackPanel>
<TextBlock Text="Набор кнопкок" />
<Button
Background="Red"
Height="20"
Content="Red" />
<Button
Background="Yellow"
Height="20"
Content="Yellow" />
<Button
Background="Green"
Height="20"
Content="Green" />
</StackPanel>
</Button>
Выравнивание содержимого внутри элемента задается свойствами HorizontalContentAlignment (выравнивание по горизонтали) и VerticalContentAlignment (выравнивание по вертикали), аналогичны свойствам VerticalAlignment/HorizontalAlignment. Свойство HorizontalContentAlignment принимает значения Left, Right, Center (положение по центру), Stretch (растяжение по всей ширине). Например:
<StackPanel>
<Button
Margin="5"
HorizontalContentAlignment="Left"
Content="Left"
Height="90"
Width="500" />
<Button
Margin="5"
HorizontalContentAlignment="Right"
Content="Right"
Height="90"
Width="500" />
<Button
Margin="5"
HorizontalContentAlignment="Center"
Content="Center"
Height="90"
Width="500" />
</StackPanel>
VerticalContentAlignment принимает значения Top (положение в верху), Bottom (положение внизу), Center (положение по центру), Stretch (растяжение по всей высоте)
Padding
С помощью свойства Padding мы можем установить отступ содержимого элемента:
<StackPanel>
<Button
x:Name="button1"
Padding="50 30 0 40"
HorizontalContentAlignment="Left">
Hello World
</Button>
<Button
x:Name="button2"
Padding="60 20 0 30"
HorizontalContentAlignment="Center">
Hello World
</Button>
</StackPanel>
Свойство Padding задаётся в формате Padding="отступ_слева отступ_сверху отступ_справа отступ_снизу"
.
Если со всех четырех сторон предполагается один и тот же отступ, то, как и в случае с Margin, мы можем задать одно число:
<Button
x:Name="button2"
Padding="20"
Content="Hello World" />
Важно понимать, от какой точки задается отступ. В случае с первой кнопкой в ней контект выравнивается по левому краю, поэтому отступ слева будет предполагать отступ от левого края элемента Button. А вторая кнопка располагается по центру. Поэтому для нее отступ слева предполагает отступ от той точки, в которой содержимое бы находилось при центрировании без применения Padding.
Комбинация значений свойств HorizontalContentAlignment/VerticalContentAlignment и Padding позволяет оптимальным образом задать расположение содержимого.
В WPF кнопки представлены целым рядом классов, которые наследуются от базового класса ButtonBase:
Button
Элемент Button представляет обычную кнопку:
<Button
x:Name="button1"
Width="60"
Height="30"
Background="LightGray" />
От класса ButtonBase кнопка наследует ряд событий, например, Click, которые позволяют обрабатывать пользовательский ввод.
Чтобы связать кнопку с обработчиком события нажатия, нам надо определить в самой кнопке атрибут Click. А значением этого атрибута будет название обработчика в коде C#. А затем в самом коде C# определить этот обработчик.
Например, код xaml:
<Button
x:Name="button1"
Width="60"
Height="30"
Content="Нажать"
Click="Button_Click" />
И обработчик в коде C#:
private void Button_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show("Кнопка нажата");
}
Либо можно не задавать обработчик через атрибут, а стандартным образом для C# прописать в коде: button1.Click+=Button_Click;
К унаследованным свойствам кнопка имеет такие свойства как IsDefault и IsCancel, которые принимают значения true и false.
Если свойство IsDefault установлено в true, то при нажатии клавиши Enter будет вызываться обработчик нажатия этой кнопки.
Аналогично если свойство IsCancel будет установлено в true, то при нажатии на клавишу Esc будет вызываться обработчик нажатия этой кнопки.
Например, определим код xaml:
<Window
x:Class="ControlsApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:ControlsApp"
mc:Ignorable="d"
Title="Элементы управления"
Height="250"
Width="300"
>
<StackPanel>
<Button
x:Name="acceptButton"
Content="ОК"
IsDefault="True"
Click="acceptButton_Click" />
<Button
x:Name="escButton"
Content="Выход"
IsCancel="True"
Click="escButton_Click" />
</StackPanel>
</Window>
А в коде MainWindow.xaml.cs
определим следующий код C#:
using System.Windows;
namespace ControlsApp
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void acceptButton_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show("Действие выполнено");
}
private void escButton_Click(object sender, RoutedEventArgs e)
{
this.Close(); // закрытие окна
}
}
}
Теперь при нажатии на клавишу Enter будет отображаться сообщение, а при нажатии на Esc будет происходить выход из приложения и закрытие окна.
RepeatButton
Отличительная особенность элемента RepeatButton - непрерывная генерация события Click, пока нажата кнопка. Интервал генерации события корректируется свойствами Delay и Interval.
Сам по себе элемент RepeatButton редко используется, однако он может служить основой для создания ползунка в элементах ScrollBar и ScrollViewer, в которых нажатие на ползунок инициирует постоянную прокрутку.
ToggleButton
Представляет элементарный переключатель. Может находиться в трех состояниях - true, false и "нулевом" (неотмеченном) состоянии, а его значение представляет значение типа bool? в языке C#. Состояние можно установить или получить с помощью свойства IsChecked. Также добавляет три события - Checked (переход в отмеченное состояние), Unchecked (снятие отметки) и Intermediate (если значение равно null). Чтобы отрабатывать все три события, надо установить свойство IsThreeState="True"
ToggleButton, как правило, сам по себе тоже редко используется, однако при этом он служит основой для создания других более функциональных элементов, таких как checkbox и radiobutton.
CheckBox
Элемент CheckBox представляет собой обычный флажок. Данный элемент является производным от класса ToggleButton и поэтому может принимать также три состояния: Checked, Unchecked и Intermediate.
Чтобы получить или установить определенное состояние, надо использовать свойство IsChecked, которое также унаследовано от ToggleButton:
<StackPanel x:Name="stackPanel">
<CheckBox
x:Name="checkBox1"
IsThreeState="True"
IsChecked="False"
Height="20"
Content="Неотмечено" />
<CheckBox
x:Name="checkBox2"
IsThreeState="True"
IsChecked="True"
Height="20"
Content="Отмечено" />
<CheckBox
x:Name="checkBox3"
IsThreeState="True"
IsChecked="{x:Null}"
Height="20"
Content="Неопределено"/>
</StackPanel>
Установка свойства IsChecked="{x:Null}"
задает неопределенное состояние для элемента checkbox. Остальные два состояния задаются с помощью True и False. В данном примере также привязан к двум флажкам обработчик события Checked. Это событие возникает при установке checkbox в отмеченное состояние.
А атрибут IsThreeState="True"
указывает, что флажок может находиться в трех состояниях.
Ключевыми событиями флажка являются события Checked (генерируется при установке флажка в отмеченное состояние), Unchecked (генерируется при снятии отметки с флажка) и Indeterminate (флажок переведен в неопределенное состояние). Например, определим флажок:
<CheckBox
x:Name="checkBox"
IsChecked="False"
Height="20"
Content="Флажок"
IsThreeState="True"
Unchecked="checkBox_Unchecked"
Indeterminate="checkBox_Indeterminate"
Checked="checkBox_Checked" />
А в файле кода C# пропишем для него обработчики:
private void checkBox_Checked(object sender, RoutedEventArgs e)
{
MessageBox.Show(checkBox.Content.ToString() + " отмечен");
}
private void checkBox_Unchecked(object sender, RoutedEventArgs e)
{
MessageBox.Show(checkBox.Content.ToString() + " не отмечен");
}
private void checkBox_Indeterminate(object sender, RoutedEventArgs e)
{
MessageBox.Show(checkBox.Content.ToString() + " в неопределенном состоянии");
}
RadioButton
Элемент управления, также производный от ToggleButton, представляющий переключатель. Главная его особенность - поддержка групп. Несколько элементов RadioButton можно объединить в группы, и в один момент времени мы можем выбрать из этой группы только один переключатель. Например,
<StackPanel x:Name="stackPanel">
<RadioButton
GroupName="Languages"
Content="C#"
IsChecked="True" />
<RadioButton
GroupName="Languages"
Content="VB.NET" />
<RadioButton
GroupName="Languages"
Content="C++" />
<RadioButton
GroupName="Technologies"
Content="WPF"
IsChecked="True" />
<RadioButton
GroupName="Technologies"
Content="WinForms" />
<RadioButton
GroupName="Technologies"
Content="ASP.NET" />
</StackPanel>
Чтобы включить элемент в определенную группу, используется свойство GroupName. В данном случае у нас две группы - Languages и Technologies. Мы можем отметить не более одного элемента RadioButton в пределах одной группы, зафиксировав тем самым выбор из нескольких возможностей.
Чтобы проследить за выбором того или иного элемента, мы также можем определить у элементов событие Checked и его обрабатывать в коде:
<RadioButton
GroupName="Languages"
Content="VB.NET"
Checked="RadioButton_Checked" />
Обработчик в файле кода:
private void RadioButton_Checked(object sender, RoutedEventArgs e)
{
RadioButton pressed = (RadioButton)sender;
MessageBox.Show(pressed.Content.ToString());
}
TextBox
Этот элемент представляет поле для ввода текстовой информации.
Он имеет свойства TextWrapping, TextAlignment и TextDecorations.
Свойство TextWrapping позволяет переносить текст при установке этого свойства TextWrapping="Wrap"
. По умолчанию это свойство имеет значение NoWrap, поэтому текст не переносится.
Свойство TextAlignment выравнивает текст по центру (значение Center), правому (Right) или левому краю (Left): <TextBlock TextAlignment="Right">
Для декорации текста используется свойство TextDecorations, например, если TextDecorations="Underline"
, то текст будет подчеркнут.
С помощью свойства MaxLength можно задать предельное количество вводимых символов.
<TextBox
MaxLength="250"
TextChanged="TextBox_TextChanged">
Начальный текст
</TextBox>
В коде C# мы можем обработать событие изменения текста:
private void TextBox_TextChanged(object sender, TextChangedEventArgs e)
{
TextBox textBox = (TextBox)sender;
MessageBox.Show(textBox.Text);
}
По умолчанию, если вводимый текст превышает установленные границы поля, то текстовое поле растет, чтобы вместить весь текст. Но визуально это не очень хорошо выглядит. Поэтомуo мы можем перенести непомещающийся текст на новую строку, установив свойство TextWrapping="Wrap"
.
Чобы переводить по нажатию на клавишу Enter курсор на следующую строку, нам надо установить свойство AcceptsReturn="True"
.
Также мы можем добавить полю возможность создавать табуляцию с помощью клавиши Tab, установив свойство AcceptsTab="True"
Если нам вдруг потребуется перенести текст на другую строку, то тогда мы можем использовать элемент LineBreak:
<TextBox>
Однажды в студеную зимнюю пору
<LineBreak />
Я из лесу вышел
</TextBox>
Для отображения полос прокрутки TextBox поддерживает свойства VerticalScrollBarVisibility и НоrizontalScrollBarVisibility:
<TextBox
AcceptsReturn="True"
Height="100"
VerticalScrollBarVisibility="Auto"
HorizontalScrollBarVisibility="Auto">
Начальный текст
</TextBox>
Возможно, при создании приложения нам потребуется сделать текстовое поле недоступным для ввода (на время в зависимости от условий или вообще), тогда для этого нам надо установить свойство IsReadOnly="True"
.
Для выделения текста есть свойства SelectionStart, SelectionLength и SelectionText. Например, выделим программно текст по нажатию кнопки:
<StackPanel>
<TextBox
x:Name="textBox1"
Height="100" SelectionBrush="Blue" />
<Button
Content="Выделить текст"
Height="30"
Width="100"
Click="Button_Click"
Margin="10" />
</StackPanel>
Обработчик нажатия кнопки:
private void Button_Click(object sender, RoutedEventArgs e)
{
textBox1.SelectionStart = 5;
textBox1.SelectionLength = 10;
textBox1.Focus();
// данное выражение эквивалентно
//textBox1.Select(5, 10);
}
Проверка орфографии
TextBox обладает встроенной поддержкой орфографии. Чтобы ее задействовать, надо установить свойство SpellCheck.IsEnabled="True"
. Кроме того, по умолчанию проверка орфографии распространяется только на английский язык, поэтому, если приложение заточено под другой язык, нам надо его явным образом указать через свойство Language:
<DockPanel>
<TextBox
SpellCheck.IsEnabled="True"
Language="ru-ru">
Привет, как дила?
</TextBox>
</DockPanel>
Метка (Label)
Главной особенностью меток является поддержка мнемонических команд-клавиш быстрого доступа, которые передают фокус связанному элементу. Например,
<Label
Target="{Binding ElementName=TextBox1}">
_привет
</Label>
<TextBox
Name="TextBox1"
Margin="0 30 0 0"
Height="30"
Width="100" />
Теперь, нажав на клавишу "п", мы переведем фокус на связанное текстовое поле. При вызове приложения подчеркивание не отображается, чтобы отображать подчеркивание, надо нажать на клавишу Alt. Тогда чтобы перевести фокус на связанное текстовое поле необходимо будет нажать сочетание Alt + "п"
.
PasswordBox
Элемент предназначен для ввода парольной информации. По сути это тоже текстовое поле, только для ввода символов используется маска. Свойство PasswordChar устанавливает символ маски, отображаемый при вводе пароля. Если это свойство не задано, то по умолчанию для маски символа используется черная точка. Свойство Password устанавливает парольную строку, отображаемую по умолчанию при загрузке окна приложения.
<StackPanel>
<PasswordBox
PasswordChar="*"
MinHeight="30" />
<PasswordBox
MinHeight="30" />
</StackPanel>
RichTextBox
Для вывода текстового содержимого, насыщенного форматированием, графикой, предназначен RichTextBox. Можно даже сказать, что он выводит не просто текст, а документы с более сложным форматированием, чем обычный TextBox. Более подробно о нем, а также как на его основе создать простой текстовый редактор, мы поговорим в лекции, посвященной документам.
Эти элементы представлены в WPF довольно широко. Все они являются производными от класса ItemsControl. Все они содержат коллекцию элементов. Элементы могут быть напрямую добавлены в коллекцию, возможна также привязка некоторого массива данных к коллекции.
Возьмем простейший элемент-список - ListBox:
<Window
x:Class="ControlsApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:ControlsApp"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
mc:Ignorable="d"
Title="ListBox"
Height="200"
Width="300"
>
<Grid>
<ListBox Name="list">
<sys:String>Lumia 950</sys:String>
<sys:String>iPhone 6S Plus</sys:String>
<sys:String>Xiaomi Mi5</sys:String>
<sys:String>Nexus 5X</sys:String>
</ListBox>
</Grid>
</Window>
Все элементы, размещенные внутри спискового элемента ListBox, представляют элементы списка.
Коллекция объектов внутри элемента-списка доступна в виде свойства Items. Для управления элементами из этой коллекции мы можем использовать следующие методы:
А свойство Count позволяет узнать, сколько элементов в коллекции.
Например, применительно к вышеопределенному списку мы бы могли написать в коде C#:
list.Items.Add("LG G5");
list.Items.RemoveAt(1); // удаляем второй элемент
Нам необязательно вручную заполнять значения элемента управления списком, так как мы можем установить свойство ItemsSource, задав в качестве параметра коллекцию, из которой будет формироваться элемент управления списком. Например, в коде xaml-разметки определим пустой список:
<Grid>
<ListBox Name="list" />
</Grid>
А в файле отделенного кода выполним наполнение списка:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
string[] phones = { "iPhone 6S", "Lumia 950", "Nexus 5X", "LG G4", "Xiaomi MI5", "HTC A9" };
list.ItemsSource = phones;
}
}
Свойство ItemsSource в качестве значения принимает массив, хотя это моет быть и список типа List. И каждый элемент этого массива переходит в ListBox.
Еще одно важное свойство списковых элементов - это свойство DisplayMemberPath. Оно позволяет выбирать для отображения элементов значение одного из свойств объекта. Например, создадим в коде новый класс Phone:
class Phone
{
public string Title { get; set; }
public string Company { get; set; }
public int Price { get; set; }
}
Теперь создадим в xaml набор объектов этого класса Phone и выведем в списке значение свойства Title этих объектов:
<Window
x:Class="ControlsApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:ControlsApp"
mc:Ignorable="d"
Title="ListBox"
Height="220"
Width="300"
>
<Grid Background="Lavender">
<ListBox
Name="list"
DisplayMemberPath="Title">
<local:Phone
Title="iPhone 6S"
Company="Apple"
Price="54990" />
<local:Phone
Title="Lumia 950"
Company="Microsoft"
Price="39990" />
<local:Phone
Title="Nexus 5X"
Company="Google"
Price="29990" />
</ListBox>
</Grid>
</Window>
Поскольку мы используем класс, определенный в текущем проекте, то соответственно у нас обязательно должно быть подключено пространство имен проекте: xmlns:local="clr-namespace:ControlsApp"
. В принципе по умолчанию WPF уже его подключает. Кроме того, чтобы не возникало проблем с разметкой XAML, желательно сделать перестроение проекта. И в итоге окно нам выведет только названия смартфонов:
То же самое мы бы могли сделать программным способом:
list.ItemsSource = new List<Phone>
{
new Phone { Title="iPhone 6S", Company="Apple", Price=54990 },
new Phone {Title="Lumia 950", Company="Microsoft", Price=39990 },
new Phone {Title="Nexus 5X", Company="Google", Price=29990 }
};
list.DisplayMemberPath = "Title";
Все элементы управления списками поддерживают выделение входящих элементов. Выделенный элемент(ы) можно получить с помощью свойств SelectedItem(SelectedItems), а получить индекс выделенного элемента - с помощью свойства SelectedIndex. Свойство SelectedValue позволяет получить значение выделенного элемента.
При выделении элемента в списке генерируется событие SelectionChanged, которое мы можем обработать. Например, возьмем предыдущий список:
<ListBox
Name="list"
DisplayMemberPath="Title"
SelectionChanged="list_Selected">
<local:Phone
Title="iPhone 6S"
Company="Apple"
Price="54990" />
<local:Phone
Title="Lumia 950"
Company="Microsoft"
Price="39990" />
<local:Phone
Title="Nexus 5X"
Company="Google"
Price="29990" />
</ListBox>
А в файле кода определим обработчик для этого события:
private void list_Selected(object sender, RoutedEventArgs e)
{
Phone p = (Phone)list.SelectedItem;
MessageBox.Show(p.Title);
}
Важно учитывать, что так как в разметке xaml в списке определены элементы Phone, то в коде мы можем привести объект list.SelectedItem к типу Phone.
ComboBox
ComboBox содержит коллекцию элементов и образует выпадающий список:
<ComboBox
Name="phonesList"
Height="30"
VerticalAlignment="Top">
<TextBlock>LG Nexus 5X</TextBlock>
<TextBlock>Huawai Nexus 6P</TextBlock>
<TextBlock>iPhone 6S</TextBlock>
<TextBlock>iPhone 6S Plus</TextBlock>
<TextBlock>Microsoft Lumia 950</TextBlock>
</ComboBox>
ComboBoxItem
В качестве элементов в ComboBox-e мы можем использовать различные компоненты, но наиболее эффективным является применение элемента ComboBoxItem. ComboBoxItem представляет элемент управления содержимым, в который через свойство Content мы можем поместить другие элементы. Например:
<ComboBox
Height="50"
Width="150"
VerticalAlignment="Top"
>
<ComboBoxItem IsSelected="True">
<StackPanel Orientation="Horizontal">
<Image
Source="cats.jpg"
Width="60" />
<TextBlock>cats.jpg</TextBlock>
</StackPanel>
</ComboBoxItem>
<StackPanel Orientation="Horizontal">
<Image
Source="windowcat.jpg"
Width="60" />
<TextBlock>windowcat.jpg</TextBlock>
</StackPanel>
<StackPanel Orientation="Horizontal">
<Image
Source="234.jpg"
Width="60" />
<TextBlock>234.jpg</TextBlock>
</StackPanel>
</ComboBox>
Для создания первого элемента использовался элемент ComboBoxItem. Для второго и третьего такие элементы создаются неявно. Однако использование ComboBoxItem имеет преимущество, так как мы можем выделить данный элемент, установив свойство IsSelected="True"
, либо можем сделать недоступным с помощью установки свойства IsEnabled="False"
.
Событие SelectionChanged
Обрабатывая событие SelectionChanged, мы можем динамически получать выделенный элемент:
<ComboBox
Height="25"
Width="150"
SelectionChanged="ComboBox_Selected"
>
<!-- остальное содержимое списка-->
</ComboBox>
Обработка события в коде C#:
private void ComboBox_Selected(object sender, RoutedEventArgs e)
{
ComboBox comboBox = (ComboBox)sender;
ComboBoxItem selectedItem = (ComboBoxItem)comboBox.SelectedItem;
MessageBox.Show(selectedItem.Content.ToString());
}
Правда, для элементов со сложным содержимым подобный способ может не пройти, и если мы захотим получить текст, до него придется добираться, спускаясь по дереву вложенных элементов.
Свойства
Установка свойства IsEditable="True"
позволяет вводить в поле списка начальные символы, а затем функция автозаполнения подставит подходящий результат. По умолчанию свойство имеет значение False.
Это свойство работает в комбинации со свойством IsReadOnly: оно указывает, является поле ввода доступным только для чтения. По умолчанию имеет значение False, поэтому если IsEditable="True"
, то мы можем вводить туда произвольный текст.
Еще одно свойство StaysOpenOnEdit при установке в True позволяет сделать список раскрытым на время ввода значений в поле ввода.
DataGrid во многом похож на ListView, но более сложный по характеру и допускает редактирование содержимого таблицы.
В разделе о ListView мы создали класс Phone, объекты которого выводили в список:
public class Phone
{
public string Title { get; set; }
public string Company { get; set; }
public int Price { get; set; }
}
Теперь же выведем объекты в таблицу DataGrid. Чтобы DataGrid автоматически разбивал таблицу на столбцы, установим свойство AutoGenerateColumns="True"
(вроде можно и не устанавливать - оно по-умолчанию включено):
<Window
x:Class="ControlsApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:ControlsApp"
xmlns:col="clr-namespace:System.Collections;assembly=mscorlib"
mc:Ignorable="d"
Title="DataGrid"
Height="220"
Width="300">
<Grid Background="Lavender">
<DataGrid
x:Name="phonesGrid"
AutoGenerateColumns="True"
ItemsSource="{DynamicResource
ResourceKey=phones}">
<DataGrid.Resources>
<col:ArrayList x:Key="phones">
<local:Phone
Title="iPhone 6S"
Company="Apple"
Price="54990" />
<local:Phone
Title="Lumia 950"
Company="Microsoft"
Price="39990" />
<local:Phone
Title="Nexus 5X"
Company="Google"
Price="29990" />
</col:ArrayList>
</DataGrid.Resources>
</DataGrid>
</Grid>
</Window>
В данном случае префикс local ссылается на пространство имен текущего проекта, в котором определен класс Phone (xmlns:local="clr-namespace:Controls"
), а col - префикс-ссылка на пространство имен System.Collections (xmlns:col="clr-namespace:System.Collections;assembly=mscorlib"
). И это даст в итоге следующий вывод:
Программная установка источника для DataGrid:
List<Phone> phonesList = new List<Phone>
{
new Phone { Title="iPhone 6S", Company="Apple", Price=54990 },
new Phone {Title="Lumia 950", Company="Microsoft", Price=39990 },
new Phone {Title="Nexus 5X", Company="Google", Price=29990 }
};
phonesGrid.ItemsSource = phonesList;
Некоторые полезные свойства *DataGrid*
| --|-- RowBackground и AlternatingRowBackground | Устанавливают фон строки. Если установлены оба свойства, цветовой фон чередуется: RowBackground - для нечетных строк и AlternatingRowBackground - для четных ColumnHeaderHeight | Устанавливает высоту строки названий столбцов. ColumnWidth | Устанавливает ширину столбцов. RowHeight | Устанавливает высоту строк. GridLinesVisibility | Устанавливает видимость линий, разделяющих столбцы и строки. Имеет четыре значения - All - видны все линии, Horizontal - видны только горизонтальные линии, Vertical - видны только вертикальные линии, None - линии отсутствуют HeadersVisibility | Задает видимость заголовков HorizontalGridLinesBrush и VerticalGridLinesBrush | Задает цвет горизонтальных и вертикальных линий соответственно
Хотя предыдущий пример довольно прост, в нем есть несколько недочетов. Во-первых, у нас нет возможности повлиять на расстановку столбцов. Во-вторых, заголовки определены по названиям свойств, которые на английском языке, а хотелось бы на русском. В этом случае мы должны определить свойства отображения столбцов сами. Для этого надо воспользоваться свойством DataGrid.Columns и определить коллекцию столбцов для отображения в таблице.
Причем можно задать также и другой тип столбца, отличный от текстового. DataGrid поддерживает следующие варианты столбцов:
| --|-- DataGridTextColumn | Отображает элемент TextBlock или TextBox при редактировании DataGridHyperlinkColumn | Представляет гиперссылку и позволяет переходить по указанному адресу DataGridCheckBoxColumn | Отображает элемент CheckBox DataGridComboBoxColumn | Отображает выпадающий список - элемент ComboBox DataGridTemplateColumn | Позволяет задать специфичный шаблон для отображения столбца
Перепишем предыдущий пример с учетом новой информации:
<DataGrid
x:Name="phonesGrid"
AutoGenerateColumns="False"
HorizontalGridLinesBrush="DarkGray"
RowBackground="LightGray"
AlternatingRowBackground="White">
<DataGrid.Items>
<local:Phone
Title="iPhone 6S"
Company="Apple"
Price="54990" />
<local:Phone
Title="Lumia 950"
Company="Microsoft"
Price="39990" />
<local:Phone
Title="Nexus 5X"
Company="Google"
Price="29990" />
</DataGrid.Items>
<DataGrid.Columns>
<DataGridTextColumn
Header="Модель"
Binding="{Binding Path=Title}"
Width="90" />
<DataGridHyperlinkColumn
Header="Компания"
Binding="{Binding Path=Company}"
Width="80" />
<DataGridTextColumn
Header="Цена"
Binding="{Binding Path=Price}"
Width="50" />
</DataGrid.Columns>
</DataGrid>
Среди свойств DataGrid одним из самых интересных является RowDetailsTemplate. Оно позволяет задать шаблон отображения дополнительной информации касательно данной строки. Изменим элемент DataGrid:
<DataGrid
x:Name="phonesGrid"
AutoGenerateColumns="False"
HorizontalGridLinesBrush="DarkGray"
RowBackground="LightGray"
AlternatingRowBackground="White">
<DataGrid.Items>
<local:Phone
Title="iPhone 6S"
Company="Apple"
Price="54990" />
<local:Phone
Title="Lumia 950"
Company="Microsoft"
Price="39990" />
<local:Phone
Title="Nexus 5X"
Company="Google"
Price="29990" />
</DataGrid.Items>
<DataGrid.Columns>
<DataGridTextColumn
Header="Модель"
Binding="{Binding Path=Title}"
Width="90" />
<DataGridHyperlinkColumn
Header="Компания"
Binding="{Binding Path=Company}"
Width="80" />
<DataGridTextColumn
Header="Цена"
Binding="{Binding Path=Price}"
Width="50" />
</DataGrid.Columns>
<DataGrid.RowDetailsTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock
Text="{Binding Path=Price}" />
<TextBlock
Text=" рублей по скидке" />
</StackPanel>
</DataTemplate>
</DataGrid.RowDetailsTemplate>
</DataGrid>
Элемент Image
Элемент Image предназначен для работы с изображениями. Свойство Source позволяет задать путь к изображению, например:
<Image Source="myPhoto.jpg" />
WPF поддерживает различны форматы изображений: .bmp, .png, .gif, .jpg и т.д.
Также элемент позволяет проводить некоторые простейшие транформации с изображениями. Например, с помощью объекта FormatConvertedBitmap и его свойства DestinationFormat можно получить новое изображение:
<Grid Background="Black">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="2.5*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Image Grid.Column="0" x:Name="mainImage">
<Image.Source>
<FormatConvertedBitmap Source="3.jpg"
DestinationFormat="Gray32Float" />
</Image.Source>
</Image>
<StackPanel Grid.Column="1">
<Image Source="1.jpg" />
<Image Source="2.jpg" />
<Image Source="4.jpg" />
<Image Source="3.jpg" />
</StackPanel>
</Grid>
InkCanvas
InkCanvas представляет собой полотно, на котором можно рисовать. Первоначально оно предназначалось для стилуса, но в WPF есть поддержка также и для мыши для обычных ПК. Его очень просто использовать:
<InkCanvas Background="LightCyan" />
Либо мы можем вложить в InkCanvas какое-нибудь изображение и на нем уже рисовать:
<InkCanvas>
<Image
Source="2.jpg"
Width="300"
Height="250" />
</InkCanvas>
Все рисование в итоге представляется в виде штрихов - элементов класса System.Windows.Ink.Stroke и хранится в коллекции Strokes, определенной в классе InkCanvas.
Режим рисования
InkCanvas имеет несколько режимов, они задаются с помощью свойства EditingMode, значения для которого берутся из перечисления InkCanvasEditingMode.. Эти значения бывают следующими:
Используя эти значения и обрабатывая события InkCanvas, такие как StrokeCollected (штрих нарисован), StrokeErased (штрих стерли) и др., можно управлять набором штрихов и создавать более функциональные приложения на основе InkCanvas.
Привязка (Binding) | Содержание | Каркас приложения. Модель данных. Привязка данных. Табличный вывод.