【问题标题】:Scroll WPF DataGrid to show selected item on top滚动 WPF DataGrid 以在顶部显示所选项目
【发布时间】:2017-05-12 19:36:44
【问题描述】:

我有一个包含许多项目的 DataGrid,我需要以编程方式滚动到 SelectedItem。我在 StackOverflow 和 Google 上搜索过,似乎解决方法是 ScrollIntoView,如下:

grid.ScrollIntoView(grid.SelectedItem)

向上或向下滚动 DataGrid 直到选中的项目成为焦点。但是,根据相对于所选项目的当前滚动位置,所选项目可能最终成为 DataGrid 的 ScrollViewer 中的最后一个可见项目。我希望所选项目将成为 ScrollViewer 中的第一个可见项目(假设 DataGrid 中有足够的行允许这样做)。所以我尝试了这个:

'FindVisualChild is a custom extension method that searches in the visual tree and returns 
'the first element of the specified type
Dim sv = grid.FindVisualChild(Of ScrollViewer)
If sv IsNot Nothing Then sv.ScrollToEnd()
grid.ScrollIntoView(grid.SelectedItem)

首先我滚动到 DataGrid 的末尾,然后才滚动到 SelectedItem,此时 SelectedItem 显示在 DataGrid 的顶部。

我的问题是滚动到 DataGrid 的末尾效果很好,但随后滚动到所选项目并不总是有效。

我该如何解决这个问题,或者是否有任何其他替代策略可以滚动到顶部位置的特定记录?

【问题讨论】:

    标签: wpf vb.net datagrid scrollviewer


    【解决方案1】:

    您在正确的轨道上,只是尝试使用集合视图而不是直接在数据网格上工作以满足这种需求。

    这是一个工作示例,如果可能,所需项目始终显示为第一个选定项目,否则滚动查看器将滚动到末尾并在其位置选择目标项目。

    重点是:

    • 在业务端使用 CollectionView 并在 XAML 控件上启用当前项目同步 (IsSynchronizedWithCurrentItem=true)
    • 推迟“真正的”目标滚动,以便可视地执行“选择最后一项”(通过使用低优先级的Dispatcher.BeginInvoke

    这是业务逻辑(这是从C#到VB的自动转换)

    Public Class Foo
    
        Public Property FooNumber As Integer
            Get
            End Get
            Set
            End Set
        End Property
    End Class
    
    Public Class MainWindow
        Inherits Window
        Implements INotifyPropertyChanged
    
        Private _myCollectionView As ICollectionView
    
        Public Sub New()
            MyBase.New
            DataContext = Me
            InitializeComponent
            MyCollection = New ObservableCollection(Of Foo)
            MyCollectionView = CollectionViewSource.GetDefaultView(MyCollection)
            Dim i As Integer = 0
            Do While (i < 50)
                MyCollection.Add(New Foo)
                i = (i + 1)
            Loop
    
        End Sub
    
        Public Property MyCollectionView As ICollectionView
            Get
                Return Me._myCollectionView
            End Get
            Set
                Me._myCollectionView = value
                Me.OnPropertyChanged("MyCollectionView")
            End Set
        End Property
    
        Private Property MyCollection As ObservableCollection(Of Foo)
            Get
            End Get
            Set
            End Set
        End Property
    
        Private Sub ButtonBase_OnClick(ByVal sender As Object, ByVal e As RoutedEventArgs)
            Dim targetNum As Integer = Convert.ToInt32(targetScroll.Text)
            Dim targetObj As Foo = Me.MyCollection.FirstOrDefault(() => {  }, (r.FooNumber = targetNum))
    
            'THIS IS WHERE THE MAGIC HAPPENS
            If (Not (targetObj) Is Nothing) Then
                'Move to the collection view to the last item
                Me.MyCollectionView.MoveCurrentToLast
                'Bring this last item into the view
                Dim current = Me.MyCollectionView.CurrentItem
                itemsContainer.ScrollIntoView(current)
                'This is the trick : Invoking the real target item select with a low priority allows previous visual change (scroll to the last item) to be executed
                Dispatcher.BeginInvoke(DispatcherPriority.ContextIdle, New Action(() => {  }, Me.ScrollToTarget(targetObj)))
            End If
    
        End Sub
    
        Private Sub ScrollToTarget(ByVal targetObj As Foo)
            Me.MyCollectionView.MoveCurrentTo(targetObj)
            itemsContainer.ScrollIntoView(targetObj)
        End Sub
    
        Public Event PropertyChanged As PropertyChangedEventHandler
    
        Protected Overridable Sub OnPropertyChanged(ByVal propertyName As String)
            If (Not (PropertyChanged) Is Nothing) Then
                PropertyChanged?.Invoke(Me, New PropertyChangedEventArgs(propertyName))
            End If
    
        End Sub
    End Class
    

    这就是 xaml

     <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <DataGrid x:Name="itemsContainer" ItemsSource="{Binding MyCollectionView}" IsSynchronizedWithCurrentItem="True"  Margin="2" AutoGenerateColumns="False" >
            <DataGrid.Columns>
                <DataGridTextColumn Binding="{Binding FooNumber}"></DataGridTextColumn>
            </DataGrid.Columns>
        </DataGrid>
    
        <StackPanel Grid.Column="1">
            <TextBox x:Name="targetScroll" Text="2" Margin="2"></TextBox>
            <Button Content="Scroll To item" Click="ButtonBase_OnClick" Margin="2"></Button>
        </StackPanel>
    </Grid>
    

    【讨论】:

      【解决方案2】:

      我用以下代码解决了这个问题:

      public partial class MainWindow:Window
      {
          private ObservableCollection<Product> products=new ObservableCollection<Product> ();
      
          public MainWindow()
          {
              InitializeComponent ();
      
              for (int i = 0;i < 50;i++)
              {
                  Product p=new Product { Name="Product "+i.ToString () };
                  products.Add (p);
              }
      
              lstProduct.ItemsSource=products;
          }
      
          private void lstProduct_SelectionChanged(object sender,SelectionChangedEventArgs e)
          {
              products.Move (lstProduct.SelectedIndex,0);
              lstProduct.ScrollIntoView (lstProduct.SelectedItem);
          }
      }
      
      public class Product
      {
          public string Name { get; set; }
      }
      
      
      <Grid>
          <ListBox Name="lstProduct" Margin="20" DisplayMemberPath="Name" SelectionChanged="lstProduct_SelectionChanged" />
      </Grid>
      

      【讨论】:

      • 这对我很有帮助,但根据 MS 文档,Move() 实际上重新定位了集合中的对象,这不是我想要的;即,我需要行的顺序保持不变。但感谢有关 ScrollIntoView() 的建议。
      【解决方案3】:

      this other question 接受的答案显示了获取此类网格的第一/最后可见行的不同方法。 您可以找出行的索引并直接滚动到那里或逐行向下滚动,直到第一个可见行匹配。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-03-22
        • 2012-06-14
        • 2011-11-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-11-12
        • 1970-01-01
        相关资源
        最近更新 更多