Sau khi có vấn đề này một mình tôi muốn chia sẻ những gì tôi đã phát hiện cho đến nay.
DataGrid
sử dụng hai phương pháp khác nhau cho điều đó.
đầu tiên: RowHeader
Đây là đơn giản Template
cho 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>
Như bạn thấy DataGrid
sử dụng SelectiveScrollingOrientation
tài sản gắn liền với giữ RowHeader trong Chức vụ. Nếu thuộc tính này được thiết lập (hoặc thay đổi), nó sẽ tạo ra một điều chỉnh TranslateTransform
được điều chỉnh cho phụ huynh ScrollViewer
Bù đắp cho phần tử. Xem chi tiết trong source code.
Thứ hai: Các FrozenColumns
thứ này diễn ra trong DataGridCellsPanel
ArrangeOverride()
. Nó sử dụng một lớp học ArrangeState
riêng "để duy trì trạng thái giữa việc sắp xếp nhiều trẻ em".
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; }
}
Sau khi khởi tạo trạng thái với
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;
}
nó gọi
ArrangeChild(children[childIndex] as UIElement, i, arrangeState);
cho tất cả Childs nhận và tính toán chiều rộng ước tính cho những người không nhận ra Childs/cột.
double childSize = GetColumnEstimatedMeasureWidth(column, averageColumnWidth);
arrangeState.NextNonFrozenCellStart += childSize;
Khi kết thúc, giá trị sẽ được đặt trong các trường thích hợp trong 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);
}
}
Chi tiết cho ArrangeChild(UIElement child, int displayIndex, ArrangeState arrangeState)
bạn có thể tìm thấy từ dòng 1470 trong source code.
Kết luận
Nó không cột làm cũng đơn giản được đông lạnh. Mặc dù điều này sẽ làm việc (ngoài cắt và cuộn qua toàn bộ chiều rộng)
<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>
này sẽ không:
<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>
Lý do là DataGridHelper.FindVisualParent<ScrollViewer>(element)
(xem từ dòng 149 trong souce code) trong SelectiveScrollingOrientation attached property
thất bại. Có thể bạn tìm giải pháp thay thế, ví dụ: tạo thuộc tính đính kèm của riêng bạn với một bản sao của mã gốc nhưng nhận được ScrollViewer
theo tên. Nếu không tôi nghĩ bạn phải làm nhiều thứ từ đầu.
Bạn có ý nghĩa gì khi được cố định? Kích cỡ? – Joe
Hàng/cột được cố định như trong DataGrid hoặc Excel, để các tiêu đề luôn hiển thị. Cụ thể hơn, tiêu đề cột ở trên cùng có thể cuộn theo chiều ngang, nhưng không được theo chiều dọc; tiêu đề hàng bên trái có thể cuộn theo chiều dọc nhưng không theo chiều ngang. – newman