WPF datagrid列标题跨越多个列

时间:2010-04-14 14:35:51

标签: wpf datagrid

在WPF数据网格中,是否可以对列标题进行分组?

我所追求的是

| Column 1 | Column 2 | Column 3|
| a  b  c  | a  b  c  | a  b  c |
| z  x  y  | z  x  y  | z  x  y |

我四处搜寻,看不到明显的做法。我可以使用模板化的列,然后模仿每个模板中的额外单元格,但这不适合订购等。

我想我所说的一切,我正在寻找的是人们如何设法跨越多个coluns的列标题。

非常感谢任何帮助或想法。

6 个答案:

答案 0 :(得分:5)

这是一个老线程,但我想我应该分享我是如何做到的。

在我的应用程序中,我想在单个列标题“维护费用日期”下显示三列日期条目。我创建了一个列,其中包含两个DataTemplates,一个用于显示,另一个用于编辑:

<DataGrid.Resources>
  <DataTemplate x:Key="cellTemplate">
     <Grid>
        <Grid.ColumnDefinitions>
           <ColumnDefinition Width="100" />
           <ColumnDefinition Width="100" />
           <ColumnDefinition Width="100" />
        </Grid.ColumnDefinitions>
        <TextBlock x:Name="tbDate1"
                    Text="{Binding Path=Date1}"
                    Grid.Column="0" />
        <TextBlock x:Name="tbDate2"
                    Text="{Binding Path=Date2}"
                    Grid.Column="1" />
        <TextBlock x:Name="tbDate3"
                    Text="{Binding Path=Date3}"
                    Grid.Column="2" />
     </Grid>
  </DataTemplate>
  <DataTemplate x:Key="cellEditingTemplate">
     <Grid>
        <Grid.ColumnDefinitions>
           <ColumnDefinition Width="100" />
           <ColumnDefinition Width="100" />
           <ColumnDefinition Width="100" />
        </Grid.ColumnDefinitions>
        <DatePicker Grid.Column="0"
                    Margin="0"
                    Name="dpDate1"
                    Width="100"
                    SelectedDate="{Binding Path=Date1}" />
        <DatePicker Grid.Column="1"
                    Margin="0"
                    Name="dpDate2"
                    Width="100"
                    SelectedDate="{Binding Path=Date2}" />
        <DatePicker Grid.Column="2"
                    Margin="0"
                    Name="dpDate3"
                    Width="100"
                    SelectedDate="{Binding Path=Date3}" />
     </Grid>
  </DataTemplate>

然后我将列定义为DataGridTemplateColumn,指向上面的DataTemplates:

<DataGrid.Columns>
....
   <DataGridTemplateColumn CellTemplate="{StaticResource cellTemplate}"
                           Header="Maintenance Fee Dates"
                           CellEditingTemplate="{StaticResource cellEditingTemplate}" />
....
</DataGrid.Columns>

由于DataTemplate是使用具有三个固定长度列的Grid布局的,因此在单列标题下我会得到三个很好的日期列(或编辑时的DatePickers)。

水平网格线可以由网格处理。要在三列之间使用垂直网格线,只需将中间列的控件放在边框控件中即可。将Border控件设置为与Grid列相同的宽度,仅显示其右边框和左边框,并将其BorderBrush设置为与DataGrid的Gridlines的颜色相匹配:

  <DataTemplate x:Key="cellTemplate">
     <Grid>
        <Grid.ColumnDefinitions>
           <ColumnDefinition Width="100" />
           <ColumnDefinition Width="100" />
           <ColumnDefinition Width="100" />
        </Grid.ColumnDefinitions>
        <TextBlock x:Name="tbDate1"
                    Text="{Binding Path=Date1}"
                    Grid.Column="0" />
        <Border BorderThickness="1,0,1,0"
                BorderBrush="DarkGray"
                Width="100">
           <Border.Child>
              <TextBlock x:Name="tbDate2"
                          Text="{Binding Path=Date2}"
                          Grid.Column="1" />
           </Border.Child>
        </Border>
        <TextBlock x:Name="tbDate3"
                    Text="{Binding Path=Date3}"
                    Grid.Column="2" />
     </Grid>
  </DataTemplate>

答案 1 :(得分:2)

这个问题有点陈旧,但这是我在寻找这个问题的解决方案时出现的第一个google结果之一,我不喜欢这里发布的任何一个答案。所以这是使用自定义标头和ClipToBounds = False的简单替代方法。

<DataGridTextColumn.Header>
    <StackPanel Orientation="Horizontal">
        <TextBlock x:Name="HeightSpacer" Text="P" FontWeight="Bold" />
        <Canvas Height="{Binding ElementName=HeightSpacer, Path=ActualHeight}">
            <TextBlock Text="hone Numbers" FontWeight="Bold" ClipToBounds="False" />
        </Canvas>
    </StackPanel>
</DataGridTextColumn.Header>

您唯一需要做的就是确保此标头发送垃圾邮件的任何列的组合宽度至少是标题列的宽度。

包含标题文本的第一个字符和TextBlock绑定的Canvas.Height用于阻止标题所需的高度。只有当你的完整数据网格标题没有定义标题高度的元素,或者垃圾邮件多个列的标题比其他标题更大时才需要它(这是我的情况......这是唯一带有粗体的标题列文本)

答案 2 :(得分:1)

为列创建自定义HeaderTemplate。

    <DataGrid>
        <DataGrid.Columns>
            <DataGridTextColumn>
                <DataGridTextColumn.HeaderTemplate>
                    <DataTemplate>
                        <StackPanel>
                            <TextBlock>Column 1</TextBlock>
                            <TextBlock>xyz</TextBlock>
                        </StackPanel>
                    </DataTemplate>
                </DataGridTextColumn.HeaderTemplate>
            </DataGridTextColumn>
            <DataGridTextColumn Header="Header" />
        </DataGrid.Columns>
    </DataGrid>

答案 3 :(得分:1)

您可能最好删除列标题并在网格外添加自己的列标题。有一个好的帖子here显示了如何制作一个跨越多列的“标题”。

答案 4 :(得分:0)

这是一篇很老的帖子,我喜欢D&#39; Hags answere。不幸的是,它并没有完全满足我的需求。因此,我现在允许扩展D&#39; Hags解决方案:

  • 列跨度可以以任意方式进行,而不仅仅是整行
  • 列范围是使用对象引用定义的。当第2列和第3列数据绑定到同一个对象引用时,将呈现范围
  • 列范围应该像其他列一样显示,以便例如可以进行选择。

解决方案是这样的:

带有样式的Datagrid:

    <DataGrid  x:Name="dataGrid" ItemsSource="{Binding}" AutoGenerateColumns="False">
        <DataGrid.Resources>
            <Style TargetType="DataGridRow">

                        <Setter Property="ItemsPanel" >
                            <Setter.Value>
                                <ItemsPanelTemplate>
                                    <local:DataGridSpannedCellPanel ></local:DataGridSpannedCellPanel>

                                </ItemsPanelTemplate>
                            </Setter.Value>
                        </Setter>

            </Style>
        </DataGrid.Resources>

    </DataGrid>

设置要使用的以下类,而不是直接使用DataGridCellsPanel:

public class DataGridSpannedCellPanel : DataGridCellsPanel
{
    protected override Size ArrangeOverride(Size arrangeSize)
    {
        if (DataContext is IEnumerable)
        {
            base.ArrangeOverride(arrangeSize);

            IEnumerable<Object> data = ((IEnumerable)DataContext).Cast<Object>();
            double totalPreviousWidth = 0;
            double totalPos = 0;


            List<int> columnSize = new List<int>();
            double currentSize = 0;


            for (int i = 0; i < data.Count(); ++i)
            {
                Object el = data.ElementAt(i);
                Object nextEl = null;

                UIElement uiel = InternalChildren[i];

                if (data.Count() > i + 1)
                {
                    nextEl = data.ElementAt(i + 1);
                }


                if (Object.ReferenceEquals(el, nextEl) && el != null)
                {
                    totalPreviousWidth += uiel.RenderSize.Width;
                    uiel.Arrange(new Rect(new Point(0, 0), new Size(0, 0)));
                }
                else
                {
                    if (totalPreviousWidth > 0)
                    {
                        uiel.Arrange(new Rect(new Point(totalPos, 0),
                    new Size(totalPreviousWidth + uiel.RenderSize.Width, uiel.RenderSize.Height))

                     );
                        currentSize = totalPreviousWidth + uiel.RenderSize.Width;
                    }

                    totalPos += uiel.RenderSize.Width;

                    totalPreviousWidth = 0;
                }
            }

            return arrangeSize;
        }
        else
        {
            return base.ArrangeOverride(arrangeSize);
        }

    }
 }
}

它以下列方式使用:

public partial class MainWindow : Window
{

    void AddColumn(DataGrid dg, int i)
    {

        var col = new DataGridTextColumn();
        col.Header = (char)('A' + i);
        col.Binding = new Binding("[" + i + "]");

        dg.Columns.Add(col);

    }

    public MainWindow()
    {
        InitializeComponent();

        for (int i = 0; i < 10; ++i)
        {
            AddColumn(dataGrid, i);
        }

        List<object> rows = new List<object>();

        String[] txtHeader = new string[7];
        String multiSpan = "MergedColumn";
        txtHeader[0] = "Col1";
        txtHeader[1] = "Col2";
        txtHeader[2] = "Col3";
        // these columns should be merged to one, which is indicated by assigning 
        // the same reference to all columns.
        txtHeader[3] = multiSpan;
        txtHeader[4] = multiSpan;
        txtHeader[5] = multiSpan;

        int[] intArr = new int[10];
        for (int i = 0; i < 10; i++)
        {
            intArr[i] = i;
        }

        rows.Add(txtHeader);
        rows.Add(intArr);

        dataGrid.ItemsSource = rows;
    }
}
}

答案 5 :(得分:0)

以下是我完全使用XAML的方式。

GD Column的宽度决定了4列中每一列的宽度,UniformGrid认为列的宽度相等。 TextBoxes伸展到完全可用宽度。

DG Cell(= 4 TextBoxes)的着色被单个文本框的着色(MouseOver)取代。

<DataGridTemplateColumn Width=200>
    <DataGridTemplateColumn.CellStyle>
        <Style/> <!-- Disable previously set style -->
    </DataGridTemplateColumn.CellStyle>
    <DataGridTemplateColumn.Header>
        <StackPanel>
            <TextBlock  
                IsEnabled="False"
                Style="{StaticResource TextBlockDataGridHeader}">
                <Run>Windspeeds in area</Run>
            </TextBlock>
            <UniformGrid
                HorizontalAlignment="Stretch"
                Columns="4">
                <Label Grid.Column="0" Content="1"/>
                <Label Grid.Column="1" Content="2"/>
                <Label Grid.Column="2" Content="3"/>
                <Label Grid.Column="3" Content="4"/>
            </UniformGrid>
            <TextBlock           
                Style="{StaticResource TextBlockDataGridHeader}"
                Foreground="Red"
                Text="m/s"/>

        </StackPanel>
    </DataGridTemplateColumn.Header>
    <DataGridTemplateColumn.CellTemplate>
        <DataTemplate>
            <UniformGrid
                HorizontalAlignment="Stretch"
                Columns="4">
                <UniformGrid.Resources>
                    <Style TargetType="TextBox">
                        <Setter Property="Padding" Value="3,0,3,0"/>
                        <!-- Let row background shine through -->
                        <Setter Property="Background" Value="Transparent"/>
                        <Setter Property="Foreground" Value="White"/>
                        <!-- 'Cells' have no border -->
                        <Setter Property="BorderThickness" Value="0"/>
                        <Style.Triggers>
                            <!-- Flip colors on mouseOver -->
                            <Trigger Property="IsMouseOver" Value="True">
                               <Setter Property="Background" Value="White" />
                               <Setter Property="Foreground" Value="Black" />
                            </Trigger>
                        </Style.Triggers>
                    </Style>
                </UniformGrid.Resources>                  
                <TextBox 
                    Grid.Column="0"         
                    Text="{Binding VRef0[0],Mode=TwoWay, UpdateSourceTrigger=LostFocus,StringFormat=N1}"/>
                <TextBox 
                    Grid.Column="1"
                    Text="{Binding VRef0[1],Mode=TwoWay, UpdateSourceTrigger=LostFocus,StringFormat=N1}"/>
                <TextBox 
                    Grid.Column="2"
                    Text="{Binding VRef0[2],Mode=TwoWay, UpdateSourceTrigger=LostFocus,StringFormat=N1}"/>
                <TextBox 
                    Grid.Column="3"
                    Text="{Binding VRef0[3],Mode=TwoWay, UpdateSourceTrigger=LostFocus,StringFormat=N1}"/>
            </UniformGrid>
        </DataTemplate>                        
    </DataGridTemplateColumn.CellTemplate> 
</DataGridTemplateColumn>