7

基本上,我想让 WPF DataGrid 控件的列布局与 WinForms DataGridView 完全一样

更具体地说,这是我正在寻找的行为:

  • 网格控件应该占用它给定的空间(即,无论它的父控件中有多少空间可供它使用)。这里我指的是控件,而不是列。

  • 创建的列(无论是自动还是手动)可能会也可能不会占用所有这些空间。

  • 如果在创建列后剩余空间,则不应扩展最后一列以填充该空间

  • 如果在创建列后还有剩余空间,则不应创建一个没有任何内容的空列来填充该额外空间

据我所知,在 WPF 中,最后两个要点似乎是相互排斥的,您必须选择其中一个。有没有人找到办法做到这两点?我已经搜索了很多并且没有找到我正在寻找的东西,但是我发现的所有帖子往往都是几年前的,所以我希望现在有人已经弄清楚了这件事。

编辑:sa_ddam213,这是我整理的一个快速 xaml 项目来测试您的建议。

<Window x:Class="DataGridFix.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:DataGridFix"
        Title="MainWindow" Height="350" Width="525">

    <Window.Resources>
        <ObjectDataProvider x:Key="data"
                    ObjectType="{x:Type local:TestObject}"
                    MethodName="GetTestData" />
    </Window.Resources>
    <StackPanel>
        <DataGrid HorizontalAlignment="Left"  ColumnWidth="Auto" Height="150" VerticalAlignment="Top" ItemsSource="{Binding Source={StaticResource data}}" />
    </StackPanel>
</Window>

这是背后的代码:

命名空间 DataGridFix { public class TestObject { public int Id { get; 放; } 公共字符串名称 { 获取;放; }

    public static List<TestObject> GetTestData()
    {
        var items = new List<TestObject>();

        items.Add(new TestObject() { Id = 1, Name = "Joe" });
        items.Add(new TestObject() { Id = 2, Name = "Matt" });
        items.Add(new TestObject() { Id = 3, Name = "Hal" });

        return items;
    }
}

}

我从您的建议中看到的唯一值得注意的是将 Horizo​​ntalAlignment 设置为 Left。我这样做了,并尝试将 ColumnWidth 设置为各种设置,但每个设置都有相同的问题(当然除了 * ......从技术上讲,我可以把那个弄乱,但我不会进入那个)。

如果您使用鼠标扩展任何列,然后减小列大小,则会出现空填充列。我从您的帖子中注意到的唯一其他区别是您将 DataGrids 放在 StackPanel 中,因为您有多个。我只是为了见鬼而尝试过,但结果相同。如果您发现我所做的与您的建议之间有任何其他区别,请告诉我。

4

2 回答 2

7

为了获得您想要的行为,您可能必须修改DataGrid.

看一下代码。我已经非常接近DataGridView我认为的 WinForms 外观。

要删除额外的列,您必须从DataGridColumnHeadersPresenter. 我刚刚评论了它。其余的只是默认模板。

另一个修改是对模板的DataGrid.
By 设置HorizontalAlignment="Left"ScrollContentPresenter行不再占据控件的所有宽度。

这些是我对默认模板所做的仅有的 2 处更改。

在此处输入图像描述

<Style TargetType="{x:Type DataGridColumnHeadersPresenter}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type DataGridColumnHeadersPresenter}">
                <Grid>
                    <!-- Remove this filler column -->
                    <!--<DataGridColumnHeader x:Name="PART_FillerColumnHeader" IsHitTestVisible="False" />-->
                    <ItemsPresenter />
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

<Style TargetType="{x:Type DataGrid}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type DataGrid}">
                <Border Background="{TemplateBinding Background}"
                                BorderBrush="{TemplateBinding BorderBrush}"
                                BorderThickness="{TemplateBinding BorderThickness}"
                                SnapsToDevicePixels="True"
                                Padding="{TemplateBinding Padding}">
                    <ScrollViewer Focusable="false" Name="DG_ScrollViewer">
                        <ScrollViewer.Template>
                            <ControlTemplate TargetType="{x:Type ScrollViewer}">
                                <Grid>
                                    <Grid.RowDefinitions>
                                        <RowDefinition Height="Auto"/>
                                        <RowDefinition Height="*"/>
                                        <RowDefinition Height="Auto"/>
                                    </Grid.RowDefinitions>

                                    <Grid.ColumnDefinitions>
                                        <ColumnDefinition Width="Auto"/>
                                        <ColumnDefinition Width="*"/>
                                        <ColumnDefinition Width="Auto"/>
                                    </Grid.ColumnDefinitions>

                                    <Button Command="{x:Static DataGrid.SelectAllCommand}"
                                                    Width="{Binding RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}, Path=CellsPanelHorizontalOffset}"
                                                    Style="{DynamicResource {ComponentResourceKey TypeInTargetAssembly={x:Type DataGrid}, ResourceId=DataGridSelectAllButtonStyle}}"
                                                    Focusable="false"
                                                    Visibility="{Binding RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}, Path=HeadersVisibility, Converter={x:Static DataGrid.HeadersVisibilityConverter}, ConverterParameter={x:Static DataGridHeadersVisibility.All}}" />

                                    <DataGridColumnHeadersPresenter Grid.Column="1" Name="PART_ColumnHeadersPresenter"
                                                    Visibility="{Binding RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}, Path=HeadersVisibility, Converter={x:Static DataGrid.HeadersVisibilityConverter}, ConverterParameter={x:Static DataGridHeadersVisibility.Column}}"/>

                                    <!-- Set HorizontalAlignment="Left" to have the rows only take up the width they need and not fill the entire width of the DataGrid -->
                                    <ScrollContentPresenter HorizontalAlignment="Left" x:Name="PART_ScrollContentPresenter" Grid.Row="1" Grid.ColumnSpan="2" CanContentScroll="{TemplateBinding CanContentScroll}" />

                                    <ScrollBar Grid.Row="1" Grid.Column="2" Name="PART_VerticalScrollBar"
                                                        Orientation="Vertical"
                                                        Maximum="{TemplateBinding ScrollableHeight}"
                                                        ViewportSize="{TemplateBinding ViewportHeight}"
                                                        Value="{Binding Path=VerticalOffset, RelativeSource={RelativeSource TemplatedParent}, Mode=OneWay}"
                                                        Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}"/>

                                    <Grid Grid.Row="2" Grid.Column="1">
                                        <Grid.ColumnDefinitions>
                                            <ColumnDefinition Width="{Binding RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}, Path=NonFrozenColumnsViewportHorizontalOffset}"/>
                                            <ColumnDefinition Width="*"/>
                                        </Grid.ColumnDefinitions>
                                        <ScrollBar Grid.Column="1"
                                                    Name="PART_HorizontalScrollBar"
                                                    Orientation="Horizontal"
                                                    Maximum="{TemplateBinding ScrollableWidth}"
                                                    ViewportSize="{TemplateBinding ViewportWidth}"
                                                    Value="{Binding Path=HorizontalOffset, RelativeSource={RelativeSource TemplatedParent}, Mode=OneWay}"
                                                    Visibility="{TemplateBinding ComputedHorizontalScrollBarVisibility}"/>
                                    </Grid>
                                </Grid>
                            </ControlTemplate>
                        </ScrollViewer.Template>
                        <ItemsPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
                    </ScrollViewer>
                </Border>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

更新
.NET 4 和 .NET 4.5 之间确实存在差异。
我在装有 Visual Studio 2012 的 Windows 8 机器上进行开发,因此作为测试,我尝试以 .NET 4 为目标,看看我是否可以复制错误的行为。但它仍然工作得很好。

可以肯定的是,我尝试在仅安装了 .NET 4 的另一台机器上运行该应用程序,当使列变大然后再次变小时,这里出现了空行。

问题似乎DataGridRows是行为不正常。在仅安装 .NET 4 的机器上运行时,它们会在缩小列时保持当前大小。在 .NET 4.5 上,它们按预期调整大小。

获得所需行为的新解决方案实际上比以前的解决方案简单得多。

通过简单地将 设置为并HorizontalAlignment删除填充列,它可以在 .NET 4 和 .NET 4.5 上运行。并且不再需要更换整个模板。DataGridRowsleftDataGrid

<Style TargetType="DataGridRow">
    <Setter Property="HorizontalAlignment" Value="Left" />
</Style>
<Style TargetType="DataGridColumnHeadersPresenter">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type DataGridColumnHeadersPresenter}">
                <Grid>
                    <ItemsPresenter />
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>
于 2013-01-02T01:25:57.230 回答
5

WPF 中的 Columns 有很多布局选项,只需选择要显示的内容即可。

  • 像素
  • 大小到单元格
  • SizeToHeader
  • 汽车
  • 成比例的(*)

如果您将 设置HorizontalAlignmentLeftDataGrid根据ColumnWidth您选择的内容调整大小以适应其内容。

这是一些可用列设置的示例

   <StackPanel>
        <DataGrid HorizontalAlignment="Left"  ColumnWidth="100" Height="64" VerticalAlignment="Top" ItemsSource="{Binding ElementName=UI, Path=GridItems}" />
        <DataGrid HorizontalAlignment="Left"  ColumnWidth="SizeToCells" Height="64" VerticalAlignment="Top" ItemsSource="{Binding ElementName=UI, Path=GridItems}" />
        <DataGrid HorizontalAlignment="Left"  ColumnWidth="SizeToHeader" Height="64" VerticalAlignment="Top" ItemsSource="{Binding ElementName=UI, Path=GridItems}" />
        <DataGrid HorizontalAlignment="Left"  ColumnWidth="Auto" Height="64" VerticalAlignment="Top" ItemsSource="{Binding ElementName=UI, Path=GridItems}" />
        <DataGrid HorizontalAlignment="Left"  ColumnWidth="*" Height="64" VerticalAlignment="Top" ItemsSource="{Binding ElementName=UI, Path=GridItems}" />
    </StackPanel>

在此处输入图像描述

于 2012-12-30T10:28:29.267 回答