トップ回答者
DataGridでセル単位にコントロールを設定する方法

質問
-
お世話になっております。
DataGridのセル単位にコントロールを設定する方法を教えてください。
行単位、列単位にコントロールは固定化できず、プログラムでDataGridに表示するデータを読み込んだ時点でセルのコントロールが決まります。
たとえば、以下のように表示します。
|列 1|列2|列3|列4
行1|文字列|ハイパーリンク|画像+文字列|数値
行2| 画像|文字列|数値|画像+文字列
類似の質問で「DataGridでセル単位にコントロールを指定」にありましたので、参考にさせて頂きましたが、複数列がある場合、DataTemplateSelectorのSelectTemplateメソッドの第一引数(item)に配列が渡されるため、どの列のDataTemplateを返却してよいか、わからない状況でした。
上記に例のように表示する場合、SelectTemplateメソッドは、利用せず、別の方法があるものでしょうか。
調べ方も不足していると思いますが、よろしくお願いします。
回答
-
類似の質問で「DataGridでセル単位にコントロールを指定」にありましたので、参考にさせて頂きましたが、複数列がある場合、DataTemplateSelectorのSelectTemplateメソッドの第一引数(item)に配列が渡されるため、どの列のDataTemplateを返却してよいか、わからない状況でした。
どの列かはSelectTemplate(object item, DependencyObject container)のcontainerの親を辿っていくとDataGridCellが見つかるので、そこからDataGridColumが。さらに辿ってDataGridRowが見つかれば、ItemContainerGenerator.IndexFromContainerで何列目かがわかります。
たとえばこんな
#きちんと動作検証していないのです<Grid> <DataGrid AutoGenerateColumns="False" ItemsSource="{Binding}" IsReadOnly="true" > <DataGrid.Resources> <app:CustomCellTemplateSelector x:Key="cellSelector" xmlns:app="clr-namespace:WpfApplication1"/> <DataTemplate x:Key="CELL_TEXT"> <Grid Background="Red"> <ContentPresenter Content="{Binding Mode=OneWay}" /> </Grid> </DataTemplate> <DataTemplate x:Key="CELL_URI"> <Grid> <TextBlock> <Hyperlink NavigateUri="{Binding}" > <Run Text="{Binding Mode=OneWay}" /> </Hyperlink> </TextBlock> </Grid> </DataTemplate> <DataTemplate x:Key="CELL_URIIMAGE"> <StackPanel Orientation="Horizontal" > <Image Source="{Binding Mode=OneWay}" Width="30" Height="30"/> <TextBlock Text="{Binding Mode=OneWay}" /> </StackPanel> </DataTemplate> <DataTemplate x:Key="CELL_DOUBLE"> <TextBlock Text="{Binding Mode=OneWay}" Foreground="Green" /> </DataTemplate> <DataTemplate x:Key="CELL_DATETIME"> <DatePicker SelectedDate="{Binding Mode=OneWay}"/> </DataTemplate> </DataGrid.Resources> <DataGrid.Columns> <DataGridTemplateColumn Header="列1" CellTemplateSelector="{StaticResource cellSelector}" > </DataGridTemplateColumn> <DataGridTemplateColumn Header="列2" CellTemplateSelector="{StaticResource cellSelector}" > </DataGridTemplateColumn> <DataGridTemplateColumn Header="列3" CellTemplateSelector="{StaticResource cellSelector}" > </DataGridTemplateColumn> <DataGridTemplateColumn Header="列4" CellTemplateSelector="{StaticResource cellSelector}" > </DataGridTemplateColumn> </DataGrid.Columns> </DataGrid> </Grid>
namespace WpfApplication1 { public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); Test(); } public void Test() { List<object[]> list = new List<object[]>(); object[] item = new object[] { "あいうえお", new Uri("http://msdn.microsoft.com/ms348103.LOGO_VS2%28ja-jp,MSDN.10%29.png"), DateTime.Now , 1.2345, }; list.Add(item); item = new object[] { new Uri("http://msdn.microsoft.com/ms348103.LOGO_Win1211(ja-jp,MSDN.10).png"), Colors.LightBlue , -1000, "かきくけこ", }; list.Add(item); this.DataContext = list; } } class CustomCellTemplateSelector : System.Windows.Controls.DataTemplateSelector { protected static T FindParent<T>(DependencyObject container) where T : DependencyObject { T target = null; DependencyObject dpo = container; while (dpo != null) { target = dpo as T; if (target != null) { return target; } dpo = System.Windows.Media.VisualTreeHelper.GetParent(dpo); } return null; } public override DataTemplate SelectTemplate(object item, DependencyObject container) { if (item != null) { DataGridCell cell = FindParent<DataGridCell>(container); if (cell != null) { DataGridRow row = FindParent<DataGridRow>(cell); DataGrid dg = FindParent<DataGrid>(row); int colIndex = dg.Columns.IndexOf(cell.Column); int rowIndex = dg.ItemContainerGenerator.IndexFromContainer(row); cell.DataContext = item; if (item is System.Collections.IList) { var list = (System.Collections.IList)item; cell.SetBinding(FrameworkElement.DataContextProperty, new Binding("[" + colIndex + "]") { Source = item }); item = list[colIndex]; } object key = null; if (item is string) { key = "CELL_TEXT"; } else if (item is Uri) { Uri uri = (Uri)item; if(uri.AbsoluteUri.EndsWith(".png",true,null)) { key="CELL_URIIMAGE"; } else { key = "CELL_URI"; } } else if (item is double) { key = "CELL_DOUBLE"; } else if (item is DateTime) { key = "CELL_DATETIME"; } if (key != null) { var t = dg.FindResource(key) as DataTemplate; if (t != null) { return t; } } } } return base.SelectTemplate(item, container); } } }
横スクロールさせたいのが目的なら、横向きのItemsControlを用意した方が早くて確実だと思う
DataGridのままならLayoutTransformで90度回転させるとか<DataGrid ItemsSource="{Binding}" AutoGenerateColumns="true"> <DataGrid.LayoutTransform> <RotateTransform Angle="-90"/> </DataGrid.LayoutTransform> <DataGrid.Resources> <Style TargetType="{x:Type DataGridCell}"> <Setter Property="LayoutTransform"> <Setter.Value> <RotateTransform Angle="90" /> </Setter.Value> </Setter> </Style> </DataGrid.Resources> </DataGrid>
個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)
すべての返信
-
-
類似の質問で「DataGridでセル単位にコントロールを指定」にありましたので、参考にさせて頂きましたが、複数列がある場合、DataTemplateSelectorのSelectTemplateメソッドの第一引数(item)に配列が渡されるため、どの列のDataTemplateを返却してよいか、わからない状況でした。
どの列かはSelectTemplate(object item, DependencyObject container)のcontainerの親を辿っていくとDataGridCellが見つかるので、そこからDataGridColumが。さらに辿ってDataGridRowが見つかれば、ItemContainerGenerator.IndexFromContainerで何列目かがわかります。
たとえばこんな
#きちんと動作検証していないのです<Grid> <DataGrid AutoGenerateColumns="False" ItemsSource="{Binding}" IsReadOnly="true" > <DataGrid.Resources> <app:CustomCellTemplateSelector x:Key="cellSelector" xmlns:app="clr-namespace:WpfApplication1"/> <DataTemplate x:Key="CELL_TEXT"> <Grid Background="Red"> <ContentPresenter Content="{Binding Mode=OneWay}" /> </Grid> </DataTemplate> <DataTemplate x:Key="CELL_URI"> <Grid> <TextBlock> <Hyperlink NavigateUri="{Binding}" > <Run Text="{Binding Mode=OneWay}" /> </Hyperlink> </TextBlock> </Grid> </DataTemplate> <DataTemplate x:Key="CELL_URIIMAGE"> <StackPanel Orientation="Horizontal" > <Image Source="{Binding Mode=OneWay}" Width="30" Height="30"/> <TextBlock Text="{Binding Mode=OneWay}" /> </StackPanel> </DataTemplate> <DataTemplate x:Key="CELL_DOUBLE"> <TextBlock Text="{Binding Mode=OneWay}" Foreground="Green" /> </DataTemplate> <DataTemplate x:Key="CELL_DATETIME"> <DatePicker SelectedDate="{Binding Mode=OneWay}"/> </DataTemplate> </DataGrid.Resources> <DataGrid.Columns> <DataGridTemplateColumn Header="列1" CellTemplateSelector="{StaticResource cellSelector}" > </DataGridTemplateColumn> <DataGridTemplateColumn Header="列2" CellTemplateSelector="{StaticResource cellSelector}" > </DataGridTemplateColumn> <DataGridTemplateColumn Header="列3" CellTemplateSelector="{StaticResource cellSelector}" > </DataGridTemplateColumn> <DataGridTemplateColumn Header="列4" CellTemplateSelector="{StaticResource cellSelector}" > </DataGridTemplateColumn> </DataGrid.Columns> </DataGrid> </Grid>
namespace WpfApplication1 { public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); Test(); } public void Test() { List<object[]> list = new List<object[]>(); object[] item = new object[] { "あいうえお", new Uri("http://msdn.microsoft.com/ms348103.LOGO_VS2%28ja-jp,MSDN.10%29.png"), DateTime.Now , 1.2345, }; list.Add(item); item = new object[] { new Uri("http://msdn.microsoft.com/ms348103.LOGO_Win1211(ja-jp,MSDN.10).png"), Colors.LightBlue , -1000, "かきくけこ", }; list.Add(item); this.DataContext = list; } } class CustomCellTemplateSelector : System.Windows.Controls.DataTemplateSelector { protected static T FindParent<T>(DependencyObject container) where T : DependencyObject { T target = null; DependencyObject dpo = container; while (dpo != null) { target = dpo as T; if (target != null) { return target; } dpo = System.Windows.Media.VisualTreeHelper.GetParent(dpo); } return null; } public override DataTemplate SelectTemplate(object item, DependencyObject container) { if (item != null) { DataGridCell cell = FindParent<DataGridCell>(container); if (cell != null) { DataGridRow row = FindParent<DataGridRow>(cell); DataGrid dg = FindParent<DataGrid>(row); int colIndex = dg.Columns.IndexOf(cell.Column); int rowIndex = dg.ItemContainerGenerator.IndexFromContainer(row); cell.DataContext = item; if (item is System.Collections.IList) { var list = (System.Collections.IList)item; cell.SetBinding(FrameworkElement.DataContextProperty, new Binding("[" + colIndex + "]") { Source = item }); item = list[colIndex]; } object key = null; if (item is string) { key = "CELL_TEXT"; } else if (item is Uri) { Uri uri = (Uri)item; if(uri.AbsoluteUri.EndsWith(".png",true,null)) { key="CELL_URIIMAGE"; } else { key = "CELL_URI"; } } else if (item is double) { key = "CELL_DOUBLE"; } else if (item is DateTime) { key = "CELL_DATETIME"; } if (key != null) { var t = dg.FindResource(key) as DataTemplate; if (t != null) { return t; } } } } return base.SelectTemplate(item, container); } } }
横スクロールさせたいのが目的なら、横向きのItemsControlを用意した方が早くて確実だと思う
DataGridのままならLayoutTransformで90度回転させるとか<DataGrid ItemsSource="{Binding}" AutoGenerateColumns="true"> <DataGrid.LayoutTransform> <RotateTransform Angle="-90"/> </DataGrid.LayoutTransform> <DataGrid.Resources> <Style TargetType="{x:Type DataGridCell}"> <Setter Property="LayoutTransform"> <Setter.Value> <RotateTransform Angle="90" /> </Setter.Value> </Setter> </Style> </DataGrid.Resources> </DataGrid>
個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)