代码之家  ›  专栏  ›  技术社区  ›  Rob

WPF数据报:如何在单击单元格时停止自动滚动?

  •  24
  • Rob  · 技术社区  · 15 年前

    问题:
    如果我 DataGrid 不完全可见(显示水平和垂直滚动条),我单击其中一个部分可见的单元格,网格将自动滚动以显示该单元格。 我不想发生这种事 . 我试过和他玩 RequestBringIntoView ,如下所示:

    private void DataGrid_RequestBringIntoView(object sender, RequestBringIntoViewEventArgs e)
    {
        e.Handled = true;
    }
    

    但那没用。

    我尝试过的事情:

    • 我的单元格是自定义的 UserControls ;我尝试为 请求BringIntoView 在所有 用户控件 这就构成了我的牢房,并试图处理这件事,以为仅仅处理这件事可能还不够。 请求BringIntoView 数据网格 本身。这不起作用。
    • 托管 数据网格 里面 ScrollViewer 并处理了 滚动条 请求BringIntoView 事件。这实际上是可行的,并停止自动滚动行为,但在我的例子中,托管一个 数据网格 里面 滚动条 一点都不可取,所以我需要想出一个不同的解决方案。

    我不知道如何阻止这种行为,有什么想法吗?

    9 回复  |  直到 15 年前
        1
  •  7
  •   John Bowen    15 年前

    您可以通过修改模板来访问DataGrid的内部ScrollViewer。尽管通常不会将事件处理程序放在模板中的代码后面,但如果以内联方式声明模板,则可以像将其附加到DataGrid本身时那样处理事件处理程序。这是从Blend生成的默认模板,包括在ScrollViewer上为RequestBringintoView事件添加的处理程序:

    <ControlTemplate TargetType="{x:Type Controls:DataGrid}">
    <Border SnapsToDevicePixels="True" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Padding="{TemplateBinding Padding}">
        <ScrollViewer x:Name="DG_ScrollViewer" Focusable="False" RequestBringIntoView="DG_ScrollViewer_RequestBringIntoView">
            <ScrollViewer.Template>
                <ControlTemplate TargetType="{x:Type ScrollViewer}">
                    <Grid>
                        <Grid.RowDefinitions>
                            <RowDefinition Height="Auto"/>
                            <RowDefinition Height="*"/>
                            <RowDefinition Height="Auto"/>
                        </Grid.RowDefinitions>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="Auto"/>
                            <ColumnDefinition Width="*"/>
                            <ColumnDefinition Width="Auto"/>
                        </Grid.ColumnDefinitions>
                        <Button Width="{Binding CellsPanelHorizontalOffset, RelativeSource={RelativeSource FindAncestor, AncestorLevel=1, AncestorType={x:Type Controls:DataGrid}}}" Focusable="False">
                            <Button.Visibility>
                                <Binding Path="HeadersVisibility" RelativeSource="{RelativeSource FindAncestor, AncestorLevel=1, AncestorType={x:Type Controls:DataGrid}}">
                                    <Binding.ConverterParameter>
                                        <Controls:DataGridHeadersVisibility>All</Controls:DataGridHeadersVisibility>
                                    </Binding.ConverterParameter>
                                </Binding>
                            </Button.Visibility>
                            <Button.Template>
                                <ControlTemplate TargetType="{x:Type Button}">
                                    <Grid>
                                        <Rectangle x:Name="Border" Fill="{DynamicResource {x:Static SystemColors.ControlBrushKey}}" SnapsToDevicePixels="True"/>
                                        <Polygon x:Name="Arrow" Fill="Black" Stretch="Uniform" HorizontalAlignment="Right" Margin="8,8,3,3" VerticalAlignment="Bottom" Opacity="0.15" Points="0,10 10,10 10,0"/>
                                    </Grid>
                                    <ControlTemplate.Triggers>
                                        <Trigger Property="IsMouseOver" Value="True">
                                            <Setter Property="Stroke" TargetName="Border" Value="{DynamicResource {x:Static SystemColors.ControlDarkBrushKey}}"/>
                                        </Trigger>
                                        <Trigger Property="IsPressed" Value="True">
                                            <Setter Property="Fill" TargetName="Border" Value="{DynamicResource {x:Static SystemColors.ControlDarkBrushKey}}"/>
                                        </Trigger>
                                        <Trigger Property="IsEnabled" Value="False">
                                            <Setter Property="Visibility" TargetName="Arrow" Value="Collapsed"/>
                                        </Trigger>
                                    </ControlTemplate.Triggers>
                                </ControlTemplate>
                            </Button.Template>
                            <Button.Command>
                                <RoutedCommand/>
                            </Button.Command>
                        </Button>
                        <Custom:DataGridColumnHeadersPresenter x:Name="PART_ColumnHeadersPresenter" Grid.Column="1">
                            <Custom:DataGridColumnHeadersPresenter.Visibility>
                                <Binding Path="HeadersVisibility" RelativeSource="{RelativeSource FindAncestor, AncestorLevel=1, AncestorType={x:Type Controls:DataGrid}}">
                                    <Binding.ConverterParameter>
                                        <Controls:DataGridHeadersVisibility>Column</Controls:DataGridHeadersVisibility>
                                    </Binding.ConverterParameter>
                                </Binding>
                            </Custom:DataGridColumnHeadersPresenter.Visibility>
                        </Custom:DataGridColumnHeadersPresenter>
                        <ScrollContentPresenter x:Name="PART_ScrollContentPresenter" Grid.ColumnSpan="2" Grid.Row="1" Content="{TemplateBinding Content}" ContentStringFormat="{TemplateBinding ContentStringFormat}" ContentTemplate="{TemplateBinding ContentTemplate}" CanContentScroll="{TemplateBinding CanContentScroll}" CanHorizontallyScroll="False" CanVerticallyScroll="False"/>
                        <ScrollBar x:Name="PART_VerticalScrollBar" Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}" Grid.Column="2" Grid.Row="1" Maximum="{TemplateBinding ScrollableHeight}" Value="{Binding VerticalOffset, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}" Orientation="Vertical" ViewportSize="{TemplateBinding ViewportHeight}"/>
                        <Grid Grid.Column="1" Grid.Row="2">
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="{Binding NonFrozenColumnsViewportHorizontalOffset, RelativeSource={RelativeSource FindAncestor, AncestorLevel=1, AncestorType={x:Type Controls:DataGrid}}}"/>
                                <ColumnDefinition Width="*"/>
                            </Grid.ColumnDefinitions>
                            <ScrollBar x:Name="PART_HorizontalScrollBar" Visibility="{TemplateBinding ComputedHorizontalScrollBarVisibility}" Grid.Column="1" Maximum="{TemplateBinding ScrollableWidth}" Value="{Binding HorizontalOffset, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}" Orientation="Horizontal" ViewportSize="{TemplateBinding ViewportWidth}"/>
                        </Grid>
                    </Grid>
                </ControlTemplate>
            </ScrollViewer.Template>
            <ItemsPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
        </ScrollViewer>
    </Border>
    

        2
  •  27
  •   Kcvin    9 年前

    定义一个 EventSetter DataGrid.RowStyle 要调用阻止行进入视图的处理程序,请执行以下操作:

    XAML

    <DataGrid>
        <DataGrid.RowStyle>
            <Style TargetType="{x:Type DataGridRow}">  
                <EventSetter Event="Control.RequestBringIntoView" Handler="DataGrid_Documents_RequestBringIntoView"  />
            </Style>
        </DataGrid.RowStyle>
    </DataGrid>
    

    处理程序

    private void DataGrid_Documents_RequestBringIntoView(object sender, RequestBringIntoViewEventArgs e)
    {
        e.Handled = true;      
    }
    
        3
  •  9
  •   japf    15 年前

    我花了更多的时间来研究这个问题,因为我的第一个解决方案不起作用。

    然而,约翰的回答几乎是好的。诀窍是在RequestBringInToView事件到达ScrollViewer之前捕获它,以便标记它已处理过的事件。

    如果不需要优化整个模板,可以使用以下代码:

    var scp = TreeHelper.FindVisualChild<ScrollContentPresenter>(this.datagrid);
    scp.RequestBringIntoView += (s, e) => e.Handled = true;
    

    我们使用ScrollContentPresenter,因为它刚好位于可视化树中ScrollViewer的下方。

    希望这有帮助!

        4
  •  4
  •   Bartosz Wójtowicz    13 年前

    我也有同样的问题,简的回答帮助了我。唯一缺少的是ScrollContentPresenter只能在发生已加载事件后找到。我创建了一个从DataGrid继承的扩展的DataGrid类,它具有附加的AutoScroll属性来控制网格是否自动滚动。

    这是课程:

    using System.Windows;
    using System.Windows.Controls;
    using Microsoft.Windows.Controls;
    
    namespace Bartosz.Wojtowicz.Wpf
    {
        public class ExtendedDataGrid : DataGrid
        {
            public bool AutoScroll { get; set; }
    
            public ExtendedDataGrid()
            {
                AutoScroll = true;
                Loaded += OnLoaded;
            }
    
            private void OnLoaded(object sender, RoutedEventArgs eventArgs)
            {
                if (!AutoScroll)
                {
                    ScrollContentPresenter scp = DataGridHelper.GetVisualChild<ScrollContentPresenter>(this);
                    if (scp != null) scp.RequestBringIntoView += OnRequestBringIntoView;
                }
            }
    
            private static void OnRequestBringIntoView(object sender, RequestBringIntoViewEventArgs e)
            {
                e.Handled = true;
            }
        }
    }
    

    以下是您如何使用它:

       <local:ExtendedDataGrid AutoScroll="False">
            <!-- your grid definition -->
       </local:ExtendedDataGrid>
    
        5
  •  3
  •   DaveK    8 年前

    我和鲁米特有同样的问题,但找到了解决办法。

    我想如果我能找到区分鼠标点击和箭头键的方法,那么我就可以相应地设置e.handled。

    经过一些实验,我发现e.originalsource根据鼠标或箭头键而改变。对于鼠标单击,将调用一次RequestBringintoView的处理程序,E.OriginalSource的类型为DataGridCell。对于箭头键,处理程序被调用两次,E.OriginalSource的类型是DataGridRow,然后是DataGridCell。

    我的处理程序的代码是:

    e.Handled = (e.OriginalSource is DataGridCell);
    

    这看起来有点像黑客,但对我很有用。

        6
  •  1
  •   japf    15 年前

    我不确定这是否有效,但这里有一个基于一些调查的想法,是在使用reflector的数据报源代码中进行的:

    1/创建继承DataGridCellsPanel的类。这是数据报内部用于排列单元格的面板。

    2/用空方法重写BringIndexIntoView方法(不调用基方法)

    3/在XAML中设置itemspanelTemplate属性:

    <tk:DataGrid>
        <tk:DataGrid.ItemsPanel>
            <ItemsPanelTemplate>
                <local:DataGridCellsPanelNoAutoScroll />
            </ItemsPanelTemplate>
        </tk:DataGrid.ItemsPanel>
    </tk:DataGrid>
    

    似乎在发生mousedown事件时,有时会调用面板的bringIndexIntoView方法来执行自动滚动。用一个“不行动”代替它可能会起作用。

    我没时间测试这个解决方案,请告诉我们它是否有效。

        7
  •  0
  •   Barton    11 年前

    以下是我的工作(在尝试了迄今为止所有不太复杂的“答案”之后):

        <DataGrid Grid.Column="0" Grid.Row="1"
                  Name="ListItemContainerDataGrid"
                  ScrollViewer.VerticalScrollBarVisibility="Visible"
                  ScrollViewer.CanContentScroll="False"
                  And.Others
                  ItemsSource="{Binding Path=ListItemModels}"
                  >
        </DataGrid>
    

    scrollViewer.canContentScroll=“false”似乎令人难以置信地违反直觉…

        8
  •  0
  •   l33t    6 年前

    L33 T方式:

    static App()
    {
        EventManager.RegisterClassHandler(typeof(ScrollContentPresenter), 
            FrameworkElement.RequestBringIntoViewEvent,
            new RoutedEventHandler(OnRequestBringIntoView));
    }
    
    private static void OnRequestBringIntoView(object sender, RoutedEventArgs e)
    {
        e.Handled = true;
    }
    

    注意这个 能够 干扰第三方控制。

        9
  •  -1
  •   Ali    9 年前

    因为wpf博士回答了一个类似的问题 here 这个 RequestBringIntoView应该在itemspanel中处理。