понедельник, 21 марта 2011 г.

WPF DataGrid и раскраска (выделение цветом) ячеек и строк (HOWTO)

Иногда возникает необходимость выделить цветом какие-нибудь данные в DataGrid’е. Давайте рассмотрим, как это можно сделать в WPF.
Для примера создадим класс Person
public class Person
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int Age { get; set; }
}

Пишем небольшую заполнялку тестовых данных:
private List<Person> GenerateSampleData()
{
    Random r = new Random();
    List<Person> personList = new List<Person>();
    for (int i = 1; i <= 100; i++)
    {
        personList.Add(new Person { Id = i, Name = String.Format("Person Name {0}", i), Age = r.Next(60) });
    }
    return personList;
}

Кидаем на поверхность окна DataGrid и в конструкторе формы заполняем данными:
this.dataGrid1.ItemsSource = GenerateSampleData();

И получаем вот такую милую/унылую таблицу.


Допустим, у нас стоит задача выделить цветом те “персоны”, возраст которых не достиг 16 лет.


1.1. В определение окна добавляем ссылку на пространство имен текущего проекта

xmlns:local="clr-namespace:DataGridColor"
 

1.2. Создаем конвертер, который будет преобразовывать возраст в кисть, которой будем закрашивать фон
 
class AgeToColorConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        // Все проверки для краткости выкинул
        return (int)value <= 16 ? 
            new SolidColorBrush(Colors.OrangeRed)
            : new SolidColorBrush(Colors.White);
    }
    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new Exception("The method or operation is not implemented.");
    }
}


1.3. В ресурсах окна определяем ключ конвертера
<local:AgeToColorConverter x:key="AgeToColorConverter">

1.4. И там же, в ресурсах окна, объявляем стиль, TargetType у которого будет DataGridRow.

<Style TargetType="{x:Type DataGridRow}">
    <Setter Property="Background" Value="{Binding Age, Converter={StaticResource AgeToColorConverter}}" />
</Style>

В результате получаем вот такую полосатую таблицу:



А что если нужно выделять цветом не всю строку, а только отдельные ячейки в колонке. Для примера, нужно выделить ячейки с идентификатором меньше либо равно 16.

Эта задача решается похожим способом.

2.1. Добавляем в проект еще один конвертер
class IdToColorConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        // Все проверки для краткости выкинул
        return (int)value <= 16 ?
            new SolidColorBrush(Colors.Yellow)
            : new SolidColorBrush(Colors.Green);
    }
    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new Exception("The method or operation is not implemented.");
    }
}

2.2 Присваивам ключ
<local:IdToColorConverter x:Key="IdToColorConverter" />

2.3. Добавляем в ресурсы окна еще один стиль, TargetType’ом у которого является  DataGridCell. Так же стилю присваиваем ключ посредством указания x:Key="IdStyle", иначе стиль будет применен для всех ячеек во всех таблицах (а ведь нам этого не нужно).
<Style x:Key="IdStyle" TargetType="{x:Type DataGridCell}">
<Setter Property="Background" Value="{Binding Id, Converter={StaticResource IdToColorConverter}}" />
</Style> 


2.4. И для колонки, в которой хотим использовать раскраску отличную от всей строки указываем CellStyle:

<DataGridTextColumn Header="ID" Width="50" 
                    Binding="{Binding Id}" 
                    CellStyle="{StaticResource IdStyle}"/>


2.5. Если раздражает, что цвет Border’a ячейки теперь берется из цвета Background’а строки,  модифицируем стиль для ячейки устанавливая закраску Border’a белым цветом
<Style x:Key="IdStyle" TargetType="{x:Type DataGridCell}">
    <Setter Property="Background" Value="{Binding Id, Converter={StaticResource IdToColorConverter}}" />
    <Setter Property="BorderBrush"  Value="White" />
</Style>






Собственно, чтобы привлечь внимание к определенной ячейке не обязательно делать из DataGrid’а приведенного попугая, достаточно подкрашивать Border присваивая BorderBrush’у акцентирующую кисть.

Если есть вопросы – задавайте, будем разбираться вместе.

UPDATE: Если нужно раскрашивать строчки в зависимости от значений нескольких полей в таблице, то следует использовать MultiBinding и реализовывать интерфейс IMultiValueConverter. Для примера реализован вариант запрошенный io, когда нужно выделить строку, если идентификатор меньше 10 или возраст меньше 16.
Пример использования MultiBinding'а:
<Style TargetType="{x:Type DataGridRow}">
  <Setter Property="Background">
    <Setter.Value>
      <MultiBinding Converter="{StaticResource MultiBindingConverter}">
        <Binding Path="Id"/>
        <Binding Path="Age"/>
      </MultiBinding>
    </Setter.Value>
  </Setter>
</Style>

Пример реализации IMultiValueConverter'а:
    class MultiBindingConverter:IMultiValueConverter

    {
        public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            return ((int)values[0] <10 || (int)values[1]<16) ? 
                new SolidColorBrush(Colors.OrangeRed)
                : new SolidColorBrush(Colors.White);
        }

        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }

PS. Выложил исходники.

6 комментариев:

  1. xmlns:local="clr-namespace:DataGridColor"
    ??? где его взять ?

    ОтветитьУдалить
  2. DataGridColor - это имя проекта.

    ОтветитьУдалить
  3. спасибо большое) очень помогли

    ОтветитьУдалить
  4. А что если красить нужно по двум колонкам. Допустим, надо закрасить если возраст <=16 или (к примеру) ID <10 (закрасить при выполнении одного из условий)

    ОтветитьУдалить
  5. ссылку на исходники дайте пожалуйста

    ОтветитьУдалить
  6. Спасибо огромное за статью, очень помогло.

    ОтветитьУдалить