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);


これでぱーでき。

表示できるし、取得できるし。

ただし、プロパティから値を拾うのがけだるいですねー。

できればプロパティのタイプに拡張性を持たせたいけども・・・、

なんかむずかしそうだわー。