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

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

问题描述

我创建了一个基于Grid(而不是DataGrid)的用户控件,它包装在一个ScrollViewer中。现在我想像DataGrid一样冻结了行/列的功能,但无法弄清楚如何。



有人可以给我一些洞察,如何在WPF DataGrid中完成?

解决方案


DataGrid 使用两种不同的方法。






首先:RowHeader






这是$ code的简化模板 DataGridRow :

 < 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 =1Grid.Row =1
SelectiveScrollingGrid.SelectiveScrollingOrientation ={Binding RelativeSource = {RelativeSource AncestorType = {x:Type DataGrid}},
Path = AreRowDetailsFrozen,Converter = {x:Static DataGrid.RowDetailsS​​crollingConverter},
ConverterParameter = {x:Static SelectiveScrollingOrientation.Vertical}}... />
< / SelectiveScrollingGrid>
< / Border>

如您所见, DataGrid a href =http://msdn.microsoft.com/en-us/library/system.windows.controls.primitives.selectivescrollinggrid.selectivescrollingorientation.aspx =nofollow> SelectiveScrollingOrientation 附加属性来保持RowHeader的位置。如果此属性设置(或更改),则会为元素的父项 ScrollViewer 偏移量创建一个适应的 TranslateTransform 。请参阅 源代码






第二:FrozenColumns






这个东西发生在 DataGridCellsPanel ArrangeOverride()。它使用一个私人的 ArrangeState class来保持多个孩子之间的状态。

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

public int FrozenColumnCount {get;组; }
public double ChildHeight {get;组; }
public double NextFrozenCellStart {get;组; }
public double NextNonFrozenCellStart {get;组; }
public double ViewportStartX {get;组; }
public double DataGridHorizo​​ntalScrollStartX {get;组; }
public UIElement OldClippedChild {get;组; }
public UIElement NewClippedChild {get;组;
}

使用


$ b初始化状态后$ b

  private void InitializeArrangeState(ArrangeState arrangeState)
{
DataGrid parentDataGrid = ParentDataGrid;
double horizo​​ntalOffset = parentDataGrid.Horizo​​ntalScrollOffset;
double cellsPanelOffset = parentDataGrid.CellsPanelHorizo​​ntalOffset;
arrangeState.NextFrozenCellStart = horizo​​ntalOffset;
arrangeState.NextNonFrozenCellStart - = cellsPanelOffset;
arrangeState.ViewportStartX = horizo​​ntalOffset - cellsPanelOffset;
arrangeState.FrozenColumnCount = parentDataGrid.FrozenColumnCount;
}

它调用

 将ArrangeChild(children [childIndex]作为UIElement,i,arrangeState); 

为所有实现的孩子计算未实现的孩子/列的估计宽度。

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

最后,这些值将在 DataGrid

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

//更新datagrid
的NonFrozenColumnsViewportHorizo​​ntalOffset属性if(parentDataGrid!= null)
{
parentDataGrid.NonFrozenColumnsViewportHorizo​​ntalOffset = arrangeState.DataGridHorizo​​ntalScrollStartX;
}

//删除前一个剪切子节点上的剪辑
if(arrangeState.OldClippedChild!= null)
{
arrangeState.OldClippedChild.CoerceValue ClipProperty);
}

//为了冻结列添加要修剪的新小孩的剪辑。
_clippedChildForFrozenBehaviour = arrangeState.NewClippedChild;
if(_clippedChildForFrozenBehaviour!= null)
{
_clippedChildForFrozenBehaviour.CoerceValue(ClipProperty);
}
}

ArrangeChild的详细信息UIElement child,int displayIndex,ArrangeState arrangeState)您可以从 源代码 >。






结论



< >

它不是简单的制作列被冻结。即使这样会有效(除了剪切和滚动条在整个宽度上)

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

这不会:

 < ScrollViewer Horizo​​ntalScrollBarVisibility =Auto> 
< Grid>
< Grid.ColumnDefinitions>
< ColumnDefinition />
< ColumnDefinition />
< /Grid.ColumnDefinitions>
< TextBlock Grid.Column =0Text =Fixed
Background =LightBlueWidth =300
SelectiveScrollingGrid.SelectiveScrollingOrientation =Vertical/>
< TextBlock Grid.Column =1Text =Scrolled
Background =LightGreenWidth =300/>
< / Grid>
< / ScrollViewer>

原因是 DataGridHelper.FindVisualParent&ScrollViewer>(element) code>(参见 SelectiveScrollingOrientation附加属性中的src / Framework / System / Windows / Controls / DataGridHelper @ cs / 1305600 / DataGridHelper @ csrel =nofollow> souce代码 失败。也许你找到解决方法,例如使用原始代码的副本创建自己的附加属性,但按名称获取 ScrollViewer 。否则我认为你必须从头开始做许多事情。


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.

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 uses two different methods for that.


First: The RowHeader


This is the simplified Template for DataGridRow:

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

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.


Second: The FrozenColumns


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

After initializing the state with

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

it calls

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;

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

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


Conclusion


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>

this will not:

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

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天全站免登陆