WPF工具包DataGrid:如何使列标题宽度与网格列宽度相同

4
我使用以下自定义列标题样式来为我的WPF Toolkit DataGrid添加表头:
<Style x:Name="ColumnStyle" x:Key="ColumnHeaderStyle" TargetType="my:DataGridColumnHeader">
    <Setter Property="ContentTemplate">
        <Setter.Value>
            <DataTemplate>
                <StackPanel Orientation="Vertical" HorizontalAlignment="Stretch" Background="LightYellow">
                    <TextBlock Text="{Binding Name}" HorizontalAlignment="Stretch" TextAlignment="Left" Background="LightGreen" />
                    <TextBlock Text="{Binding Data}" HorizontalAlignment="Stretch" TextAlignment="Right" Background="LightBlue" />
                </StackPanel>
            </DataTemplate>
        </Setter.Value>
    </Setter>
</Style>

头部的DataContext在代码中设置,使用DataGrid的初始宽度正确显示了Name和Data字符串属性:

---------------
|Name         |
|         Data|
---------------

然而,当我调整列的大小时,标题显示没有重新排版,而是保持不变:
--------------------
|Name              |
|         Data     |
--------------------

我原本期望它看起来是这样的:
--------------------
|Name              |
|              Data|
--------------------

我需要做些什么才能获得上述所需的行为?

同样,标题内容似乎也无法在垂直方向上拉伸。

更新:添加

    <Setter Property="VerticalAlignment">
        <Setter.Value>Bottom</Setter.Value>
    </Setter>

在样式中设置HorizontalAlignment属性为'Stretch'似乎可以将标题正确对齐到底部。不幸的是,这并没有达到我想要的效果。

复现详细信息: 以下是演示该行为的代码片段。

Window1.xaml:

<Window x:Class="GridTest.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" Height="300" Width="300" xmlns:my="http://schemas.microsoft.com/wpf/2008/toolkit">
    <Window.Resources>
        <Style x:Name="ColumnStyle" x:Key="ColumnHeaderStyle" TargetType="my:DataGridColumnHeader">
            <Setter Property="ContentTemplate">
                <Setter.Value>
                    <DataTemplate>
                        <DockPanel>
                            <TextBlock DockPanel.Dock="Left" Text="{Binding Name}" />
                            <TextBlock DockPanel.Dock="Right" Text="{Binding Data}" />
                        </DockPanel>
                    </DataTemplate>
                </Setter.Value>
            </Setter>
        </Style>

        <Style x:Name="RowHeaderStyle" x:Key="RowHeaderStyle" TargetType="my:DataGridRowHeader">
            <Setter Property="Content" Value="{Binding}" />
            <Setter Property="ContentTemplate">
                <Setter.Value>
                    <DataTemplate>
                        <StackPanel Orientation="Horizontal">
                            <TextBlock Text="{Binding Path=Content.Name, RelativeSource={RelativeSource FindAncestor,AncestorType={x:Type my:DataGridRowHeader}}}" 
                                       VerticalAlignment="Center"/>
                            <TextBlock Padding="5">|</TextBlock>
                            <TextBlock Text="{Binding Path=Content.Data, RelativeSource={RelativeSource FindAncestor,AncestorType={x:Type my:DataGridRowHeader}}}"
                                       VerticalAlignment="Center"/>
                        </StackPanel>
                    </DataTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </Window.Resources>
    <Grid>
        <my:DataGrid Name="dg" 
                     ColumnHeaderStyle="{StaticResource ColumnHeaderStyle}"
                     RowHeaderStyle="{StaticResource RowHeaderStyle}"
                     HeadersVisibility="All">
        </my:DataGrid>
    </Grid>
</Window>

以及 Window1.xaml.cs 中的代码后台

using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Data;
using Microsoft.Windows.Controls;
using SLModel;

namespace GridTest
{
    /// <summary>
    /// Interaction logic for Window1.xaml
    /// </summary>
    public partial class Window1 : Window
    {
        public Window1()
        {
            InitializeComponent();

            this.Loaded += new RoutedEventHandler(Window1_Loaded);
        }

        void Window1_Loaded(object sender, RoutedEventArgs e)
        {
            Inputs = new List<Input>();
            Outputs = new List<Output>();

            Input i1 = new Input() { Name = "I 1", Data = "data 1" };
            Input i2 = new Input() { Name = "I 2", Data = "data 2" };
            Input i3 = new Input() { Name = "I 3", Data = "data 3" };


            Inputs.Add(i1); Inputs.Add(i2);

            Output o1 = new Output() { Name = "O 1", Data = "data 1" };
            Output o2 = new Output() { Name = "O 2", Data = "data 2" };
            Output o3 = new Output() { Name = "O 3", Data = "data 3" };

            Outputs.Add(o1); Outputs.Add(o2); Outputs.Add(o3);

            Relationship r1 = new Relationship() { Formula = "F1" };
            Relationship r2 = new Relationship() { Formula = "F2" };
            Relationship r3 = new Relationship() { Formula = "F3" };
            Relationship r4 = new Relationship() { Formula = "F4" };
            Relationship r5 = new Relationship() { Formula = "F5" };
            Relationship r6 = new Relationship() { Formula = "F6" };


            i1.Relationships.Add(r1);
            i1.Relationships.Add(r2);
            i2.Relationships.Add(r3);
            i2.Relationships.Add(r4);
            i3.Relationships.Add(r5);
            i3.Relationships.Add(r6);

            CreateColumn(o1, 0);
            CreateColumn(o2, 1);
            CreateColumn(o3, 2);

            dg.Items.Add(i1);
            dg.Items.Add(i2);
            dg.Items.Add(i3);
            dg.ColumnWidth = DataGridLength.SizeToHeader;
        }

        private void CreateColumn(Output output, int index)
        {
            Binding textBinding = new Binding();
            textBinding.Path = new PropertyPath(string.Format("Relationships[{0}].Formula", index));
            textBinding.Mode = BindingMode.TwoWay;

            DataGridTextColumn tc = new DataGridTextColumn();
            tc.Binding = textBinding;
            dg.Columns.Add(tc);
            tc.Header = output;
        }

        private List<Output> Outputs { get; set; }
        private List<Input> Inputs { get; set; }
    }
}

使用简单的类Input、Output和Relationship,如下所示:

public class Input { public Input() { Relationships = new ObservableCollection(); }

public string Name { get; set; }
public string Data { get; set; }

public ObservableCollection<Relationship> Relationships { get; set; }

公共类 Output { 公共 Output() { } }

public string Name { get; set; }
public string Data { get; set; }

public class Relationship { public Relationship() { } public string Formula { get; set; } }

复现步骤:

  1. 打开应用程序

  2. 观察列标题“O 1data 1”,“O 2data 2”和“O 3data 3”

  3. 通过拖动列分隔符将第一列变宽

  4. 观察“Name” TextBlock(在本例中为“O 1”)与“Data” TextBlock(“data 1”)之间的距离是否发生变化,即“Data” TextBlock 是否已“停靠”到列标题的右侧边缘。

3个回答

4

我建议用Grid替换你的StackPanel

<Style x:Name="ColumnStyle" x:Key="ColumnHeaderStyle" TargetType="my:DataGridColumnHeader">
    <Setter Property="ContentTemplate">
        <Setter.Value>
            <DataTemplate>
                <Grid>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="Auto" />
                        <RowDefinition Height="Auto" />
                    </Grid.RowDefinitions>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="Auto" />
                        <ColumnDefinition Width="*" />
                        <ColumnDefinition Width="Auto" />
                    </Grid.ColumnDefinitions>
                    <TextBlock Grid.Row="0" Grid.Column="0" Text="{Binding Name}" Background="LightGreen" />
                    <TextBlock Grid.Row="1" Grid.Column="2" Text="{Binding Data}" Background="LightBlue" />
                </Grid>
            </DataTemplate>
        </Setter.Value>
    </Setter>
</Style>

如果这不起作用,您可能需要编辑DataGridColumnHeader的ControlTemplate。我不确定默认模板是什么样子的,但如果ContentPresenter不在可伸缩容器(如Grid)内,那么无论您如何拉伸ContentPresenter中的内容,它都不会被拉伸。但有一件事我非常确定,即使您告诉它们,StackPanels也不会被拉伸,因此首先在DataTemplate中尝试使用Grid。
更新(已修复)
好的,我找到了DataGridColumnHeader的默认ControlTemplate。结果发现它确实使用了Grid,所以我认为那并不是问题所在。
关键可能在于ContentPresenter:
<ContentPresenter
    SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
    VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
    HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" />

你可能只需要在你的DataGridColumnHeader样式中设置HorizontalContentAlignment="Stretch"

@Phillip,我认为你还需要更改ControlTemplate。请查看我的答案更新。 - devuxer
看起来你拿到的是行标头的控件模板,我会找到列标头的模板,根据你的指示验证我认为正确的解决方案。为什么不把可扩展面板设置为默认值呢? - Philipp Schmid
哦,你说得对。对此我很抱歉。看起来列标题使用的是网格(Grid),而不是堆栈面板(StackPanel)。请查看我的最新更新。 - devuxer
2
就是这样了!将以下内容添加到我的DataGridColumnHeader样式中:<Setter Property="HorizontalContentAlignment"> <Setter.Value>Stretch</Setter.Value> </Setter> 解决了问题(而且还很简单!)。感谢您的帮助和坚持! - Philipp Schmid

3

使用 WPF 4 DataGrid,但遇到了同样的问题,只需在 DataGridColumnHeader 样式中设置 HorizontalContentAlignment 就可以解决此问题(感谢以上建议的 DanM)...

<Style TargetType="{x:Type DataGridColumnHeader}">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
</Style>

0
请使用 Dock 面板。
<DockPanel Background="LightYellow">
   <TextBlock DockPanel.Dock="Left" Text="{Binding Name}" TextAlignment="Left" Background="LightGreen" />
   <TextBlock DockPanel.Dock="Right" Text="{Binding Data}" HorizontalAlignment="Right" TextAlignment="Right" Background="LightBlue" />
</DockPanel >

似乎没有任何区别。我已经为每个元素添加了DockPanel.Dock =“Top”,但当列的大小调整超出初始大小时,它们仍然无法跨越整个列宽度。是否还有其他属性需要设置才能正确扩展? - Philipp Schmid
抱歉,之前遗漏了 DockPanel.Dock="Left" 和 "Right",已经进行了编辑。 - Aran Mulholland
在我的真实示例中,我实际上有4个垂直堆叠的项,因此我为它们所有设置了DockPanel.Dock="Top"。但这并没有使它们水平扩展。 - Philipp Schmid
我认为真正的问题在于当用户扩展DataGridColumn时,DockPanel没有进行拉伸。因此,DockPanel内部的项目也不会拉伸(无论其停靠策略是左右还是上下)。 - Philipp Schmid
我不太明白,为什么设置DockPanel.Dock="Top"会使它们水平扩展?你必须将它们停靠在左侧和右侧。如果您在标题中放置一个按钮并且不设置大小,则它将填充以占用所有可用空间,对吗?我建议您使用KaXaml或xamlPad进行实验,因为我认为这是一个布局问题,与数据网格无关。我的建议(上面的代码)是否适用于只有两个元素? - Aran Mulholland
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接