Silverlight のデータグリッドは便利です。
ItemsSourceに値のリストを設定するだけで、ゴニョゴニョできるんですから。
表示まわりでやることなんてそんなに変わらないし、
( TemplateColumn使うなら話は別ですが )
Columnの定義をその都度書くのは正直めんどくさい。
設定ファイルでも外にだして、いやっふーってしたい。
そんな昼下がりです。
で、作ってみました。
結構長いんで、アップしてもよくわからなくなりそうなので、
処理の抜粋を記述しておきます。
【 定義 】
private void DataGridAddColumns
(
DataGrid targetDataGrid ,
String HeaderTitle ,
String ColumnType ,
Boolean CanUserSort ,
Boolean IsReadOnry ,
String BindingPath
)
{
....
}
引数のColumnTypeに従い、対象となるカラムのインスタンスを生成します。
Type columnType = WindowsControlsData.GetType( "System.Windows.Controls.DataGrid" + ColumnType + "Column"); DataGridColumn column = ( DataGridColumn )Activator.CreateInstance( columnType );
ちょろっとしたのを設定します。
column.Header = HeaderTitle; column.CanUserSort = CanUserSort; column.IsReadOnly = IsReadOnry;
バインドしないことには始まらないので、バインドします。
ただし、この際は、ColumnTypeがTemplateColumnになっていない事。
Binding Binding = new Binding();
Binding.Path = new PropertyPath(BindingPath);
PropertyArray.Add( BindingPath );
// テキスト、チェックボックスはDataGridBoundColumnを継承しているので、
// DataGridBoundColumnでキャストしてあげます。
( ( DataGridBoundColumn )column ).Binding = Binding;
あとは、カラムと追加します。
// カラムを追加 targetDataGrid.Columns.Add( column );
はい、できあがりー。
こんなのじゃ対して面白く無いですね。
多段のヘッダーを動的に作ってみましょうか。
まず、以下のクラスを定義してみます。
/// <summary>ヘッダー行情報</summary>
public class HeaderLine
{
/// <summary>
/// Line番号をもとに比較を行います
/// </summary>
static public int ComparisonByLineNo( HeaderLine x, HeaderLine y)
{
if (x.intLineNo > y.intLineNo)
{
return 1;
}
else if (x.intLineNo < y.intLineNo)
{
return -1;
}
else
{
return 0;
}
}
/// <summary>ヘッダー高さ</summary>
public int Height { get; set; }
/// <summary>行番号</summary>
public int intLineNo { get; set; }
/// <summary>表示文字列</summary>
public String strTitle { get; set; }
/// <summary>ソート矢印表示有無</summary>
public Boolean bolSortPos { get; set; }
/// <summary>行のテンプレート</summary>
public String strTitleTemplate { get; set; }
}
このクラスに、ヘッダーのスタイルを設定する値を格納していきます。
#region スタイルに付与するXMLNS /// <summary>スタイルに付与するXMLNS</summary> readonly string XMLNS = " xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation\ "" + " xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml\ "" + " xmlns:controls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls"" + " xmlns:data="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data"" + " xmlns:dataprimitives="clr-namespace:System.Windows.Controls.Primitives;assembly=System.Windows.Controls.Data"" + " xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation" "; #endregion /// <summary>カラムのスタイルを生成します</summary> /// <param name="lstHeaderLine">生成対象データ</param> /// <returns>生成スタイル</returns> private Style cleateStyle(List<HeaderLine> lstHeaderLine) { if (lstHeaderLine.Count == 0) { return null; } lstHeaderLine.Sort(HeaderLine.ComparisonByLineNo); #region データの生成 String strGridRowDefinitions = ""; String strContentPresenters = ""; int SortPos = -1; int ColumnSpan = 4; for (int i = 0; i < lstHeaderLine.Count; i++) { HeaderLine headerLine = lstHeaderLine.ElementAt( i ); strContentPresenters += "<TextBlock Margin="2.5,0,0,0" " + "Grid.Row=""+(headerLine.intLineNo - 1).ToString() + "" " + "Text=""+ headerLine.strTitle + "" "; if (!String.IsNullOrEmpty(headerLine.strTitleTemplate)) { strContentPresenters += "Style="{StaticResource " + headerLine.strTitleTemplate + " }" "; } strContentPresenters += "/>"; strGridRowDefinitions += "<RowDefinition Height="" + headerLine.Height.ToString() + "" />"; if( headerLine.bolSortPos && SortPos == -1 ) { SortPos = i; ColumnSpan = 3; } } #endregion String strStyleXML = ""; strStyleXML = "<?xml version="1.0" ?>" + "<Style " + XMLNS + " " + "TargetType="dataprimitives:DataGridColumnHeader" >" + //"<Setter Property="Foreground" Value="#FFFF0000"/>" + #region 設定
"<Setter Property="Background" Value="#FFFFFFFF"/>" + "<Setter Property="HorizontalContentAlignment" Value="Left"/>" + "<Setter Property="VerticalContentAlignment" Value="Center"/>" + "<Setter Property="IsTabStop" Value="False"/>" + "<Setter Property="SeparatorBrush" Value="#FFC9CACA"/>" + "<Setter Property="Padding" Value="8"/>" + #endregion #region テンプレート
"<Setter Property="Template">" + "<Setter.Value>" + "<ControlTemplate TargetType="dataprimitives:DataGridColumnHeader">" + "<Grid>" + "<Grid.RowDefinitions>" + "<RowDefinition Height = "5"/>" + "<RowDefinition />" + "<RowDefinition Height = "5"/>" + "</Grid.RowDefinitions>"; if( SortPos == -1 ) { strStyleXML += "<Grid.ColumnDefinitions>" + "<ColumnDefinition/>" + "<ColumnDefinition Width="Auto"/>" + "<ColumnDefinition Width="15"/>" + "<ColumnDefinition Width="5"/>" + "</Grid.ColumnDefinitions>"; } else { strStyleXML += "<Grid.ColumnDefinitions>" + "<ColumnDefinition/>" + "<ColumnDefinition Width="Auto"/>" + "<ColumnDefinition Width="5"/>" + "</Grid.ColumnDefinitions>"; } #region VisualStateManager strStyleXML += "<VisualStateManager.VisualStateGroups>" + "<VisualStateGroup x:Name="CommonStates">" + "<VisualState x:Name="Normal"/>" + #region <VisualState x:Name="MouseOver"/> "<VisualState x:Name="MouseOver">" + "<Storyboard>" + "<ColorAnimation Duration="0" " + "Storyboard.TargetName="BackgroundRectangle" " + "Storyboard.TargetProperty="(Fill).Color" To="#FF448DCA"/>" + "<ColorAnimation Duration="0" " + "Storyboard.TargetName="BackgroundGradient" " + "Storyboard.TargetProperty="(Fill).(GradientStops)[3].Color" To="#7FFFFFFF"/>" + "<ColorAnimation Duration="0" " + "Storyboard.TargetName="BackgroundGradient" " + "Storyboard.TargetProperty="(Fill).(GradientStops)[2].Color" To="#CCFFFFFF"/>" + "<ColorAnimation Duration="0" " + "Storyboard.TargetName="BackgroundGradient" " + "Storyboard.TargetProperty="(Fill).(GradientStops)[1].Color" To="#F2FFFFFF"/>" + "</Storyboard>" + "</VisualState>" + #endregion #region <VisualState x:Name="Pressed"/> "<VisualState x:Name="Pressed">" + "<Storyboard>" + "<ColorAnimation Duration="0" " + "Storyboard.TargetName="BackgroundRectangle" " + "Storyboard.TargetProperty="(Fill).Color" To="#FF448DCA"/>" + "<ColorAnimation Duration="0" " + "Storyboard.TargetName="BackgroundGradient" " + "Storyboard.TargetProperty="(Fill).(GradientStops)[0].Color" To="#D8FFFFFF"/>" + "<ColorAnimation Duration="0" " + "Storyboard.TargetName="BackgroundGradient" " + "Storyboard.TargetProperty="(Fill).(GradientStops)[1].Color" To="#C6FFFFFF"/>" + "<ColorAnimation Duration="0" " + "Storyboard.TargetName="BackgroundGradient" " + "Storyboard.TargetProperty="(Fill).(GradientStops)[2].Color" To="#8CFFFFFF"/>" + "<ColorAnimation Duration="0" " + "Storyboard.TargetName="BackgroundGradient" " + "Storyboard.TargetProperty="(Fill).(GradientStops)[3].Color" To="#3FFFFFFF"/>" + "</Storyboard>" + "</VisualState>" + #endregion "</VisualStateGroup>" + "<VisualStateGroup x:Name="SortStates">" + "<VisualState x:Name="Unsorted"/>" + "<VisualState x:Name="SortAscending">" + "<Storyboard>" + "<DoubleAnimation Duration="0" Storyboard.TargetName="SortIcon" Storyboard.TargetProperty="Opacity" To="1.0"/>" + "</Storyboard>" + "</VisualState>" + "<VisualState x:Name="SortDescending">" + "<Storyboard>" + "<DoubleAnimation Duration="0" Storyboard.TargetName="SortIcon" Storyboard.TargetProperty="Opacity" To="1.0"/>" + "<DoubleAnimation Duration="0" Storyboard.TargetName="SortIcon" Storyboard.TargetProperty="(RenderTransform).ScaleY" To="-.9"/>" + "</Storyboard>" + "</VisualState>" + "</VisualStateGroup>" + "</VisualStateManager.VisualStateGroups>" + #endregion #region ヘッダーの色 "<Rectangle x:Name="BackgroundRectangle" Fill="#FF1F3B53" Stretch="Fill" Grid.RowSpan="3" Grid.ColumnSpan="" + ColumnSpan.ToString() + ""/>" + "<Rectangle x:Name="BackgroundGradient" Stretch="Fill" Grid.RowSpan="3" Grid.ColumnSpan="" + ColumnSpan.ToString() + "">" + "<Rectangle.Fill>" + "<LinearGradientBrush EndPoint=".7,1" StartPoint=".7,0">" + "<GradientStop Color="#FCFFFFFF" Offset="0.015"/>" + "<GradientStop Color="#F7FFFFFF" Offset="0.375"/>" + "<GradientStop Color="#E5FFFFFF" Offset="0.6"/>" + "<GradientStop Color="#D1FFFFFF" Offset="1"/>" + "</LinearGradientBrush>" + "</Rectangle.Fill>" + "</Rectangle>" + #endregion #region ヘッダーのセパレータ "<Rectangle x:Name="VerticalSeparator" Fill="#FFC9CACA" " + "HorizontalAlignment="Right" Grid.RowSpan="" + ( ColumnSpan -1 ).ToString() + "" " + "VerticalAlignment="Stretch" Width="1" Visibility="Visible" " + "Grid.Column="" + ( ColumnSpan -1).ToString() + ""/>" + #endregion "<Grid Grid.Row="1" >"; if( SortPos != -1 ) { strStyleXML += "<Grid.ColumnDefinitions>" + "<ColumnDefinition />" + "<ColumnDefinition Width="19" />" + "</Grid.ColumnDefinitions>" + "<Path x:Name="SortIcon" "+ "Fill="#000000" " + "Stretch="Uniform" "+ "HorizontalAlignment="Right" " + "Margin="4,0,0,0" "+ "VerticalAlignment="Center" " + "Width="8" " + "Opacity="0" " + "RenderTransformOrigin=".5,.5" " + "Grid.Column="1" " + "Grid.Row="" + SortPos.ToString() + "" " + "Data="F1 M -5.215,6.099L 5.215,6.099L 0,0L -5.215,6.099 Z ">" + "<Path.RenderTransform>" + "<ScaleTransform ScaleX=".9" ScaleY=".9"/>" + "</Path.RenderTransform>" + "</Path>"; } strStyleXML += "<Grid.RowDefinitions>" + strGridRowDefinitions + "</Grid.RowDefinitions>" + strContentPresenters + "</Grid>"; if( SortPos == -1 ) { strStyleXML += "<Path x:Name="SortIcon" "+ "Fill="#000000" " + "Stretch="Uniform" "+ "HorizontalAlignment="Right" "+ "Margin="4,0,0,0" "+ "VerticalAlignment="Center" " + "Width="8" " + "Opacity="0" " + "RenderTransformOrigin=".5,.5" " + "Grid.Column="2" " + "Grid.RowSpan="4" " + "Data="F1 M -5.215,6.099L 5.215,6.099L 0,0L -5.215,6.099 Z ">" + "<Path.RenderTransform>" + "<ScaleTransform ScaleX=".9" ScaleY=".9"/>" + "</Path.RenderTransform>" + "</Path>"; } strStyleXML += "</Grid>" + "</ControlTemplate>"+ "</Setter.Value>" + #endregion "</Setter>" + "</Style>" ; return (Style)XamlReader.Load(strStyleXML); }
これで、多段のスタイルを動的に生成すます。
あとは、このスタイルをカラムのHeaderStyleに設定して上げればおk。
で、これができたはいいんですが、
データをバインドするためにはデータクラスを用意しなくてはなりません。
( 他にもConverterを使った逃げかたがあるようなのですが、それはよく分かりません )
どうしたものかなーと、悩んでいたらこんな記事を発見。
[ クラスの動的生成 - しがないプログラマ の日記 ]
http://d.hatena.ne.jp/Horiuchi_H/20081109/1226214664
ようは、アセンブリを動的に生成するらしい。
詳しいコードは後日解析しようとおもいます。
上記のコード丸パクリで行けるようですねー。
ただ、Silverlight用にちょっとゴニョゴニョしなくてはなりません。
どこをゴニョゴニョするかというと、
以下の二行を修正シマス
AssemblyBuilder assemblyBuilder = domain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.RunAndSave);
ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("SubModule", assemblyName.Name);
↓
AssemblyBuilder assemblyBuilder = domain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run); ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule( assemblyName.Name);
これでぱーでき。
表示できるし、取得できるし。
ただし、プロパティから値を拾うのがけだるいですねー。
できればプロパティのタイプに拡張性を持たせたいけども・・・、
なんかむずかしそうだわー。