【问题标题】:How does WPF DataGrid get frozen rows/columns working?WPF DataGrid 如何让冻结的行/列工作?
【发布时间】:2012-12-08 04:17:13
【问题描述】:

我创建了一个基于 Grid(不是 DataGrid)的用户控件,它被包装在一个 ScrollViewer 中。现在我想像在 DataGrid 中一样拥有冻结行/列的功能,但不知道如何。

谁能告诉我它是如何在 WPF DataGrid 中完成的?

【问题讨论】:

  • 冻结是什么意思?尺寸?
  • 像 DataGrid 或 Excel 一样冻结行/列,以便标题始终可见。更具体地说,顶部的列标题可以水平滚动,但不能垂直滚动;左侧的行标题可以垂直滚动,但不能水平滚动。

标签: wpf datagrid scrollviewer


【解决方案1】:

在我自己遇到这个问题后,我想分享我到目前为止发现的内容。

DataGrid 为此使用了两种不同的方法。


第一:RowHeader


这是DataGridRow 的简化版Template

<Border x:Name="DGR_Border" ... >
    <SelectiveScrollingGrid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>

        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>

        <DataGridRowHeader Grid.RowSpan="2"
            SelectiveScrollingGrid.SelectiveScrollingOrientation="Vertical" ... />

        <DataGridCellsPresenter Grid.Column="1" ... />

        <DataGridDetailsPresenter Grid.Column="1" Grid.Row="1"
            SelectiveScrollingGrid.SelectiveScrollingOrientation="{Binding RelativeSource={RelativeSource AncestorType={x:Type DataGrid}},
                                                                           Path=AreRowDetailsFrozen, Converter={x:Static DataGrid.RowDetailsScrollingConverter},
                                                                           ConverterParameter={x:Static SelectiveScrollingOrientation.Vertical}}" ... />
    </SelectiveScrollingGrid>
</Border>

如您所见,DataGrid 使用 SelectiveScrollingOrientation 附加属性将 RowHeader 保持在适当位置。如果设置(或更改)此属性,它会创建一个适应的 TranslateTransform 绑定到元素的父 ScrollViewer 偏移量。详情见source code


第二个:FrozenColumns


这件事发生在DataGridCellsPanelArrangeOverride()。它使用一个私有的ArrangeState 类“来维护多个孩子的排列之间的状态”。

private class ArrangeState
{
    public ArrangeState()
    {
        FrozenColumnCount = 0;
        ChildHeight = 0.0;
        NextFrozenCellStart = 0.0;
        NextNonFrozenCellStart = 0.0;
        ViewportStartX = 0.0;
        DataGridHorizontalScrollStartX = 0.0;
        OldClippedChild = null;
        NewClippedChild = null;
    }

    public int FrozenColumnCount { get; set; }
    public double ChildHeight { get; set; }
    public double NextFrozenCellStart { get; set; }
    public double NextNonFrozenCellStart { get; set; }
    public double ViewportStartX { get; set; } 
    public double DataGridHorizontalScrollStartX { get; set; }
    public UIElement OldClippedChild { get; set; }
    public UIElement NewClippedChild { get; set; }
} 

初始化状态后
private void InitializeArrangeState(ArrangeState arrangeState)
{
    DataGrid parentDataGrid = ParentDataGrid;
    double horizontalOffset = parentDataGrid.HorizontalScrollOffset;
    double cellsPanelOffset = parentDataGrid.CellsPanelHorizontalOffset;
    arrangeState.NextFrozenCellStart = horizontalOffset;
    arrangeState.NextNonFrozenCellStart -= cellsPanelOffset;
    arrangeState.ViewportStartX = horizontalOffset - cellsPanelOffset;
    arrangeState.FrozenColumnCount = parentDataGrid.FrozenColumnCount;
}

它调用

ArrangeChild(children[childIndex] as UIElement, i, arrangeState);

对于所有已实现的子项,并计算未实现的子项/列的估计宽度。

double childSize = GetColumnEstimatedMeasureWidth(column, averageColumnWidth);
arrangeState.NextNonFrozenCellStart += childSize;

最后将在DataGrid 的相应字段中设置值。

private void FinishArrange(ArrangeState arrangeState)
{
    DataGrid parentDataGrid = ParentDataGrid;

    // Update the NonFrozenColumnsViewportHorizontalOffset property of datagrid
    if (parentDataGrid != null)
    {
        parentDataGrid.NonFrozenColumnsViewportHorizontalOffset = arrangeState.DataGridHorizontalScrollStartX;
    }

    // Remove the clip on previous clipped child
    if (arrangeState.OldClippedChild != null)
    {
        arrangeState.OldClippedChild.CoerceValue(ClipProperty);
    }

    // Add the clip on new child to be clipped for the sake of frozen columns.
    _clippedChildForFrozenBehaviour = arrangeState.NewClippedChild;
    if (_clippedChildForFrozenBehaviour != null)
    {
        _clippedChildForFrozenBehaviour.CoerceValue(ClipProperty);
    }
}

ArrangeChild(UIElement child, int displayIndex, ArrangeState arrangeState) 的详细信息可以在source code 的第 1470 行找到。


结论


不是让列被冻结那么简单。即使这会起作用(除了整个宽度的剪辑和滚动条)

<ListView ItemsSource="some rows">
    <ListView.ItemTemplate>
        <DataTemplate>
            <Grid>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition />
                    <ColumnDefinition />
                </Grid.ColumnDefinitions>
                <TextBlock Grid.Column="0" Text="Fixed"
                           Background="LightBlue" Width="300"
                           SelectiveScrollingGrid.SelectiveScrollingOrientation="Vertical" />
                <TextBlock Grid.Column="1" Text="Scrolled"
                           Background="LightGreen" Width="300" />
            </Grid>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>

这不会:

<ScrollViewer HorizontalScrollBarVisibility="Auto">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <TextBlock Grid.Column="0" Text="Fixed"
                   Background="LightBlue" Width="300"
                   SelectiveScrollingGrid.SelectiveScrollingOrientation="Vertical" />
        <TextBlock Grid.Column="1" Text="Scrolled"
                   Background="LightGreen" Width="300" />                    
    </Grid>
</ScrollViewer>

原因是SelectiveScrollingOrientation attached property 中的DataGridHelper.FindVisualParent&lt;ScrollViewer&gt;(element)(参见souce code 中的第149 行)失败。也许您会找到解决方法,例如使用原始代码的副本创建您自己的附加属性,但按名称获取ScrollViewer。否则我认为你必须从头开始做很多事情。

【讨论】:

  • 好的,我看到了一些线索,但我无法让它为我工作。你能再给我一些提示吗?
【解决方案2】:

Datagrid Column and Row 有一个名为“Frozen”的属性

如果您想冻结列,我建议您执行以下操作

您希望它在选定的行或列事件上,然后在事件上获取列/行并将其标记为 Frozen = true

或在鼠标右键单击时创建另一个按钮或上下文菜单,您可以在其上冻结/解冻当前标记

列/行

希望对你有帮助

【讨论】:

  • 这无济于事,因为问题是别的。
  • 感谢您的意见,Sagar,但这不是我所要求的。
猜你喜欢
  • 2013-09-03
  • 2011-07-25
  • 1970-01-01
  • 2012-02-04
  • 2023-04-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多