Uploaded by Для Комэрции

Тема 3.9 TreeView и DataGrid.

advertisement
Тема 3.9. TreeView и DataGrid
Оглавление
TreeView ....................................................................................................................................................... 1
Пример 1: ................................................................................................................................................. 1
Пример 2: ................................................................................................................................................. 2
Пример 3: Динамическое добавление узлов в дерево при разворачивании узла. .......................... 3
Пример 4: DockPanel в узле TreeView .................................................................................................... 7
DataGrid ........................................................................................................................................................ 7
Пример 5: ................................................................................................................................................. 7
Пример 6: ................................................................................................................................................. 8
Некоторые полезные свойства DataGrid ............................................................................................... 9
Пример 7: ...............................................................................................................................................10
TreeView
Данный элемент управления предназначен для древовидного отображения данных в
окне приложения. Может содержать как коллекцию элементов TreeViewItem, так и
другое содержимое, например, текстовые блоки:
Пример 1:
<TreeView>
<TextBox>Элемент TreeView</TextBox>
<TreeViewItem Header="Базы данных">
<TreeViewItem Header="MS SQL Server" />
<TreeViewItem Header="MySQL" />
<TreeViewItem Header="MongoDB" />
<TreeViewItem Header="Postgres" />
</TreeViewItem>
<TreeViewItem Header="Языки программирования">
<TreeViewItem Header="C-языки">
<TreeViewItem Header="C#" />
<TreeViewItem Header="C/C++" />
<TreeViewItem Header="Java" />
</TreeViewItem>
<TreeViewItem Header="Basic">
<TreeViewItem Header="Visual Basic" />
<TreeViewItem Header="VB.Net" />
<TreeViewItem Header="PureBasic" />
</TreeViewItem>
</TreeViewItem>
</TreeView>
Однако все же лучше обертывать элементы в объекты TreeViewItem. С помощью его
свойства Header мы можем установить текстовую метку или заголовок узла дерева.
Элемент
TreeViewItem
предлагает
также
ряд
свойств
для
управления
состоянием: IsExpanded (принимает логическое значение и показывает, раскрыт ли
узел) и IsSelected (показывает, выбран ли узел).
Чтобы отследить выбор или раскрытие узла, мы можем обработать соответствующие
события. Событие Expanded возникает при раскрытии узла, а событие Collapsed,
наоборот, при его сворачивании.
Выбор узла дерева мы можем обработать с помощью обработки события Selected.
Пример 2:
<TreeView>
<TreeViewItem Header="C-языки" Expanded="TreeViewItem_Expanded">
<TreeViewItem Header="C#" Selected="TreeViewItem_Selected" />
<TreeViewItem Header="C/C++" Selected="TreeViewItem_Selected" />
<TreeViewItem Header="Java" Selected="TreeViewItem_Selected" />
</TreeViewItem>
</TreeView>
Теперь добавим в файл связанного кода C# обработчики для этих событий:
private void TreeViewItem_Expanded(object sender, RoutedEventArgs e)
{
TreeViewItem tvItem = (TreeViewItem)sender;
MessageBox.Show("Узел " + tvItem.Header.ToString() + " раскрыт");
}
private void TreeViewItem_Selected(object sender, RoutedEventArgs e)
{
TreeViewItem tvItem = (TreeViewItem)sender;
MessageBox.Show("Выбран узел: " + tvItem.Header.ToString());
}
Элементы управления TreeView часто применяются для размещения больших
объемов данных. Объясняется это тем, что TreeView обладает сворачиваеморазворачиваемой структурой. Даже в случае прокрутки TreeView пользователем
сверху донизу, видимой не обязательно будет вся доступная в нем информация.
Информация, не являющаяся видимой, может вообще пропускаться в элементе
управления TreeView, сокращая накладные расходы (и время, необходимое для
его заполнения). Даже еще лучше то, что при открытии элемента TreeViewItem
инициируется событие Expanded, а при закрытии — событие Collapsed. Этот
момент очень удобно использовать для добавления недостающих узлов или
удаления тех, что уже больше не нужны. Такой подход называется
оперативным (just-in-time) созданием узлов.
Оперативное создание узлов может применяться в приложениях, в которых
данные извлекаются из базы, но классическим примером, несомненно, является
приложение, предназначенное для просмотра каталогов. В настоящее время
большинство жестких дисков имеют огромную емкость и постоянно
разрастающуюся структуру каталогов.
Хотя элемент управления TreeView и можно заполнить структурой каталогов
жесткого диска, этот процесс является удручающе медленным. Гораздо лучше,
когда сначала отображается частично свернутое представление, и пользователю
предлагается самостоятельно добираться до конкретных каталогов. При
разворачивании каждого узла в дерево добавляются соответствующие
подкаталоги, и протекает этот процесс практически мгновенно.
Пример 3: Динамическое
разворачивании узла.
добавление
узлов
в
дерево
при
Сначала добавляется в TreeView список дисков при первой загрузке окна.
Изначально узел для каждого диска представляется в свернутом виде. Буква
диска отображается в заголовке, а объект DriveInfo хранится в свойстве
TreeViewItem.Tag для упрощения поиска вложенных каталогов в дальнейшем без
воссоздания этого объекта. (Это увеличивает накладные расходы приложения,
связанные с памятью, но при этом сокращает количество проверок безопасности
доступа к файлам. Общий эффект является незначительным, но зато немного
улучшает производительность и упрощает код.) Ниже приведен код, в котором
TreeView заполняется списком дисков с помощью класса System.IO.DriveInfo:
Данный код добавляет под узлом каждого диска указатель места заполнения
(строку со звездочкой). Этот указатель не отображается, поскольку узел сначала
находится в свернутом состоянии. При разворачивании узла этот указатель
можно удалить и добавить на его месте список подкаталогов.
Указатель места заполнения представляет собой удобный инструмент,
который можно использовать для определения того, развернул ли уже
пользователь данную папку для просмотра ее содержимого. Однако его главной
обязанностью является отображение рядом с каждым элементом специального
значка, указывающего на возможность разворачивания. Без него пользователь
просто не сможет разворачивать каталог и отображать содержащиеся в нем
подпапки. Если в каталоге нет никаких подпапок, этот значок будет просто
исчезать при попытке пользователя развернуть его, что похоже на поведение
проводника Windows при просмотре сетевых папок.
Для реализации оперативного создания узлов необходимо обрабатывать
событие TreeViewItem.Expanded. Поскольку это событие поддерживает
пузырьковое распространение, обработчик событий можно присоединять прямо
к элементу TreeView, чтобы он обрабатывал событие Expanded любого
находящегося внутри него элемента TreeViewItem.
Код Xaml
Обработчик события раскрытия узла (разместить после public MainWindow() , полн
ый код приложения смотрите ниже в приложении)
Пример 4: DockPanel в узле TreeView
<TreeView>
<TreeViewItem Header="Animals">
<TreeViewItem.Items>
<DockPanel>
<Image Source="data\fish.png"/>
<TextBlock Margin="5" Foreground="Brown"
FontSize="12">Fish</TextBlock>
</DockPanel>
<DockPanel>
<Image Source="data\dog.png"/>
<TextBlock Margin="5" Foreground="Brown"
FontSize="12">Dog</TextBlock>
</DockPanel>
<DockPanel>
<Image Source="data\cat.png"/>
<TextBlock Margin="5" Foreground="Brown"
FontSize="12">Cat</TextBlock>
</DockPanel>
</TreeViewItem.Items>
</TreeViewItem>
</TreeView>
DataGrid
DataGrid во многом похож на ListView, но более сложный по характеру и
допускает редактирование содержимого таблицы.
Пример 5:
Создадим класс 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="WpfApp5.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:WpfApp5"
xmlns:col="clr-namespace:System.Collections;assembly=mscorlib"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<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"). И это даст в итоге следующий вывод:
Пример 6:
Программная установка источника для 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;
При этом в xaml останется только
<DataGrid x:Name="phonesGrid" AutoGenerateColumns="True">
</DataGrid>
Некоторые полезные свойства 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
Позволяет задать специфичный шаблон для отображения
столбца
Пример 7:
Перепишем предыдущий пример с учетом новой информации:
<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>
При щелчке по строке появляется дополнительная строка:
Приложение: Код раскрытия узла списка из примера 3
using
using
using
using
using
using
using
using
using
using
using
using
using
using
using
System;
System.Collections.Generic;
System.Linq;
System.Text;
System.Threading.Tasks;
System.Windows;
System.Windows.Controls;
System.Windows.Data;
System.Windows.Documents;
System.Windows.Input;
System.Windows.Media;
System.Windows.Media.Imaging;
System.Windows.Navigation;
System.Windows.Shapes;
System.IO;
namespace WpfApp6
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
foreach (DriveInfo drive in DriveInfo.GetDrives())
{
TreeViewItem item = new TreeViewItem();
item.Tag = drive;
item.Header = drive.ToString();
item.Items.Add("*");
trw_Products.Items.Add(item);
}
}
private void trw_Products_Expanded(object sender, RoutedEventArgs e)
{
TreeViewItem item = (TreeViewItem)e.OriginalSource;
item.Items.Clear();
DirectoryInfo dir;
if (item.Tag is DriveInfo)
{
DriveInfo drive = (DriveInfo)item.Tag;
dir = drive.RootDirectory;
}
else dir = (DirectoryInfo)item.Tag;
try
{
foreach (DirectoryInfo subDir in dir.GetDirectories())
{
TreeViewItem newItem = new TreeViewItem();
newItem.Tag = subDir;
newItem.Header = subDir.ToString();
newItem.Items.Add("*");
item.Items.Add(newItem);
}
}
catch
{ }
}
}
}
Download