Static/Freeze WPF DataGrid 顶部的 DataGridRow
Static/Freeze DataGridRow at the top of DataGrid for WPF
我想创建一个顶部有 static/frozen 行的自定义 DataGrid,类似于 Excel 的冻结行功能,无论您如何滚动,静态行将始终保持在顶部.我遇到过 Freeze DataGrid Row,但发现没有完整的解决方案。静态行也需要像所有其他行一样允许可重新排序的列。
这是我创建自定义 DataGrid 的尝试。我找到了我的静态行将显示的区域。 space between column headers and scroll content presenter
我尝试使用 DataGridRow 控件,但无法获取它 displayed/working。也许我误解了这个控件的使用方式,这是错误的方法?
错误的 DataGridRow 尝试
<DataGridRow Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="2" Item="{Binding StaticRow, RelativeSource={RelativeSource AncestorType={x:Type local:CustomDataGrid}}}" />
CustomDataGrid.xaml
我在 DataGridColumnHeadersPresenter 和 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); }
}
}
有几种方法可以解决这个问题。
一种是有两个数据网格,一个在另一个上面。隐藏第二个的第 header 列。
顶部显示您的固定行,第二个显示您要滚动的行。
这里有几个问题。
您可能必须绑定列宽。
如果您允许用户对列进行排序,那么您需要单击冻结数据网格中的 header 对第二个显示 non-frozen 行的 collection 进行排序。
您也不会在第一行看到滚动条。
这种方法的好处是您可以直接使用数据网格,而不是更改可能产生不良副作用的相当复杂的代码。
另一种选择是查看 Vincent Sibal 的方法,并可能对此做一些工作。
“注意:示例的目的仅供学习。请不要在生产代码中使用它,因为它没有经过全面测试。”
https://blogs.msdn.microsoft.com/vinsibal/2008/10/31/wpf-datagrid-frozen-row-sample/
如果你能说服它可靠地工作,那看起来会是一个更优雅的解决方案。
我想创建一个顶部有 static/frozen 行的自定义 DataGrid,类似于 Excel 的冻结行功能,无论您如何滚动,静态行将始终保持在顶部.我遇到过 Freeze DataGrid Row,但发现没有完整的解决方案。静态行也需要像所有其他行一样允许可重新排序的列。
这是我创建自定义 DataGrid 的尝试。我找到了我的静态行将显示的区域。 space between column headers and scroll content presenter
我尝试使用 DataGridRow 控件,但无法获取它 displayed/working。也许我误解了这个控件的使用方式,这是错误的方法?
错误的 DataGridRow 尝试
<DataGridRow Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="2" Item="{Binding StaticRow, RelativeSource={RelativeSource AncestorType={x:Type local:CustomDataGrid}}}" />
CustomDataGrid.xaml
我在 DataGridColumnHeadersPresenter 和 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); }
}
}
有几种方法可以解决这个问题。 一种是有两个数据网格,一个在另一个上面。隐藏第二个的第 header 列。 顶部显示您的固定行,第二个显示您要滚动的行。
这里有几个问题。 您可能必须绑定列宽。 如果您允许用户对列进行排序,那么您需要单击冻结数据网格中的 header 对第二个显示 non-frozen 行的 collection 进行排序。 您也不会在第一行看到滚动条。 这种方法的好处是您可以直接使用数据网格,而不是更改可能产生不良副作用的相当复杂的代码。
另一种选择是查看 Vincent Sibal 的方法,并可能对此做一些工作。 “注意:示例的目的仅供学习。请不要在生产代码中使用它,因为它没有经过全面测试。” https://blogs.msdn.microsoft.com/vinsibal/2008/10/31/wpf-datagrid-frozen-row-sample/
如果你能说服它可靠地工作,那看起来会是一个更优雅的解决方案。