none
DataGridのセル結合 RRS feed

  • 質問

  • はじめて投稿させていただきます。

    DataGridのセル結合について調べてまして、ほぼ無理なのは今も変わらないようですね。

    それでもなんとかできないか挑戦しています。

    仕様の要求から以下のようなテンプレートのDataGridを使っています。

    標準の編集モードは利用せず、自前でTextBoxを張り付けています。

    <DataGridTemplateColumn Header="a"> <DataGridTemplateColumn.CellTemplate> <DataTemplate> <TextBox Text={Binding C1}"/> </DataTemplate> </DataGridTemplateColumn.CellTemplate> </DataGridTemplateColumn>

    「a」列には同じ値が続く場合があるので、そこを結合させたいのです。

    試してみるうちに、上記のTextBoxにアクセスできる事がわかり、

    これを結合後のセルの大きさにサイズを変えて覆いかぶせれば

    あたかも結合したように見えると考えました。

    サイズを変えるまではできるのですが次の行のセルの裏に潜っているので

    ZIndexで調整を試みているのですがダメです。

    このTextBoxを最前面に持ってくる方法はないのでしょうか?

    TextBox tb = dg.GetTextBox(0, 1); ←自前で作ったTextBoxを取得する拡張メソッド
    Canvas.SetZIndex(tb, 1); ←ZIndexを設定。しかし機能せず。
    tb.Height = 200; ←結合後の高さを設定。サイズはきちんと変わっている。(SNOOPで確認)


    2015年4月19日 7:36

回答

  • gekkaさん。ソースまでの提示していただいてありがとうございます。

    やりたいことは出来ました。ポイントはご提示通り、下から上、Canvasでした。
    gekkaさんのアドバイスをもとにXAMLは以下の変更をしました。

    <DataGridTemplateColumn Header="a">
        <DataGridTemplateColumn.CellTemplate>
            <DataTemplate>
                <Canvas>                                   ←追加
                    <TextBox Text={Binding C1}"/>
                </Canvas>                                   ←追加
            </DataTemplate>
        </DataGridTemplateColumn.CellTemplate>
    </DataGridTemplateColumn>

    ソースは以下のように変更しました。

    TextBox tb = dg.GetTextBox(2, 1); ←3行目2列目と2行目2列目を結合する Canvas.SetTop(tb, -60); ←一つ上のセルまで移動 tb.Height = 118; ←高さ調整 tb.Width = 248; ←幅調整

    Canvas cv1 = dg.GetCanvas(1, 1);
    cv1.Visibility = Visibility.Collapsed; ←一つ上のセルを隠す


    幅や高さは固定な表なので、これで大体仕様を満たせます。

    DataGridを使うかどうかは置いといてすっきり解決しました。ありがとうございました。

    • 回答としてマーク せいゆう 2015年4月25日 23:13
    • 回答としてマークされていない せいゆう 2015年4月25日 23:13
    • 回答としてマーク せいゆう 2015年4月29日 8:14
    2015年4月25日 23:12

すべての返信

  • セルはCanvasに配置されているわけでは無いのでCanvas.SetZIndexしても無意味です。

    DataGridのセル結合はやめた方がいいと思いますけどね。特に行方向の結合は仮想化を無効にしないと使い物にならないですし。
    どうしてもやりたいなら以下の方法で解決できないこともない。

    <Window x:Class="WpfApplication1.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:local="clr-namespace:WpfApplication1"
            Title="MainWindow" Height="350" Width="525">
        <Window.Resources>
            <local:CellHeigthConverter x:Key="hconv" />
        </Window.Resources>
        <Grid>
            <DataGrid ItemsSource="{x:Static local:Item.TestData}" AutoGenerateColumns="false"
                      VirtualizingPanel.IsVirtualizing="false"
                      CanUserAddRows="false" CanUserResizeRows="false" CanUserSortColumns="False" >
                <DataGrid.Columns>
                    <DataGridTextColumn Binding="{Binding Path=Count}" />
                    <DataGridTemplateColumn Header="a" Width="50">
                        <DataGridTemplateColumn.CellTemplate>
                            <DataTemplate>
                                <Canvas Height="0" Width="0"  Visibility="{Binding Path=Visibility}" VerticalAlignment="Bottom" HorizontalAlignment="Left">
                                    <TextBox Text="{Binding C1}" BorderThickness="0" BorderBrush="Transparent" 
                                             VerticalContentAlignment="Center" 
                                             Width="{Binding Path=ActualWidth,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=ContentPresenter}}"
                                             Canvas.Top="{Binding Path=ActualHeight,RelativeSource={RelativeSource Mode=Self},Converter={StaticResource hconv}}">
                                        <TextBox.Height>
                                            <MultiBinding Converter="{StaticResource hconv}">
                                                <Binding Path="ActualHeight" RelativeSource="{RelativeSource Mode=FindAncestor,AncestorType=DataGridRow}"/>
                                                <Binding Path="Count" />
                                            </MultiBinding>
                                        </TextBox.Height>
                                    </TextBox>
                                </Canvas>
                            </DataTemplate>
                        </DataGridTemplateColumn.CellTemplate>
                    </DataGridTemplateColumn>
    
                </DataGrid.Columns>
            </DataGrid>
        </Grid>
    </Window>
    using System;
    using System.Collections.Generic;
    using System.Windows;
    using System.Windows.Data;
    using System.Windows.Documents;
    
    namespace WpfApplication1
    {
        public partial class MainWindow : Window
        {
            public MainWindow() { InitializeComponent(); }
        }
    
        /// <summary>結合高さを処理するためのコンバーター</summary>
        public class CellHeigthConverter : IMultiValueConverter, IValueConverter
        {
            public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
            {
                if (values != null && values[0] is double && values[1] is int)
                {
                    //行の高さは一定であるとみなして結合数から高さを計算
                    return (double)values[0] * (int)values[1] - 2;//適当にマージン補正
                }
                else
                {
                    return values;
                }
            }
    
            public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
            {
                throw new NotSupportedException();
            }
    
            public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
            {
                if (value is double)
                {
                    //結合した高さの分だけCanvasのTopを上に移動させるためにマイナスに
                    return -(double)value;
                }
                else
                {
                    return value;
                }
            }
    
            public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
            {
                throw new NotSupportedException();
            }
        }
    
        //テストデータ
        public class Item
        {
            public string C1 { get; set; }
            public int Count { get; set; }//結合数
            public Visibility Visibility { get; set; }//結合の邪魔になるので使わないセルを非表示にするため
    
            public static List<Item> CreateTestItems()
            {
                List<Item> retval = new List<Item>();
                Item item;
                retval.Add(new Item() { C1 = "非結合", Visibility = System.Windows.Visibility.Visible, Count = 1 });
    
                for (int i = 0; i < 9; i++)
                {
                    //結合で隠れるセル用
                    retval.Add(new Item() { C1 = "意味無し", Visibility = Visibility.Collapsed, Count = 1 });
                }
    
                item = new Item();
                item.C1 = "結合";
                item.Visibility = Visibility.Visible;
                item.Count = 10;//下から上に結合するので最後に結合の処理
                retval.Add(item);
    
                retval.Add(new Item() { C1 = "非結合", Visibility = System.Windows.Visibility.Visible, Count = 1 });
    
                return retval;
            }
    
            public static List<Item> TestData
            {
                get { return _TestData ?? (_TestData = CreateTestItems()); }
            }
            private static List<Item> _TestData;
        }
    }

    下の行が覆いかぶさってしまうのは、上から下に向かって配置処理が行われるためです。
    であるなら、上のセルを下に伸ばすのではなく、下のセルを上に伸ばせば良いということになります。

    下から上に伸ばすのが嫌なら、下から上に向かって配置するStackPanel(ArrangeOverrideでできます)を自分で作って、DataGrid.ItemsPanelに設定してください。

    追記:ArrangeOverride使わなくてもできました。

    class ReverseOverwrapStackPanel : System.Windows.Controls.StackPanel
    {
        protected override System.Windows.Media.Visual GetVisualChild(int index)
        {
            return base.GetVisualChild(this.VisualChildrenCount - index - 1);
        }
    }

    個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)

    • 編集済み gekkaMVP 2015年4月20日 12:44
    • 回答の候補に設定 星 睦美 2015年4月21日 5:31
    2015年4月19日 13:06
  • gekkaさん。ソースまでの提示していただいてありがとうございます。

    やりたいことは出来ました。ポイントはご提示通り、下から上、Canvasでした。
    gekkaさんのアドバイスをもとにXAMLは以下の変更をしました。

    <DataGridTemplateColumn Header="a">
        <DataGridTemplateColumn.CellTemplate>
            <DataTemplate>
                <Canvas>                                   ←追加
                    <TextBox Text={Binding C1}"/>
                </Canvas>                                   ←追加
            </DataTemplate>
        </DataGridTemplateColumn.CellTemplate>
    </DataGridTemplateColumn>

    ソースは以下のように変更しました。

    TextBox tb = dg.GetTextBox(2, 1); ←3行目2列目と2行目2列目を結合する Canvas.SetTop(tb, -60); ←一つ上のセルまで移動 tb.Height = 118; ←高さ調整 tb.Width = 248; ←幅調整

    Canvas cv1 = dg.GetCanvas(1, 1);
    cv1.Visibility = Visibility.Collapsed; ←一つ上のセルを隠す


    幅や高さは固定な表なので、これで大体仕様を満たせます。

    DataGridを使うかどうかは置いといてすっきり解決しました。ありがとうございました。

    • 回答としてマーク せいゆう 2015年4月25日 23:13
    • 回答としてマークされていない せいゆう 2015年4月25日 23:13
    • 回答としてマーク せいゆう 2015年4月29日 8:14
    2015年4月25日 23:12