C# WPF - 使 DataGrid 拉伸到可用大小,而不是扩展可用大小

C# WPF - Make DataGrid stretch to available size, not expand the available size

我使用 Visual Studio 2010。我一直在努力获取数据网格来填充现有的 space。如有必要,在数据网格上显示滚动条,而不是在页面上。

示例代码:

<Page x:Class="TestApp.Page1"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
  xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
  mc:Ignorable="d" 
  d:DesignHeight="300" d:DesignWidth="300"
Title="Page1">
<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="20"/>
        <ColumnDefinition Width="*"/>
        <ColumnDefinition Width="20"/>
    </Grid.ColumnDefinitions>
    <Grid.RowDefinitions>
        <RowDefinition Height="20"/>
        <RowDefinition Height="200"/>
        <RowDefinition Height="20"/>
    </Grid.RowDefinitions>
    <Expander x:Name="MyExpander"
              Grid.Column="1"
              Grid.Row="1"
              HorizontalAlignment="Stretch"
              VerticalAlignment="Top"
              Header="My Expander"
              Expanded="MyExpander_Expanded"
              Collapsed="MyExpander_Collapsed">
        <Viewbox Stretch="Uniform">
        <StackPanel>
            <DataGrid Margin="5"
                      Height="100"
                      HorizontalAlignment="Stretch"
                      VerticalAlignment="Stretch"
                      HorizontalScrollBarVisibility="Auto"
                      VerticalScrollBarVisibility="Auto">
            <DataGrid.Columns>
                <DataGridTextColumn Header="MyFirstColumn"/>
                <DataGridTextColumn Header="MySecondColumn"/>
                <DataGridTextColumn Header="MyThirdColumn"/>
                <DataGridTextColumn Header="MyFourthColumn"/>
                <DataGridTextColumn Header="MyFifthColumn"/>
                <DataGridTextColumn Header="MySixthColumn"/>
                <DataGridTextColumn Header="MySeventhColumn"/>
                <DataGridTextColumn Header="MyEighthColumn"/>
                <DataGridTextColumn Header="MyNinthColumn"/>
                <DataGridTextColumn Header="MyTenthColumn"/>
            </DataGrid.Columns>
        </DataGrid>
        <Button x:Name="DoSomething"
                Margin="5"
                Width="100"
                Height="30"
                Content="Do Something"
                Click="DoSomething_Click"/>
        </StackPanel>
        </Viewbox>
    </Expander>
</Grid>

问题是,似乎无论我做什么,数据网格都会一直伸展直到所有列都完全可见,即使它伸展超过 window(或页面)大小。 注意:我在右侧有一个面板,显示状态、日期、信息...,这些应该保留在屏幕上。

我尝试了太多东西,无法一一列举。我发现唯一可行的 'solution' 是在网格上添加另一个控件(不可见),如分隔符,并将扩展器宽度绑定到分隔符的宽度。它有效,但它是蹩脚的编程。

MaxWidth="{Binding ElementName=MySeparator, Path=ActualWidth}"

将堆栈面板的宽度绑定到列的(实际)宽度只会使堆栈面板消失。

有人可以帮助我吗?谢谢。

-- 更新-- 这就是我想要实现的目标:

看来如果我使用硬编码的列宽,就不会出现这个问题。仅当有一个列具有 star-length 时,数据网格才需要多少 space。

尝试像这样用 ScrollViewer 包裹你的 DataGrid

<ScrollViewer VerticalScrollBarVisibility="Auto"
              HorizontalScrollBarVisibility="Auto">
    <DataGrid>
        // SOME CONTENT
    </DataGrid>
</ScrollViewer>

此外,我完全不知道您是否需要 ViewBox。尝试删除它,看看它是否符合您的需求。

去掉 StackPanel:

<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="20"/>
        <ColumnDefinition Width="*"/>
        <ColumnDefinition Width="20"/>
    </Grid.ColumnDefinitions>
    <Grid.RowDefinitions>
        <RowDefinition Height="20"/>
        <RowDefinition Height="200"/>
        <RowDefinition Height="20"/>
    </Grid.RowDefinitions>
    <Expander x:Name="MyExpander"
              Grid.Column="1"
              Grid.Row="1"
              HorizontalAlignment="Stretch"
              VerticalAlignment="Top"
              Header="My Expander"
              Expanded="MyExpander_Expanded"
              Collapsed="MyExpander_Collapsed">
        <Viewbox Stretch="Uniform">
            <DataGrid Margin="5"
                      Height="100"
                      HorizontalAlignment="Stretch"
                      VerticalAlignment="Stretch"
                      HorizontalScrollBarVisibility="Auto"
                      VerticalScrollBarVisibility="Auto">
                <DataGrid.Columns>
                    <DataGridTextColumn Header="MyFirstColumn"/>
                    <DataGridTextColumn Header="MySecondColumn"/>
                    <DataGridTextColumn Header="MyThirdColumn"/>
                    <DataGridTextColumn Header="MyFourthColumn"/>
                    <DataGridTextColumn Header="MyFifthColumn"/>
                    <DataGridTextColumn Header="MySixthColumn"/>
                    <DataGridTextColumn Header="MySeventhColumn"/>
                    <DataGridTextColumn Header="MyEighthColumn"/>
                    <DataGridTextColumn Header="MyNinthColumn"/>
                    <DataGridTextColumn Header="MyTenthColumn"/>
                </DataGrid.Columns>
            </DataGrid>
            <Button x:Name="DoSomething"
                Margin="5"
                Width="100"
                Height="30"
                Content="Do Something"
                Click="DoSomething_Click"/>
        </Viewbox>
    </Expander>
</Grid>

StackPanelsScrollViewers 不能很好地协同工作,因为 StackPanel 测量其子项的方式:

并且由于 DataGrid 的默认控件模板包含一个 ScrollViewer,您无需添加自己的模板。

由于你的图表只解决了滚动问题,所以我现在主要关注它。我仍然不完全清楚您希望此布局如何显示 before 需要滚动,但让我们以此为起点。试一试,如果行为不是您想要的,请尝试尽可能具体地说明需要更改的内容(一张图片胜过一千个字)。

为简洁起见,我只包含 Expander 及其内容。祖先元素与原始元素完全相同 post.

<Expander x:Name="MyExpander"
          Grid.Column="1"
          Grid.Row="1"
          VerticalAlignment="Top"
          Header="My Expander"
          Expanded="MyExpander_Expanded"
          Collapsed="MyExpander_Collapsed">
  <StackPanel>
    <DataGrid Margin="5"
              xmlns:s="clr-namespace:System;assembly=mscorlib">
      <!-- Temporary items source to demonstrate grid measuring to fit rows -->
      <DataGrid.ItemsSource>
        <x:Array Type="s:String">
          <s:String>Dummy Row 1</s:String>
          <s:String>Dummy Row 2</s:String>
          <s:String>Dummy Row 3</s:String>
        </x:Array>
      </DataGrid.ItemsSource>
      <DataGrid.Columns>
        <DataGridTextColumn Header="MyFirstColumn" />
        <DataGridTextColumn Header="MySecondColumn" />
        <DataGridTextColumn Header="MyThirdColumn" />
        <DataGridTextColumn Header="MyFourthColumn" />
        <DataGridTextColumn Header="MyFifthColumn" />
        <DataGridTextColumn Header="MySixthColumn" />
        <DataGridTextColumn Header="MySeventhColumn" />
        <DataGridTextColumn Header="MyEighthColumn" />
        <DataGridTextColumn Header="MyNinthColumn" />
        <DataGridTextColumn Header="MyTenthColumn" />
      </DataGrid.Columns>
    </DataGrid>
    <Button x:Name="DoSomething"
            Margin="5"
            Width="100"
            Height="30"
            Content="Do Something"
            Click="DoSomething_Click" />
  </StackPanel>
</Expander>

这是两列的样子:

[

这是十列的样子:

更新

根据您对另一个答案的评论,您的页面在运行时加载到 ScrollViewer 中。如果 ScrollViewer 支持水平滚动,那么这就是问题的根源:它会根据需要为您的页面提供尽可能多的水平 space,而这种需求是由 DataGrid 驱动的.一个(hacky)解决方法是将您的 DataGrid 包裹在一个自定义装饰器中,该装饰器在测量时伪造其所需的宽度,然后在排列时填充任何可用的水平 space 。如果你想试试这个,使用我上面的例子,但是把 DataGrid 包装在装饰器里,像这样:

<l:ZeroWidthDecorator xmlns:l="clr-namespace:TestApp">
  <DataGrid><!-- ... --></DataGrid>
</l:ZeroWidthDecorator>

装饰器的代码如下:

public class ZeroWidthDecorator : Border
{
    private Size _lastSize;
    private Size _idealSize;

    protected override void OnVisualChildrenChanged(DependencyObject added, DependencyObject removed)
    {
        base.OnVisualChildrenChanged(added, removed);
        _idealSize = new Size();
        _lastSize = new Size();
    }

    protected override Size MeasureOverride(Size constraint)
    {
        var child = this.Child;
        if (child == null)
            return new Size();
        if (child.IsMeasureValid)
            child.Measure(new Size(_lastSize.Width, Math.Max(_lastSize.Height, constraint.Height)));
        else
            child.Measure(new Size(0d, constraint.Height));
        _idealSize = child.DesiredSize;
        return new Size(0d, _idealSize.Height);
    }

    protected override Size ArrangeOverride(Size arrangeSize)
    {
        var child = this.Child;
        if (child != null)
        {
            if (arrangeSize != _lastSize)
            {
                // Our parent will assume our measure is the same if the last
                // arrange bounds are still available, so force a reevaluation.
                this.InvalidateMeasure();
            }
            child.Arrange(new Rect(arrangeSize));
        }
        _lastSize = arrangeSize;
        return arrangeSize;
    }
}

我没有对此进行过广泛的测试,但我尝试了几种不同的方案,似乎 有效。 使用它需要您自担风险。

感谢大家的努力。该解决方案由 Mike Strobel 在评论中提出。由于我无法将评论标记为,因此我将在此处重复。

我有一个 window,包含一个滚动查看器,所有页面都放在上面。我从 window 中删除了滚动查看器并将滚动条放在页面上。

<Page x:Class=...>
    <ScrollViewer HorizontalScrollBarVisibility="Disabled"
                  VerticalScrollBarVisibility="Visible">
        <Grid>

这样真的解决了问题!谢谢迈克!

(如果你也运行遇到这个问题,请也给mike的评论点个赞)。