Comment faire défiler automatiquement la grid de données WPF

Je pense que je suis stupide. J’ai cherché pendant 15 minutes et ai trouvé plusieurs solutions différentes pour faire défiler des datagrids, mais aucune ne semble fonctionner pour moi.

J’utilise WPF avec .NET 3.5 et le WPF Toolkit DataGrid. Ma grid est mise à jour lorsque ma collection observable change et fonctionne parfaitement. Maintenant, mon DataGrid est situé dans une grid normale et des barres de défilement apparaissent si le DataGrid devient trop gros. Aussi très bien…

Et maintenant vient la question de 1.000.000 $:

Comment faire pour que la grid de données défile jusqu’à la dernière ligne? Il y a:

  • no AutoScroll Property
  • aucun index CurrentRowSelected
  • un CurrentCell, mais aucune collection que je pourrais utiliser pour CurrentCell = AllCells.Last

Des idées? Je me sens vraiment stupide et il semble étrange que cette question soit si difficile. Qu’est-ce que je rate?

😉

if (mainDataGrid.Items.Count > 0) { var border = VisualTreeHelper.GetChild(mainDataGrid, 0) as Decorator; if (border != null) { var scroll = border.Child as ScrollViewer; if (scroll != null) scroll.ScrollToEnd(); } } 

Vous devriez utiliser la méthode datagrid

 datagrid.ScrollIntoView(itemInRow); 

ou

 datagrid.ScrollIntoView(itemInRow, column); 

De cette façon, vous ne devez pas chercher la visionneuse de défilement, etc.

J’ai écrit une propriété attachée pour autoscroll de grid:

 using System; using System.Collections.Generic; using System.Collections.Specialized; using System.Windows; using System.Windows.Controls; public static class DataGridBehavior { public static readonly DependencyProperty AutoscrollProperty = DependencyProperty.RegisterAttached( "Autoscroll", typeof(bool), typeof(DataGridBehavior), new PropertyMetadata(default(bool), AutoscrollChangedCallback)); private static readonly Dictionary handlersDict = new Dictionary(); private static void AutoscrollChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs args) { var dataGrid = dependencyObject as DataGrid; if (dataGrid == null) { throw new InvalidOperationException("Dependency object is not DataGrid."); } if ((bool)args.NewValue) { Subscribe(dataGrid); dataGrid.Unloaded += DataGridOnUnloaded; dataGrid.Loaded += DataGridOnLoaded; } else { Unsubscribe(dataGrid); dataGrid.Unloaded -= DataGridOnUnloaded; dataGrid.Loaded -= DataGridOnLoaded; } } private static void Subscribe(DataGrid dataGrid) { var handler = new NotifyCollectionChangedEventHandler((sender, eventArgs) => ScrollToEnd(dataGrid)); handlersDict.Add(dataGrid, handler); ((INotifyCollectionChanged)dataGrid.Items).CollectionChanged += handler; ScrollToEnd(dataGrid); } private static void Unsubscribe(DataGrid dataGrid) { NotifyCollectionChangedEventHandler handler; handlersDict.TryGetValue(dataGrid, out handler); if (handler == null) { return; } ((INotifyCollectionChanged)dataGrid.Items).CollectionChanged -= handler; handlersDict.Remove(dataGrid); } private static void DataGridOnLoaded(object sender, RoutedEventArgs routedEventArgs) { var dataGrid = (DataGrid)sender; if (GetAutoscroll(dataGrid)) { Subscribe(dataGrid); } } private static void DataGridOnUnloaded(object sender, RoutedEventArgs routedEventArgs) { var dataGrid = (DataGrid)sender; if (GetAutoscroll(dataGrid)) { Unsubscribe(dataGrid); } } private static void ScrollToEnd(DataGrid datagrid) { if (datagrid.Items.Count == 0) { return; } datagrid.ScrollIntoView(datagrid.Items[datagrid.Items.Count - 1]); } public static void SetAutoscroll(DependencyObject element, bool value) { element.SetValue(AutoscrollProperty, value); } public static bool GetAutoscroll(DependencyObject element) { return (bool)element.GetValue(AutoscrollProperty); } } 

Usage:

   
 listbox.Add(foo); listbox.SelectedIndex = count - 1; listbox.ScrollIntoView(listbox.SelectedItem); listbox.SelectedIndex = -1; 

Je sais que c’est une réponse tardive, mais je n’ai trouvé que LE moyen le plus facile de faire défiler les informations jusqu’au bas d’un DataGrid. dans l’événement DataContextChanged , mettez ceci dans:

 myDataGrid.ScrollIntoView(CollectionView.NewItemPlaceholder); 

Facile hein?

C’est pourquoi cela fonctionne: sur chaque grid de données, il existe un emplacement au bas du DataGrid où vous pouvez append un nouvel élément à votre liste auquel il est lié. C’est un CollectionView.NewItemPlaceholder , et il n’y en aura qu’un seul dans votre DataGrid. Donc, vous pouvez simplement faire défiler jusqu’à cela.

Pour avoir ajouté un élément AutoScroll jusqu’au dernier:

 YourDataGrid.ScrollIntoView(YourDataGrid.Items.GetItemAt(YourDataGrid.Items.Count-1)); 

Peut cette aide 🙂

si données volumineuses datagrid.ScrollIntoView (itemInRow, column); fonctionne pas bien alors nous devons utiliser ci-dessous un seul:

 if (mainDataGrid.Items.Count > 0) { var border = VisualTreeHelper.GetChild(mainDataGrid, 0) as Decorator; if (border != null) { var scroll = border.Child as ScrollViewer; if (scroll != null) scroll.ScrollToEnd(); } } 

J’ai constaté que le moyen le plus simple de procéder consiste à appeler la méthode ScrollIntoView à partir de l’événement attaché ScrollViewer.ScrollChanged. Ceci peut être défini dans XAML comme suit:

  

L’object ScrollChangedEventArgs a diverses propriétés qui peuvent être utiles pour calculer la disposition et la position de défilement (étendue, décalage, fenêtre d’affichage). Notez que ceux-ci sont généralement mesurés en nombre de lignes / colonnes lorsque vous utilisez les parameters de virtualisation DataGrid par défaut.

Voici un exemple d’implémentation qui maintient l’élément inférieur en vue lorsque de nouveaux éléments sont ajoutés au DataGrid, à moins que l’utilisateur ne déplace la barre de défilement pour afficher les éléments plus haut dans la grid.

  private void control_ScrollChanged(object sender, ScrollChangedEventArgs e) { // If the entire contents fit on the screen, ignore this event if (e.ExtentHeight < e.ViewportHeight) return; // If no items are available to display, ignore this event if (this.Items.Count <= 0) return; // If the ExtentHeight and ViewportHeight haven't changed, ignore this event if (e.ExtentHeightChange == 0.0 && e.ViewportHeightChange == 0.0) return; // If we were close to the bottom when a new item appeared, // scroll the new item into view. We pick a threshold of 5 // items since issues were seen when resizing the window with // smaller threshold values. var oldExtentHeight = e.ExtentHeight - e.ExtentHeightChange; var oldVerticalOffset = e.VerticalOffset - e.VerticalChange; var oldViewportHeight = e.ViewportHeight - e.ViewportHeightChange; if (oldVerticalOffset + oldViewportHeight + 5 >= oldExtentHeight) this.ScrollIntoView(this.Items[this.Items.Count - 1]); } 

Réellement…

J’avais également le même problème lorsque j’apprenais à propos de Collection Views comment utiliser DataContext dans WPF.

Moi aussi, j’ai été confronté à la tâche de créer ensemble un programme WPF dont j’ai besoin pour pouvoir monter et descendre sur le DataGrid à l’aide de boutons, car j’avais besoin de le mettre sur un écran tactile résistif UNIQUEMENT pour les constructeurs de production \ t de mon entreprise. pas de souris ni de clavier à utiliser.

Mais cet exemple a fonctionné pour moi en utilisant la méthode ScrollIntoView comme mentionné précédemment dans ce post:

  private void OnMoveUp(object sender, RoutedEventArgs e) { ICollectionView myCollectView = CollectionViewSource.GetDefaultView(Orders); if (myCollectView.CurrentPosition > 0) myCollectView.MoveCurrentToPrevious(); if (myCollectView.CurrentItem != null) theDataGrid.ScrollIntoView(myCollectView.CurrentItem); } private void OnMoveDown(object sender, RoutedEventArgs e) { ICollectionView myCollectView = CollectionViewSource.GetDefaultView(Orders); if (myCollectView.CurrentPosition < Orders.Count) myCollectView.MoveCurrentToNext(); if (myCollectView.CurrentItem !=null) theDataGrid.ScrollIntoView(myCollectView.CurrentItem); } 

Where Orders est une collection List

en XAML:

       << code >>  

Suivez les conseils précédents et conservez le contrôle DataGrid seul et non dans un panneau de stack. Pour la définition de ligne pour DataGrid (la troisième ligne dans ce cas), je règle la hauteur sur 150 et la barre de défilement fonctionne.

Voici une autre excellente solution.

 public sealed class CustomDataGrid : DataGrid { protected override void OnItemsSourceChanged(IEnumerable oldValue, IEnumerable newValue) { base.OnItemsSourceChanged(oldValue, newValue); } protected override void OnItemsChanged(NotifyCollectionChangedEventArgs e) { base.OnItemsChanged(e); if (this.Items.Count > 0) this.ScrollIntoView(this.Items[this.Items.Count - 1]); } } 

Ce dont vous avez besoin, c’est d’obtenir la référence à l’object ScrollViewer pour votre DataGrid. Vous pouvez ensuite manipuler la propriété VerticalOffset pour faire défiler vers le bas.

Pour append encore plus de lumière à votre application … vous pouvez append une animation Spline au défilement pour que tout soit à la hauteur du rest de l’application.

Si vous utilisez un modèle MVVM , vous pouvez combiner cet article avec cet autre: http://www.codeproject.com/KB/WPF/AccessControlsInViewModel.aspx .

L’idée est d’utiliser des propriétés attachées pour accéder au contrôle dans votre classe ViewModel. Une fois que vous avez fait cela, vous devez vérifier que la grid de données n’est pas nulle et qu’elle contient des éléments.

 if ((mainDataGrid != null) && (mainDataGrid.Items.Count > 0)){ //Same snippet } 

Défilement automatique WPF DataGrid

Défilement automatique tant que le bouton de la souris est enfoncé sur un contrôle de bouton.

Le XAML

  

Le code

  private bool pagedown = false; private DispatcherTimer pageDownTimer = new DispatcherTimer(); private void XBTNPageDown_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) { pagedown = true; pageDownTimer.Interval = new TimeSpan(0, 0, 0, 0, 30); pageDownTimer.Start(); pageDownTimer.Tick += (o, ea) => { if (pagedown) { var sv = XDG.FindVisualChild(); sv.PageDown(); pageDownTimer.Start(); } else { pageDownTimer.Stop(); } }; } private void XBTNPageDown_MouseUp(object sender, MouseButtonEventArgs e) { pagedown = false; } 

Ceci est la méthode d’extension

Placez-le dans une classe statique de votre choix et ajoutez une référence au code ci-dessus.

  public static T FindVisualChild(this DependencyObject depObj) where T : DependencyObject { if (depObj != null) { for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++) { DependencyObject child = VisualTreeHelper.GetChild(depObj, i); if (child != null && child is T) { return (T)child; } T childItem = FindVisualChild(child); if (childItem != null) return childItem; } } return null; } 

NOTE: La propriété sv peut être déplacée pour éviter le travail répété.

Quelqu’un at-il un moyen de faire cela RX?

Si vous avez utilisé dataview pour le datagrid.datacontext, vous pouvez utiliser ceci:

 private void dgvRecords_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e) { var dv = dgvRecords.DataContext as DataView; if (dv.Count > 0) { var drv = dv[dv.Count - 1] as DataRowView; dgvRecords.ScrollIntoView(drv); } }