WPFプログラミング備忘録

WPF DataGridのセルに色を付けよう

WPF

この記事ではDataGridのセルに任意の色を付ける方法の一つを説明しています。

今回はフォーカスを持っているセルを含む行の背景色を緑色にし、その他の行の背景色を灰色にしました。

XAMLファイルは以下のとおりです。

<Window x:Class="WpfDataGridSetCellColor.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:WpfDataGridSetCellColor"
        mc:Ignorable="d"
        Title="MainWindow" Height="180" Width="400" ContentRendered="Window_ContentRendered">
    <Window.Resources>
        <!-- PersonCollectionクラスのインスタンス生成 -->
        <local:PersonCollection x:Key="objPersonCollection" />
        
        <Style x:Key="CellColor" TargetType="DataGridCell">
            <Style.Triggers>
                <!-- 選択されていない行 -->
                <DataTrigger Binding="{Binding ColorFlag}" Value="0">
                    <Setter Property="Foreground" Value="Black"/>
                    <Setter Property="Background" Value="LightGray"/>
                </DataTrigger>
                <!-- 選択されている行 -->
                <DataTrigger Binding="{Binding ColorFlag}" Value="1">
                    <Setter Property="Foreground" Value="Black"/>
                    <Setter Property="Background" Value="GreenYellow"/>
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </Window.Resources>
    <Grid DataContext="{StaticResource objPersonCollection}">
        <DataGrid x:Name="MyDataGrid"
                  Grid.Row="0"
                  ItemsSource="{Binding Path=People}"
                  AutoGenerateColumns="False" 
                  CanUserSortColumns="False" 
                  CanUserAddRows="False" 
                  SelectionMode="Single" 
                  SelectionUnit="Cell"
                  CurrentCellChanged="MyDataGrid_CurrentCellChanged"
                  >
            <DataGrid.Columns>
                <DataGridTextColumn Header="姓" Binding="{Binding LastName}" Width="120" IsReadOnly="True" CanUserSort="False" CellStyle="{StaticResource CellColor}" />
                <DataGridTextColumn Header="名" Binding="{Binding FirstName}" Width="120" IsReadOnly="True" CanUserSort="False" CellStyle="{StaticResource CellColor}" />
                <DataGridTextColumn Header="電話番号" Binding="{Binding Tel}" Width="*" IsReadOnly="True" CanUserSort="False" CellStyle="{StaticResource CellColor}" />
            </DataGrid.Columns>
        </DataGrid>
    </Grid>
</Window>

ポイントはリソースセクションでスタイルを定義することです。以下は抜粋です。 ColorFlag プロパティの値によって色を付けわけています。

<Style x:Key="CellColor" TargetType="DataGridCell">
        <Style.Triggers>
            <!-- 選択されていない行 -->
            <DataTrigger Binding="{Binding ColorFlag}" Value="0">
                <Setter Property="Foreground" Value="Black"/>
                <Setter Property="Background" Value="LightGray"/>
            </DataTrigger>
            <!-- 選択されている行 -->
            <DataTrigger Binding="{Binding ColorFlag}" Value="1">
                <Setter Property="Foreground" Value="Black"/>
                <Setter Property="Background" Value="GreenYellow"/>
            </DataTrigger>
        </Style.Triggers>
</Style>

コードビハインドは以下のとおりです。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace WpfDataGridSetCellColor
{
    public class Person : INotifyPropertyChanged
    {
        private string _colorFlag;
        public string LastName { get; set; }
        public string FirstName { get; set; }
        public string Tel { get; set; }
        public int RecordNumber { get; set; }
        public string ColorFlag 
        {
            get { return _colorFlag; }
            set 
            { 
                if(value != _colorFlag)
                {
                    _colorFlag = value;
                    NotifyPropertyChanged();
                } 
            }
        }

        public static string OtherRowColor = "0";
        
        public static string CurrentRowColor = "1";
        public Person(string LastName, string FirstName, string Tel)
        {
            this.LastName = LastName;
            this.FirstName = FirstName;
            this.Tel = Tel;
        }

        public event PropertyChangedEventHandler PropertyChanged;

        private void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }

    public class PersonCollection
    {
        private List<Person> _people = new List<Person>();
        public List<Person> People
        {
            get { return _people; }
        }
        public PersonCollection()
        {
            _people.Add(new Person("山田", "太郎", "1111-1111"));
            _people.Add(new Person("鈴木", "花子", "2222-2222"));
            _people.Add(new Person("畠山", "銀次", "3333-3333"));
            _people.Add(new Person("青木", "健一", "4444-4444"));
            _people.Add(new Person("多田野", "章宏", "5555-5555"));

            // 各項目に 0 始まりの連番を持たせておく
            for(int i = 0; i < _people.Count; i++)
            {
                _people[i].RecordNumber = i;
            }
        }
    }
    /// <summary>
    /// MainWindow.xaml の相互作用ロジック
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void Window_ContentRendered(object sender, EventArgs e)
        {
            MoveCurrentCell(MyDataGrid, 0, 0);
        }

        // 選択されているセルの行番号を取得する
        private int GetCurrentRowNumber(DataGrid dg)
        {
            int row = 0;
            try
            {
                row = dg.Items.IndexOf(dg.CurrentItem);
            }
            catch
            {
                row = -1;
            }

            return row;
        }

        // 選択されているセルの列番号を取得する(今回は使用しない)
        private int GetCurrentColumnNumber(DataGrid dg)
        {
            int col = -1;
            DataGridCellInfo cellInfo = dg.CurrentCell;

            if (cellInfo.Column != null)
            {
                col = cellInfo.Column.DisplayIndex;
            }

            return col;
        }

        // カレントセルを移動する
        private void MoveCurrentCell(DataGrid dg, int row, int col)
        {
            dg.Focus();

            var cellInfo = new DataGridCellInfo(dg.Items[row], dg.Columns[col]);

            dg.CurrentCell = cellInfo;
        }

        private void MyDataGrid_CurrentCellChanged(object sender, EventArgs e)
        {
            int currentRowNumber = GetCurrentRowNumber(MyDataGrid);

            if (currentRowNumber < 0)
            {
                return;
            }

            // 行番号からDataGridRowオブジェクトを取得する
            DataGridRow dataGridRow = MyDataGrid.ItemContainerGenerator.ContainerFromIndex(currentRowNumber) as DataGridRow;

            if (dataGridRow == null)
            {
                return;
            }

            // 選択されている行を表すPersonオブジェクトを取得する
            Person person = dataGridRow.Item as Person;

            // リソースから PersonCollection オブジェクトを取得する
            var personCollection = this.TryFindResource("objPersonCollection");

            if(personCollection == null)
            {
                return;
            }

            var people = ((PersonCollection)personCollection).People;

            // ColorFlagを設定して色を付ける
            for (int row = 0; row < people.Count; row++)
            {
                if(row == person.RecordNumber)
                {
                    // 選択されている行の場合
                    people[row].ColorFlag = Person.CurrentRowColor;
                }
                else
                {
                    // 選択されていない行の場合
                    people[row].ColorFlag = Person.OtherRowColor;
                }
            }
        }
    }
}

セルが移動したときに呼ばれるCurrentCellChangedイベントハンドラ(MyDataGrid_CurrentCellChanged)の最後で ColorFlag の値を設定しています。以下は抜粋です。

for (int row = 0; row < people.Count; row++)
{
    if(row == person.RecordNumber)
    {
        // 選択されている行の場合
        people[row].ColorFlag = Person.CurrentRowColor;
    }
    else
    {
        // 選択されていない行の場合
        people[row].ColorFlag = Person.OtherRowColor;
    }
}

プログラム起動時の画面は以下のとおりです。左上のセルにフォーカスを移動しています。

行を移動すると選択されているセルを含む行の背景色が緑色になることがわかります。4行目に移動した時の画面は以下のとおりです。

なお、PersonクラスではColorFlagプロパティを更新した際にUIへ通知するためにINotifyPropertyChangedインターフェースを実装していますが、これが少し煩わしい時があります。別の方法として依存関係プロパティを使用することもできます。Personクラスの ColorFlagプロパティを以下のように依存関係プロパティにします。

public class Person : System.Windows.DependencyObject
{
    public string LastName { get; set; }
    public string FirstName { get; set; }
    public string Tel { get; set; }
    public int RecordNumber { get; set; }

    // 依存関係プロパティ
    public string ColorFlag
    {
        get { return (string)GetValue(ColorFlagProperty); }
        set { SetValue(ColorFlagProperty, value); }
    }

    public static readonly DependencyProperty ColorFlagProperty =
        DependencyProperty.Register("ColorFlag", typeof(string), typeof(Person), new PropertyMetadata("0"));

    public static string OtherRowColor = "0";
   
    public static string CurrentRowColor = "1";
        
    public Person(string LastName, string FirstName, string Tel)
    {
        this.LastName = LastName;
        this.FirstName = FirstName;
        this.Tel = Tel;
    }
}

動作はINotifyPropertyChangedインターフェースを実装した時と同じです。ただし、この方法では他のクラスを継承できなくなるので、あまりお勧めできません。

CLRプロパティと依存関係プロパティの通知メカニズムについては以下の記事をご確認ください。

>> WPF データバインディングとUIへの通知機能

今回はDataGridのセルに色を付ける方法の一つを検証しました。

関連記事

>> WPF DataGridのセルの内容を中央や右寄せで表示と編集をする

>> WPF DataGridの行を取得する

>> WPF DataGridの行を入れ替える

>> WPF DataGridの行をフィルタリングする

>> WPF DataContext プロパティの特徴について

以上です。

コメント