WPF DataGrid如何使冻结的行/列工作? [英] How does WPF DataGrid get frozen rows/columns working?
问题描述
我创建了一个基于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.RowDetailsScrollingConverter},
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;
DataGridHorizontalScrollStartX = 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 DataGridHorizontalScrollStartX {get;组; }
public UIElement OldClippedChild {get;组; }
public UIElement NewClippedChild {get;组;
}
使用
$ b初始化状态后$ b
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]作为UIElement,i,arrangeState);
为所有实现的孩子计算未实现的孩子/列的估计宽度。
double childSize = GetColumnEstimatedMeasureWidth(column,averageColumnWidth);
arrangeState.NextNonFrozenCellStart + = childSize;
最后,这些值将在 DataGrid
。
private void FinishArrange(ArrangeState arrangeState)
{
DataGrid parentDataGrid = ParentDataGrid;
//更新datagrid
的NonFrozenColumnsViewportHorizontalOffset属性if(parentDataGrid!= null)
{
parentDataGrid.NonFrozenColumnsViewportHorizontalOffset = arrangeState.DataGridHorizontalScrollStartX;
}
//删除前一个剪切子节点上的剪辑
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 HorizontalScrollBarVisibility =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屋!