2016-12-28 1 views
10

J'ai un DataGrid avec de nombreux éléments et j'ai besoin de faire défiler par programme le SelectedItem. J'ai cherché sur Google et StackOverflow, et il semble que la solution est ScrollIntoView, comme suit:Faites défiler le contrôle DataGrid WPF pour afficher l'élément sélectionné au-dessus

grid.ScrollIntoView(grid.SelectedItem) 

qui fait défiler la DataGrid vers le haut ou vers le bas jusqu'à ce que l'élément sélectionné est mis au point. Toutefois, en fonction de la position de défilement actuelle par rapport à l'élément sélectionné, l'élément sélectionné peut être le dernier élément visible dans le ScrollViewer du DataGrid. Je veux que l'élément sélectionné soit le premier élément visible dans ScrollViewer (en supposant qu'il y ait suffisamment de lignes dans le DataGrid pour permettre cela). J'ai donc essayé ceci:

'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) 

D'abord, je faites défiler jusqu'à la fin de la DataGrid et seulement alors que je fais défiler la SelectedItem, à quel point le SelectedItem est affiché en haut de la grille de données.

Mon problème est que le défilement jusqu'à la fin du DataGrid fonctionne bien, mais le défilement subséquent vers l'élément sélectionné ne fonctionne pas toujours.

Comment puis-je résoudre ce problème ou existe-t-il d'autres stratégies pour faire défiler vers un enregistrement spécifique dans la première position?

Répondre

3

La réponse acceptée à this other question montre une approche différente pour obtenir la première/dernière ligne visible d'une telle grille. Vous pouvez trouver l'index de votre ligne et y faire défiler directement ou faire défiler ligne par ligne jusqu'à ce que la première rangée apparaisse.

5

Vous étiez sur la bonne voie, essayez simplement de travailler avec la vue de collection au lieu de travailler directement sur la grille de données pour ce genre de besoins.

Voici un exemple de travail où l'élément désiré est toujours affiché comme premier élément sélectionné si possible, sinon le scrollviewer défile jusqu'à la fin et l'élément cible est sélectionné à sa position.

Les points clés sont les suivants:

  • Utilisez CollectionView du côté des entreprises et permettent au défilement cible « réelle » synch élément en cours sur le contrôle XAML (IsSynchronizedWithCurrentItem=true)
  • Différer afin de permettre la « Sélectionner dernier élément » à visualy exécuté (en utilisant un Dispatcher.BeginInvoke avec une faible priorité)

Voici la logique métier (Ceci est convertion automatique de 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 

Et c'est le 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> 
3

J'ai résolu cette question avec le code suivant:

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>