使ってるもの:Visual Studio 2008 std + WPF ToolkitのDataGrid
何をしたいかって、属性をプロパティにつけることで、DataGridの列の自動生成の動作をカスタマイズしたいってことです。
さくっとやってみます。
下準備
DataGridAutoGenerateCustomSampleという名前でプロジェクトを新規作成してWPF Toolkitを参照に加えます。
そしてWindow1.xamlを以下のようにしてDataGridをぽちっと置きます。
<Window x:Class="DataGridAutoGenerateCustomSample.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:dg="http://schemas.microsoft.com/wpf/2008/toolkit"
Title="Window1" Height="300" Width="300">
<Grid>
<dg:DataGrid ItemsSource="{Binding}" />
</Grid>
</Window>
いつものPersonクラスを作成します。
namespace DataGridAutoGenerateCustomSample
{
public class Person
{
public int ID { get; set; }
public string Name { get; set; }
public string Address { get; set; }
public string Temp { get; set; }
}
}
そして、これをDataContextに入れます。
using System.Linq;
using System.Windows;
namespace DataGridAutoGenerateCustomSample
{
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
// ダミーデータ作成
DataContext = Enumerable.Range(1, 100).Select(i =>
new Person
{
ID = i,
Name = "田中 太郎" + i,
Address = "東京都のどこか" + i,
Temp = "おまけ"
}).ToList();
}
}
}
ここまでで実行すると以下のような画面が表示されます。
以上で下準備完了です。
添付動作の作成
DataGridの自動生成を有効にするという機能を持った添付プロパティを作成します。
添付プロパティの値がtrueの時に、列が自動生成されるときのイベントを登録するようなつくりにする。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using Microsoft.Windows.Controls;
using System.Diagnostics;
namespace DataGridAutoGenerateCustomSample
{
public class GenerateAttributeColumn
{
public static bool GetEnable(DependencyObject obj)
{
return (bool)obj.GetValue(EnableProperty);
}
public static void SetEnable(DependencyObject obj, bool value)
{
obj.SetValue(EnableProperty, value);
}
public static readonly DependencyProperty EnableProperty =
DependencyProperty.RegisterAttached(
"Enable",
typeof(bool),
typeof(GenerateAttributeColumn),
new UIPropertyMetadata(false,
EnablePropertyChanged));
private static void EnablePropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
var dataGrid = sender as DataGrid;
// DataGridじゃなきゃ何もしない
if (dataGrid == null) return;
var enable = (bool) e.NewValue;
dataGrid.AutoGeneratingColumn -= DataGridAutoGeneratingColumn;
if (enable)
{
dataGrid.AutoGeneratingColumn += DataGridAutoGeneratingColumn;
}
}
private static void DataGridAutoGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs e)
{
Debug.WriteLine("カラム作成処理を記述する: " + e.Column);
}
}
}
そして、この添付プロパティをDataGridに設定します。
<Window x:Class="DataGridAutoGenerateCustomSample.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:dg="http://schemas.microsoft.com/wpf/2008/toolkit"
xmlns:local="clr-namespace:DataGridAutoGenerateCustomSample"
Title="Window1" Height="300" Width="300">
<Grid>
<dg:DataGrid ItemsSource="{Binding}" local:GenerateAttributeColumn.Enable="True"/>
</Grid>
</Window>
これを実行すると、見た目は変わらないけど、デバッグログに以下の出力がされます。
後は、カラム作成時の処理を書いてしまえばOK。
カラム作成処理を記述する: Microsoft.Windows.Controls.DataGridTextColumn
カラム作成処理を記述する: Microsoft.Windows.Controls.DataGridTextColumn
カラム作成処理を記述する: Microsoft.Windows.Controls.DataGridTextColumn
カラム作成処理を記述する: Microsoft.Windows.Controls.DataGridTextColumn
とりあえず、行ヘッダーのテキストと、非表示列の設定が出来る注釈を作成します。
using System;
namespace DataGridAutoGenerateCustomSample
{
// DataGridの行ヘッダに表示されるテキストを設定する
public class HeaderAttribute : Attribute
{
public string Text { get; private set; }
public HeaderAttribute(string text)
{
this.Text = text;
}
}
// 指定したプロパティを表示しないようにする
public class HideAttribute : Attribute
{
}
}
これをPersonクラスにつけます。
namespace DataGridAutoGenerateCustomSample
{
public class Person
{
// IDはそのまま
public int ID { get; set; }
// Nameは名前
[Header("名前")]
public string Name { get; set; }
// Addressは住所
[Header("住所")]
public string Address { get; set; }
// Tempは表示しない
[Hide]
public string Temp { get; set; }
}
}
後は、列生成のイベントでアノテーションを読み取って列をカスタマイズする処理を書きます。
private static void DataGridAutoGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs e)
{
var dataGrid = sender as DataGrid;
var desc = e.PropertyDescriptor as PropertyDescriptor;
// 重要な変数がキャストできなかったら処理をしない
if (dataGrid == null || desc == null) return;
// HideAttributeがある場合は列を作らない
var hide = desc.Attributes[typeof(HideAttribute)];
if (hide != null)
{
e.Cancel = true;
return;
}
// HeaderAttributeが指定されている場合は、ヘッダーのテキストを変える
var header = desc.Attributes[typeof(HeaderAttribute)] as HeaderAttribute;
if (header != null)
{
e.Column.Header = header.Text;
}
}
以上で完成です。
実行するとヘッダー行のテキストが変わるのと、Tempプロパティが表示されなくなります。
もうちょっとがんばって作りこめばTemplateに対応させたり、列をDataGridTextColumn以外にしたりとか夢広がりそうです。