Столбцы DataGrid

82

За счет применения автоматически сгенерированных столбцов можно быстро получить элемент управления DataGrid, отображающий все необходимые данные. Однако при таком подходе теряется значительная степень контроля. Например, невозможно управлять тем, как столбцы должны упорядочиваться, насколько широкими они должны быть, как должны форматироваться значения внутри них и какой текст заголовка должен размещаться сверху.

Гораздо более мощный подход предусматривает отключение функции автоматической генерации столбцов за счет установки свойства AutoGenerateColumns в false. Это позволит явно определить все необходимые столбцы и указать для них любые желаемые параметры и порядок отображения. Потребуется всего лишь заполнить коллекцию DataGrid.Columns правильными объектами столбцов.

В настоящее время DataGrid поддерживает множество типов столбцов, которые представлены разными классами, унаследованными от одного общего класса DataGridColumn:

DataGridTextColumn

Этот столбец является стандартным выбором для большинства типов данных. Его значение преобразуется в текст и отображается в элементе управления TextBox. При редактировании строки этот элемент TextBlock заменяется стандартным текстовыми полем.

DataGridCheckBoxColumn

Этот столбец отображает флажок и чаще всего автоматически используется для булевских (или булевских, допускающих null) значений. Как правило, флажок доступен только для чтения, но при редактировании строки он превращается в обычный флажок.

DataGridHyperlinkColumn

Этот столбец отображает ссылку, на которой можно щелкать. В случае применения в сочетании навигационными контейнерами WPF, таким как Frame или NavigationWindow, позволяет переходить по другому URI (обычно на внешний веб-сайт).

DataGridComboBox

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

DataGridTemplateColumn

Этот столбец пока что является самым мощным вариантом. Он позволяет определять шаблон данных для отображения значений столбцов, с той же степенью гибкости и возможностями, которые имеются при использовании шаблонов в списковом элементе управления. Его можно применять, например, для вывода данных изображения или для использования специализированного элемента WPF (наподобие раскрывающегося списка с допустимыми значениями или элемента DatePicker для значений даты).

Ниже показан пример переделанного элемента управления DataGrid, который создает два столбца с названиями товаров и ценами. В нем также применяются более понятные заголовки для столбцов, а столбец Product расширен так, чтобы умещать предназначенные для него данные:

<DataGrid Name="gridProducts" AutoGenerateColumns="False">
        <DataGrid.Columns>
               <DataGridTextColumn Header="Продукт" Width="175" 
                             Binding="{Binding Path=ModelName}"></DataGridTextColumn> 
               <DataGridTextColumn Header="Цена" 
                             Binding="{Binding Path=UnitCost,StringFormat={}{0:C}}"></DataGridTextColumn>
        </DataGrid.Columns>
 </DataGrid>

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

Для установки свойства DataGridColumn.Header обычно используется простая строка, но это не обязательно. Заголовок столбца действует как элемент управления содержимым, и потому для свойства Header может быть указан любой элемент, в том числе изображение или панель компоновки с комбинацией элементов.

Свойство DataGridColumn.Width поддерживает жестко закодированные значения и несколько режимов автоматической настройки размеров, в точности как и свойство DataGrid.ColumnWidth, рассмотренное в предыдущей статье. Единственное отличие состоит в том, что свойство DataGridColumn.Width применяется к единственному столбцу, а свойство DataGrid.ColumnWidth задает значение по умолчанию для всей сетки. Значение свойства DataGridColumn.Width переопределяет значение свойства DataGrid.ColumnWidth.

Выражение привязки является самой важной деталью, поскольку отвечает за предоставление корректной информации. Оно устанавливается в свойстве DataGridColumn.Binding. Этот подход отличается от применяемого в простых списковых элементах управления вроде ListBox и ComboBox. В них для данной цели служит свойство DisplayMemberPath, а не Binding. Подход со свойством Binding является более гибким, поскольку позволяет применять форматирование строк и конвертеры значений без перехода на использование полнофункционального шаблонного столбца.

Класс DataGridCheckBoxColumn

Класс Product не включает никаких булевских свойств, но если бы имел, то для них подошел бы класс DataGridCheckBoxColumn.

Как и в DataGridTextColumn, в классе DataGridCheckBoxColumn для извлечения данных применяется свойство Binding. Здесь в роли данных выступает значение true или false, которое применяется для установки свойства IsChecked размещающегося внутри элемента CheckBox. Класс DataGridCheckBoxColumn имеет свойство по имени Content, которое позволяет отобразить рядом с флажком дополнительное содержимое, и свойство IsThreeState, которое позволяет указать, должен ли флажок поддерживать неопределенное состояние вдобавок к отмеченному и неотмеченному состоянию.

Если DataGridCheckBoxColumn используется для отображения информации из булевского значения, допускающего null, свойство IsThreeState имеет смысл установить в true. Это даст возможность щелчком переводить флажок в неопределенное состояние (при котором он отображается в слегка затененном виде), устанавливая привязанное значение в null.

Класс DataGridHyperlinkColumn

Класс DataGridHyperlinkColumn позволяет отображать текстовые значения, каждое из которых содержит один URL-адрес. Например, если в классе Product есть свойство по имени ProductLink со значением вроде http://professorweb.ru, можно отобразить эту информацию в DataGridHyperlinkColumn. Каждое привязанное значение тогда будет отображаться с использованием элемента Hyperlink следующим образом:

<Hyperlink NavigateUri=" http://professorweb.ru"> http://professorweb.ru</Hyperlink>

Пользователь получает возможность щелкать на гиперссылке и переходить на соответствующую страницу, при этом никакого специального кода писать не понадобилось. Однако здесь имеется одно существенное ограничение: такой прием с автоматической навигацией работает только в случае размещения объекта DataGrid в контейнере, который поддерживает события навигации, как, например, Frame или NavigationWindow.

Если нужен более универсальный способ для достижения похожего эффекта, следует воспользоваться классом DataGridTemplateColumn. С его помощью можно отобразить подчеркнутый текст с возможностью щелчка на нем (в действительности можно даже применить элемент управления Hyperlink) и обрабатывать эти события щелчка в коде.

Обычно в DataGridHyperlinkColumn применяется один и тот же фрагмент информации для навигации и для отображения. Однако при желании их можно указать по отдельности. Для этого в свойстве Binding понадобится установить URL-адрес, а необязательное свойство ContentBinding использовать для извлечения отображаемого текста из какого-то другого свойства привязанного объекта данных.

Класс DataGridComboBoxColumn

Класс DataGridComboBoxColumn позволяет отображать обычный текст, но предоставляет более совершенное поведение в режиме редактирования — пользователь получает возможность выбирать желаемый вариант из списка доступных опций в элементе управления ComboBox. (Фактически, выбор ограничивается списком значений, поскольку непосредственный ввод текста в ComboBox не разрешен.) Ниже показан пример выбора пользователем категории товара в DataGridComboBoxColumn:

Использование DataGridComboBoxColumn

При использовании DataGridComboBoxColumn понадобится решить, каким образом заполнять элемент ComboBox в режиме редактирования. Самым простым подходом является его заполнение вручную, в разметке. Например, вот как добавить в ComboBox список строк:

xmlns:col="clr-namespace:System.Collections;assembly=mscorlib"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
...
<DataGridComboBoxColumn Header="Категория" SelectedItemBinding="{Binding Path=CategoryName}">
        <DataGridComboBoxColumn.ItemsSource>
                  <col:ArrayList>
                                <sys:String>General</sys:String>
                                <sys:String>Communications</sys:String>
                                <sys:String>Deception</sys:String>
                                <sys:String>Munitions</sys:String>
                                <sys:String>Protection</sys:String>
                                <sys:String>Tools</sys:String>
                                <sys:String>Travel</sys:String>
                  </col:ArrayList>
         </DataGridComboBoxColumn.ItemsSource>
</DataGridComboBoxColumn></pre></code>

Это проектное решение вполне нормально работает, но наилучшим не является, поскольку детали данных в нем скрыты глубоко внутри разметки пользовательского интерфейса. К счастью, на выбор доступно несколько других вариантов:

  • Извлечение коллекции данных из ресурса. Эта коллекция может определяться в разметке (как в предыдущем примере) либо генерироваться в коде (как будет показано далее).

  • Извлечение коллекции ItemsSource из статического метода с использованием расширения разметки Static. Однако цельное проектное решение предусматривает вызов этого метода в классе окна, а не в классе данных.

  • Извлечение коллекции данных из ресурса ObjectProvider, который затем может вызвать класс доступа к данным.

  • Установка свойства DataGridComboBox.Column непосредственно в коде.

Во многих ситуациях, значения, отображаемые в списке, не совпадают со значениями, которые должны быть сохранены в объекте данных. Типичным случаем является работа со связанными данными (например, заказами, которые связываются с товарами, записями об оплате, которые связываются с заказчиками, и т.д.).