如何在WPF中修改DataGrid垂直滚动条的位置?

6
已解决:请查看下面的XAML代码、截图和说明。

这个问题有点难以描述,让我解释一下我的问题。我有一个定义了高度的数据网格,因此出现了滚动条。我想将垂直滚动条限制在不包括标题的区域内。虽然它只滚动数据行而不是标题,但在视觉上它覆盖了整个数据网格区域右侧。问题在于滚动条区域会出现两个框(一个在上面,一个在下面)。我不知道如何摆脱它们或如何将滚动条限制在数据网格的正文中。

enter image description here

我唯一能想到的方法(但我不喜欢它的外观)是将DataGrid的背景设置为透明。这是结果:

enter image description here

正如您所见,滚动条很烦人地突出。此外,如果背景透明,还存在水平滚动条和最后一行之间存在间隙的问题:

enter image description here

还有一种解决方案可以去掉这两个框的数据网格背景颜色,使它们看起来不那么突出:

http://social.msdn.microsoft.com/Forums/vstudio/en-US/9fc4252b-38b1-4369-8d76-b6c5ae1e4df5/how-to-remove-the-blank-space-above-the-verticalscroolbar-of-datagrid-in-wpf?forum=wpf

类似的解决方案可以在这里找到:Annoying Square Where Scrollbars Meet

然而,它并没有解决滚动条突兀地伸出侧边的问题。

我尝试过的方法是将头部与正文分开,并将正文放入垂直ScrollViewer中,然后将头部和正文放入水平ScrollViewer中,以便它们都可以水平滚动。但是,正如你所想象的那样,这并不起作用,因为您必须向右滚动才能看到垂直滚动条。我相信有一种方法可以使其保持在右侧冻结,但我还没有想出来。另一个问题是头部宽度必须匹配该列中任何单元格的最大可能宽度,否则所有内容都会偏移。当向右滚动到底时,显示的结果如下:

enter image description here

我对控件模板非常陌生,因此无法确定是否可行,因为我找不到正确的组件:
如果我给垂直滚动条一个负边距到左侧(比如-6,0,0,0),并在单元格块的右侧添加类似大小的填充(0,0,6,0),则垂直滚动条技术上应该向左移动。我将继续尝试并努力解决这个问题,除非有人能够为我提供答案(那将是很棒的)。
编辑#1:
好吧,我取得了一些进展,并能够将滚动条的边距设置为(-17,0,0,0)。17似乎是滚动条的宽度。它似乎有一个特定的键来设置其宽度:

http://msdn.microsoft.com/en-us/library/system.windows.systemparameters.verticalscrollbarwidthkey(v=vs.110).aspx

但我想不出在XAML中将其作为边距的负偏移值插入的方法。在代码后台中这并不难...但我更愿意保持全部使用XAML。无论如何,这是进展的截图和XAML代码部分:

enter image description here

这是XAML代码部分:
    <Style TargetType="{x:Type DataGrid}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type DataGrid}">
                    <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="*"/>
                                            <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}}}"/>
                                        <ScrollContentPresenter Margin="0,0,17,0" x:Name="PART_ScrollContentPresenter" CanContentScroll="{TemplateBinding CanContentScroll}" Grid.ColumnSpan="2" Grid.Row="1"/>
                                        <ScrollBar Margin="-17,0,0,0" x:Name="PART_VerticalScrollBar" Grid.Column="2" Maximum="{TemplateBinding ScrollableHeight}" Orientation="Vertical" Grid.Row="1" Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}" Value="{Binding VerticalOffset, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}" ViewportSize="{TemplateBinding ViewportHeight}"/>
                                        <Grid Grid.Column="1" Grid.Row="2">
                                            <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>

重要的部分是代码片段中间设置ScrollBar的Margin属性为"-17,0,0,0",这使得变化成为可能。
我目前的问题是我无法确定哪个组件需要向左偏移17个单位(通过添加margin或padding)。我已经尝试了所有的组件,但还没有成功。如果有人在我之前找到了解决办法,我会尽快更新。现在,滚动条将覆盖最后一列中的任何内容,直到我修复偏移量。
编辑#2:
请参考上面更新的XAML代码。我所做的是将Margin属性添加到ScrollContentPresenter中,值为"0,0,17,0"。

一个副作用,我完全可以接受,就是它也会抵消掉标题,但你只有在向右滚动到底部时才能看到。它只影响最后一列的标题,因为ScrollContentPresenter也会偏移它…奇怪的是,有一个DataGridColumnHeadersPresenter,但它是独立工作的…所以,我会继续努力解决这个问题。不幸的是,ScrollContentPresenter没有填充,这将像魔法一样发挥作用。所以,现在我必须弄清楚如何填充它,而不是设置边距,或者找出一个不同的方法。

类似的方法是将水平滚动条(第二个网格中的滚动条)的边距设置为0,0,-17,0。它会将其移动17个单位,然后将DataGridColumnHeaderPresenter的边距设置为0,0,-17,0。它会将其移动17个单位向右。

EDIT #3:

这里还有另一种方法,供感兴趣的人参考:

enter image description here

将垂直滚动条的边距设置为0,-22,0,-17。-22是我的标题高度,所以根据您的情况进行调整。此方法可以拉伸滚动条以覆盖两个白色框。
编辑#4:
我找到了解决方案。请查看我的答案中的XAML代码、屏幕截图和说明。
3个回答

8

以上,我尝试了几种方法,但没有达到完美的效果。这里是我非常满意的解决方案,希望能帮助其他人。我使用了Blend来逐行查看模板代码,这是为我的目的而找到的最佳组合。

最终结果:

enter image description here

抱歉标题有点乱,我是有意遮盖它们的。如您所见,纵向滚动条在标题和底部的横向滚动条之间对齐得很好。通过将垂直滚动条(位于模板的第一个网格中)的边距设置为左侧的-17,Margin="-17,0,0,0",我完成了此操作。然后,我将ScrollContentProperty的边距设置为右侧的17,该属性位于垂直滚动条上方的模板代码中,Margin="0,0,17,0,以确保垂直滚动条不会覆盖任何内容。这样,当我们滚动到极限右侧时,标题的右侧留下了17个单位的空白空间,这是由于ScrollContentPresenter上的17个边距也影响了标题。为了解决这个问题,我为DataGridColumnHeader创建了一个样式,并将其右侧的边距设置为-17,Margin="0,0,-17,0",确保它延伸到空白空间中。为了确保最后一列标题文本不会悬停在垂直滚动条上,从而使视觉效果不佳,我在右侧添加了17个填充,Padding="0,0,17,0"

以下是使此操作成为可能的DataGridDataGridColumnHeader的XAML代码:

DataGrid:

    <Style TargetType="{x:Type DataGrid}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type DataGrid}">
                    <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="*"/>
                                            <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}}}"/>
                                        <!--This is the scroll content presenter that gets shifted to the left 17 units so that scrollbar doesn't cover it-->
                                        <ScrollContentPresenter Margin="0,0,17,0" x:Name="PART_ScrollContentPresenter" CanContentScroll="{TemplateBinding CanContentScroll}" Grid.ColumnSpan="2" Grid.Row="1"/>
                                        <!--This is the vertical scrollbar. Margin is used to shift it to the left 17 units over the content-->
                                        <ScrollBar Margin="-17,0,0,0" x:Name="PART_VerticalScrollBar" Grid.Column="2" Maximum="{TemplateBinding ScrollableHeight}" Orientation="Vertical" Grid.Row="1" Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}" Value="{Binding VerticalOffset, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}" ViewportSize="{TemplateBinding ViewportHeight}"/>
                                        <Grid Grid.Column="1" Grid.Row="2">
                                            <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>

DataGridColumnHeader:

    <Style TargetType="{x:Type DataGridColumnHeader}">
        <Setter Property="Height" Value="22"/>
        <!--Padding to shift header text of the last column to the left 17 units-->
        <Setter Property="Padding" Value="0,0,17,0"/>
        <!--Margin to shift the entire header to the right 17 units to fill the void-->
        <Setter Property="Margin" Value="0,0,-17,0"/>
    </Style>

一个类似的方法是将水平滚动条的边距设置为-17,Margin="0,0,-17,0",将DataGridColumnHeader的填充设置为17,Padding="0,0,17,0",并将其边距设置为-17,Margin="0,0,-17,0"。这可以达到相同的效果。


我已经使用你的代码进行了工作,但是在DataGrid的右侧存在空白(如空白空间),而项目却在里面。当滚动条不可见时,它看起来像是为滚动条保留了位置,当DataGrid中有足够的项目需要滚动时,滚动条才会出现并填充该位置。你是否遇到过同样的问题? - Roxy'Pro
@Roxy'Pro 不,但我的解决方案非常静态,所以我确定会出现基于分辨率等问题。这些天我不会那样解决它(当时我刚开始学习编程)。 - B.K.
1
这个人很棒。 - Ramankingdom

2
更好的解决方案是为 Name="PART_VerticalScrollBar" 设置 Grid.Row="0" Grid.RowSpan="2"

不一定更好...因为那不是我想要的效果。我不希望它一直延伸到顶部并替换标题部分。不过,我会调整标题的“ColumnSpan”,这样就不必去调整边距属性了。当时我发帖时还很新手,所以现在我会用不同的方式来处理这些问题。 - B.K.
这正是我一直在寻找的解决方案。谢谢Maxim! :) - Kirk Woll

0
这里的问题是DataGrid将控件网格分成了3行3列。就像这样:

                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="Auto"/>
                                <ColumnDefinition Width="*"/>
                                <ColumnDefinition Width="Auto"/>
                            </Grid.ColumnDefinitions>
                            <Grid.RowDefinitions>
                                <RowDefinition Height="Auto"/>
                                <RowDefinition Height="*"/>
                                <RowDefinition Height="Auto"/>
                            </Grid.RowDefinitions>

并且滚动条位于第二行的第三列。


<ScrollBar x:Name="PART_VerticalScrollBar" Grid.Column="2" Maximum="{TemplateBinding ScrollableHeight}" Orientation="Vertical" Grid.Row="1" Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}" Value="{Binding VerticalOffset, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}" ViewportSize="{TemplateBinding ViewportHeight}"/>

由于第三行的高度是自动的,并且第三列中没有任何内容放置在第一行中,因此滚动条占据了整个空间。

我使用默认模板并在第三列的第一行内放置了白色边框。 解决方案:

    <ControlTemplate x:Key="DataGridControlTemplate1" TargetType="{x:Type DataGrid}">
        <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="*"/>
                                <RowDefinition Height="Auto"/>
                            </Grid.RowDefinitions>
                            <Button Command="ApplicationCommands.SelectAll" Focusable="False" Style="{DynamicResource {ComponentResourceKey ResourceId=DataGridSelectAllButtonStyle, TypeInTargetAssembly={x:Type DataGrid}}}" Width="{Binding CellsPanelHorizontalOffset, RelativeSource={RelativeSource FindAncestor, AncestorLevel=1, AncestorType={x:Type DataGrid}}}">
                                <Button.Visibility>
                                    <Binding Path="HeadersVisibility" RelativeSource="{RelativeSource FindAncestor, AncestorLevel=1, AncestorType={x:Type DataGrid}}">
                                        <Binding.ConverterParameter>
                                            <DataGridHeadersVisibility>All</DataGridHeadersVisibility>
                                        </Binding.ConverterParameter>
                                    </Binding>
                                </Button.Visibility>
                            </Button>
                            <DataGridColumnHeadersPresenter x:Name="PART_ColumnHeadersPresenter" Grid.Column="1">
                                <DataGridColumnHeadersPresenter.Visibility>
                                    <Binding Path="HeadersVisibility" RelativeSource="{RelativeSource FindAncestor, AncestorLevel=1, AncestorType={x:Type DataGrid}}">
                                        <Binding.ConverterParameter>
                                            <DataGridHeadersVisibility>Column</DataGridHeadersVisibility>
                                        </Binding.ConverterParameter>
                                    </Binding>
                                </DataGridColumnHeadersPresenter.Visibility>
                            </DataGridColumnHeadersPresenter>
                            <Border Background="White" BorderThickness="0" Grid.Column="2" />
                            <ScrollContentPresenter x:Name="PART_ScrollContentPresenter" CanContentScroll="{TemplateBinding CanContentScroll}" CanHorizontallyScroll="False" Grid.ColumnSpan="2" CanVerticallyScroll="False" ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" ContentStringFormat="{TemplateBinding ContentStringFormat}" Grid.Row="1"/>
                            <ScrollBar x:Name="PART_VerticalScrollBar" Grid.Column="2" Maximum="{TemplateBinding ScrollableHeight}" Orientation="Vertical" Grid.Row="1" Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}" Value="{Binding VerticalOffset, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}" ViewportSize="{TemplateBinding ViewportHeight}"/>
                            <Grid Grid.Column="1" Grid.Row="2">
                                <Grid.ColumnDefinitions>
                                    <ColumnDefinition Width="{Binding NonFrozenColumnsViewportHorizontalOffset, RelativeSource={RelativeSource FindAncestor, AncestorLevel=1, 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>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接