7

ScrollViewer にラップされた (DataGrid ではなく) Grid に基づくユーザー コントロールを作成しました。DataGrid と同じように行/列を固定したいのですが、方法がわかりませんでした。

WPF DataGrid でそれがどのように行われるか、誰かが私に洞察を与えることができますか?

4

2 に答える 2

5

この問題を自分で抱えた後、これまでに発見したことを共有したいと思います。

DataGridそのために2つの異なる方法を使用します。


最初: RowHeader


これは の簡略化Templateです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>

ご覧のとおりDataGrid、添付プロパティを使用しSelectiveScrollingOrientationて RowHeader を所定の位置に保持します。このプロパティが設定されている (または変更されている) 場合、要素TranslateTransformの親ScrollViewerOffset に適合した境界が作成されます。ソース コードの詳細を参照してください。


2 番目: FrozenColumns


このようなものはで行われます。「複数の子の配置間で状態を維持する」ためにプライベートクラスを使用します。DataGridCellsPanel ArrangeOverride()ArrangeState

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

すべての実現された子について、実現されていない子/列の推定幅を計算します。

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

最後に、 の適切なフィールドに値が設定されます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)の 1470 行目から確認できます。


結論


列を凍結するのは簡単ではありません。これは機能しますが(全幅のクリッピングとスクロールバーを除く)

<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<ScrollViewer>(element)の 149 行目から参照) inが失敗するためです。おそらく、元のコードのコピーを使用して独自の添付プロパティを作成し、名前で取得するなどの回避策を見つけることができます 。そうでなければ、ゼロから多くのことをしなければならないと思います。SelectiveScrollingOrientation attached propertyScrollViewer

于 2012-12-08T14:04:36.533 に答える
0

Datagrid の列と行には、「凍結」というプロパティがあります。

列を固定したい場合は、次のことをお勧めします

選択した行または列イベントでそれを必要とし、次にイベントで列/行を取得し、それを Frozen = true としてマークします

またはマウスの右クリックで別のボタンまたはコンテキストメニューを作成し、現在マークされているものをフリーズ/フリーズ解除します

列行

お役に立てれば

于 2012-12-08T05:32:33.123 に答える