none
DataGridでセル単位にコントロールを設定する方法 RRS feed

  • 質問

  • お世話になっております。

    DataGridのセル単位にコントロールを設定する方法を教えてください。

    行単位、列単位にコントロールは固定化できず、プログラムでDataGridに表示するデータを読み込んだ時点でセルのコントロールが決まります。

    たとえば、以下のように表示します。

       |列 1|列2|列3|列4

    行1|文字列|ハイパーリンク|画像+文字列|数値

    行2| 画像|文字列|数値|画像+文字列

    類似の質問で「DataGridでセル単位にコントロールを指定」にありましたので、参考にさせて頂きましたが、複数列がある場合、DataTemplateSelectorのSelectTemplateメソッドの第一引数(item)に配列が渡されるため、どの列のDataTemplateを返却してよいか、わからない状況でした。

    上記に例のように表示する場合、SelectTemplateメソッドは、利用せず、別の方法があるものでしょうか。

    調べ方も不足していると思いますが、よろしくお願いします。

    2013年7月28日 2:21

回答

  • 類似の質問で「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!)


    • 編集済み gekkaMVP 2013年7月29日 14:07 コードと文章の区切りを修正
    • 回答の候補に設定 星 睦美 2013年7月30日 8:22
    • 回答としてマーク ころわん 2013年7月30日 10:24
    2013年7月29日 14:06

すべての返信

  • 列単位にDataTemplateSelectorを別にしたら、すくなくともどのプロパティを対象に判断したらいいかわからないですかねぇ?

    その場合、DataGridの列数ぶんDataTemplateSelectorを継承したクラス作る必要がありますが。


    かずき Blog:http://d.hatena.ne.jp/okazuki/

    2013年7月28日 12:08
  • 表形式で表示する必要があるのでしょうか? 仮にあるとしても、私ならDataGridは使わないと思います。例えばListBoxを使えば、各行に好きなコントロールを自由に配置することができます。ただ、Visal Studio 2010だとデザイナを使って視覚的に作ることが出来ず、Blendが必要になります。Visual Studio 2012ではデザイナで編集することが可能です。


    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/

    2013年7月29日 0:27
    モデレータ
  • かずき_okazukiさん、ありがとうございます。

    説明不足ですいません。今回の表は、横方向に時系列でデータを表示する表になります。

    通常のGridの行列を入れ替えたイメージです。

    そのため、1行のデータの各列は、配列で保持しようと考えています。

    DataGridの列数分のDataTemplateSelectorクラスを用意するのは、少々厳しいと考えております。

    このような表で、対応方法案をご存知でしたら、よろしくお願いします。

    2013年7月29日 11:59
  • trapemiyaさん、ありがとうございます。

    表形式で、縦軸は、ある程度固定の項目を表示し、横軸に時系列データを表示するような、横スクロールがメインの表になります。

    ListBoxの利用は、確認できていませんでした。

    Visual Studio 2012を利用して、ListBoxでの実現性を検証してみたいと思います。

    • 回答の候補に設定 星 睦美 2013年7月30日 8:22
    2013年7月29日 12:06
  • 類似の質問で「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!)


    • 編集済み gekkaMVP 2013年7月29日 14:07 コードと文章の区切りを修正
    • 回答の候補に設定 星 睦美 2013年7月30日 8:22
    • 回答としてマーク ころわん 2013年7月30日 10:24
    2013年7月29日 14:06
  • ころわん さん、こんにちは
    フォーラム オペレーターの星 睦美です。

    今回は私のほうで質問に参考になりそうな返信に[回答の候補に設定]させていただきました。
    もし役立つ内容でしたら、投稿者から[回答としてマーク]いただければ幸いです。


    日本マイクロソフト株式会社 フォーラム オペレーター 星 睦美

    2013年7月30日 8:25
  • gekkaさん、ありがとうございます。

    ご提示頂いた方法で、セルの列位置が特定できました。

    横向きのItemsControlは、考えたいと思います。

    ありがとうございました。

    2013年7月30日 10:24