简体   繁体   中英

Static/Freeze DataGridRow at the top of DataGrid for WPF

I wanted to create a custom DataGrid with static/frozen row at the top, similar to Excel's freeze row functionality, no matter how you scroll, the static row will always remain up top. I've came across Freeze DataGrid Row but noticed there weren't a complete solution. The static row also need to allow re-orderable columns like all other rows.

Here are my attempt on creating the custom DataGrid. I've found the area where my static row will display at. space between column headers and scroll content presenter

I've tried to use a DataGridRow control but was unable to get it displayed/working. Maybe I've misunderstood how this control is used and this is the wrong approach?

Wrong DataGridRow attempt

<DataGridRow Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="2" Item="{Binding StaticRow, RelativeSource={RelativeSource AncestorType={x:Type local:CustomDataGrid}}}" />

CustomDataGrid.xaml

I have a textblock placeholder between DataGridColumnHeadersPresenter and ScrollContentPresenter

<Style TargetType="{x:Type local:CustomDataGrid}">
    <Setter Property="Background" Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"/>
    <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
    <Setter Property="BorderBrush" Value="#FF688CAF"/>
    <Setter Property="BorderThickness" Value="1"/>
    <Setter Property="RowDetailsVisibilityMode" Value="VisibleWhenSelected"/>
    <Setter Property="ScrollViewer.CanContentScroll" Value="true"/>
    <Setter Property="ScrollViewer.PanningMode" Value="Both"/>
    <Setter Property="Stylus.IsFlicksEnabled" Value="False"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type local:CustomDataGrid}">
                <Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Padding="{TemplateBinding Padding}" SnapsToDevicePixels="True">
                    <ScrollViewer x:Name="DG_ScrollViewer" Focusable="false">
                        <ScrollViewer.Template>
                            <ControlTemplate TargetType="{x:Type ScrollViewer}">
                                <Grid>
                                    <Grid.ColumnDefinitions>
                                        <ColumnDefinition Width="Auto"/>
                                        <ColumnDefinition Width="*"/>
                                        <ColumnDefinition Width="Auto"/>
                                    </Grid.ColumnDefinitions>
                                    <Grid.RowDefinitions>
                                        <RowDefinition Height="Auto"/>
                                        <RowDefinition Height="Auto"/>
                                        <RowDefinition Height="*"/>
                                        <RowDefinition Height="Auto"/>
                                    </Grid.RowDefinitions>
                                    <Button Command="{x:Static DataGrid.SelectAllCommand}" Focusable="false" Style="{DynamicResource {ComponentResourceKey ResourceId=DataGridSelectAllButtonStyle, TypeInTargetAssembly={x:Type DataGrid}}}" Visibility="{Binding HeadersVisibility, ConverterParameter={x:Static DataGridHeadersVisibility.All}, Converter={x:Static DataGrid.HeadersVisibilityConverter}, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}" Width="{Binding CellsPanelHorizontalOffset, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}"/>
                                    <DataGridColumnHeadersPresenter x:Name="PART_ColumnHeadersPresenter" Grid.Column="1" Visibility="{Binding HeadersVisibility, ConverterParameter={x:Static DataGridHeadersVisibility.Column}, Converter={x:Static DataGrid.HeadersVisibilityConverter}, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}"/>

                                    <TextBlock Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="2">Static Row Here</TextBlock>

                                    <ScrollContentPresenter x:Name="PART_ScrollContentPresenter" CanContentScroll="{TemplateBinding CanContentScroll}" Grid.ColumnSpan="2" Grid.Row="2"/>
                                    <ScrollBar x:Name="PART_VerticalScrollBar" Grid.Column="2" Maximum="{TemplateBinding ScrollableHeight}" Orientation="Vertical" Grid.Row="2" Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}" Value="{Binding VerticalOffset, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}" ViewportSize="{TemplateBinding ViewportHeight}"/>
                                    <Grid Grid.Column="1" Grid.Row="3">
                                        <Grid.ColumnDefinitions>
                                            <ColumnDefinition Width="{Binding NonFrozenColumnsViewportHorizontalOffset, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}"/>
                                            <ColumnDefinition Width="*"/>
                                        </Grid.ColumnDefinitions>
                                        <ScrollBar x:Name="PART_HorizontalScrollBar" Grid.Column="1" Maximum="{TemplateBinding ScrollableWidth}" Orientation="Horizontal" Visibility="{TemplateBinding ComputedHorizontalScrollBarVisibility}" Value="{Binding HorizontalOffset, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}" ViewportSize="{TemplateBinding ViewportWidth}"/>
                                    </Grid>
                                </Grid>
                            </ControlTemplate>
                        </ScrollViewer.Template>
                        <ItemsPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
                    </ScrollViewer>
                </Border>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
    <Style.Triggers>
        <MultiTrigger>
            <MultiTrigger.Conditions>
                <Condition Property="IsGrouping" Value="true"/>
                <Condition Property="VirtualizingPanel.IsVirtualizingWhenGrouping" Value="false"/>
            </MultiTrigger.Conditions>
            <Setter Property="ScrollViewer.CanContentScroll" Value="false"/>
        </MultiTrigger>
    </Style.Triggers>
</Style>

CustomDataGrid.cs

public class CustomDataGrid : DataGrid
{
    static CustomDataGrid()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(CustomDataGrid), new FrameworkPropertyMetadata(typeof(CustomDataGrid)));
    }

    public static readonly DependencyProperty StaticRowProperty = DependencyProperty.Register(
        "StaticRow", typeof(object), typeof(CustomDataGrid), new PropertyMetadata(default(object)));

    public object StaticRow
    {
        get { return GetValue(StaticRowProperty); }
        set { SetValue(StaticRowProperty, value); }
    }
}

There are a few ways you could approach this. One is to have two datagrids, one above the other. Hide the column headers of the second. The top presents your fixed row and the second the ones you want to scroll.

There are a couple of complications here. You are likely to have to bind column widths. If you allow the user to sort columns then you need to make clicking the header in your frozen datagrid sort the collection in the second one which presents the non-frozen rows. You also get no scroller on the top row. The benefits of this approach are that you're using datagrids straight out the box and not changing fairly complicated code with the potential for unwanted side effects.

The other option is to take a look at Vincent Sibal's approach and maybe do some work on that. " Note: The purpose of the sample is for learning purposes only. Please do not use this in production code as it is not fully tested. " https://blogs.msdn.microsoft.com/vinsibal/2008/10/31/wpf-datagrid-frozen-row-sample/

If you can persuade this to work reliably, that looks like it would be a more elegant solution.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM