WPF DataGrid 如何让冻结的行/列工作? [英] How does WPF DataGrid get frozen rows/columns working?
问题描述
我创建了一个基于 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
(参见 源代码) 在 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屋!