WPF DataGrid 如何让冻结的行/列工作? [英] How does WPF DataGrid get frozen rows/columns working?

查看:23
本文介绍了WPF DataGrid 如何让冻结的行/列工作?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I created a user control based on Grid (not DataGrid), which is wrapped in a ScrollViewer. Now I would like to have frozen rows/columns capability just like in DataGrid, but couldn't figure out how.

有人可以告诉我在 WPF DataGrid 中它是如何完成的吗?

Can somebody give me some insight how it is done in WPF DataGrid?

推荐答案

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

After having this problem by myself I want to share what I've found out so far.

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

DataGrid uses two different methods for that.

第一: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 偏移量.请参阅源代码.

As you can see DataGrid uses the SelectiveScrollingOrientation attached property to hold the RowHeader in position. If this property is set (or changing) it creates an adapted TranslateTransform bound to the parental ScrollViewer Offset for the element. See the details in source code.

第二:FrozenColumns

这些内容发生在 DataGridCellsPanel ArrangeOverride().它使用一个私有的 ArrangeState 类在多个孩子的排列之间维护状态".

This stuff takes place in DataGridCellsPanel ArrangeOverride(). It uses a private ArrangeState class "to maintain state between arrange of multiple children".

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);

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

for all realized childs and calculates the estimated width for non realized childs/columns.

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

最后,这些值将设置在 DataGrid 中的相应字段中.

At the end the values will be set in the appropriate fields in 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) 的详细信息,您可以在 源代码.

The details for ArrangeChild(UIElement child, int displayIndex, ArrangeState arrangeState) you can find from line 1470 in source code.

结论

这不是让列冻结那么简单.即使这会起作用(除了在整个宽度上剪切和滚动条)

It's not as simple making columns are frozen. Even though this will work (apart from clipping and scrollbar over whole width)

<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>

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

The reason is that DataGridHelper.FindVisualParent<ScrollViewer>(element) (see from line 149 in souce code) in SelectiveScrollingOrientation attached property fails. Maybe you find workarounds e.g. create your own attached property with a copy of the original code but get the ScrollViewer by name. Otherwise I think you have to do many things from scratch.

这篇关于WPF DataGrid 如何让冻结的行/列工作?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆