2009-06-22 2 views
31

Penso di essere stupido. Ho cercato per 15 minuti e ho trovato diverse soluzioni per lo scorrimento dei datagrids, ma nessuno sembra funzionare per me.Come eseguire lo scorrimento automatico del datagrid WPF

Sto utilizzando WPF con .NET 3.5 e il WPF Toolkit DataGrid. La mia griglia si aggiorna quando la mia collezione osservabile cambia, funziona perfettamente. Ora, il mio DataGrid si trova all'interno di una normale griglia e barre di scorrimento appaiono se il DataGrid diventa troppo grande. Anche bene ...

E ora viene il 1.000.000 $ domanda:

Come faccio ad avere il datagrid per scorrere fino all'ultima riga? c'è:

  • senza AutoScroll Proprietà
  • alcun indice CurrentRowSelected
  • un CurrentCell, ma nessun Collezione ho potuto usare per CurrentCell = AllCells.Last

Tutte le idee? Mi sento davvero stupido, e sembra strano che questa domanda sia così difficile. Cosa mi manca?

risposta

37

;)

 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(); 
      } 
     } 
+1

Grazie mille, se solo la vita fosse sempre così facile :-) –

+1

Brillante pezzo di codice, avvolto in un'eccezione ArgumentOutOfRange e sarebbe perfetto, per quando la casella di riepilogo potrebbe essere vuota. – wonea

+0

Dove va questo codice? Che evento stai agganciare? Grazie! – davidbitton

0

Quello che ti serve è ottenere il riferimento all'oggetto ScrollViewer per il tuo DataGrid. È quindi possibile modificare la proprietà VerticalOffset per scorrere verso il basso.

Per aggiungere ancora più bagliori alla tua app ... puoi aggiungere un'animazione Spline allo scroll in modo che tutto sembri uguale al resto dell'applicazione.

+0

Mm, ok, se trovato alcune proprietà associate, come ScrollViewer.CanContentScroll = "True", ma senza qualsiasi effetto Lo so, RTFM, ma hey, ho ancora speranza per una riga di codice qui. Lo so, viziato moccioso :-) –

46

Si dovrebbe utilizzare il metodo datagrid

datagrid.ScrollIntoView(itemInRow); 

o

datagrid.ScrollIntoView(itemInRow, column); 

questo modo non fornisce alcuna fare in giro a trovare il rotolo spettatore ecc

+0

non funziona !!! – user1034912

+2

@ user1034912 Se hai un problema con questa risposta, dovresti fornire dettagli sul motivo per cui non funziona o forse le tue circostanze sono diverse e devi fare un'altra domanda. Forse qualcosa è cambiato rispetto al quadro sottostante poiché questa domanda è stata risposta 6 anni fa.A volte ho avuto problemi con la visualizzazione a scorrimento mentre la griglia era virtualizzata. Dal numero di upvotes è ovvio che questo ha funzionato per un bel po 'di persone quindi forse il problema è con il tuo codice, non questa risposta. –

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

Questo è ottimo per scorrere la selezione al "centro" dello schermo (con alcuni controlli e modifiche logici). –

3

se dati di grandi dimensioni datagrid.ScrollIntoView (itemInRow, colonna); Non funziona bene, allora abbiamo bisogno di utilizzare sotto una sola:

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(); 
      } 
     } 
0

Se si utilizza MVVM modello, si può avere una combinazione di questo articolo con quest'altra: http://www.codeproject.com/KB/WPF/AccessControlsInViewModel.aspx.

L'idea è di utilizzare le proprietà associate per accedere al controllo nella classe ViewModel. Una volta che lo fai, dovresti controllare che il datagrid non sia nullo e che abbia degli elementi.

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

per avere un AutoScroll Per l'ultimo elemento aggiunto:

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

maggio di aiuto :)

+0

'YourDataGrid.Items.Last()' potrebbe rendere il codice un po 'più pulito. – Oskar

15

Ho scritto una proprietà allegata per la griglia di scorrimento automatico:

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<DataGrid, NotifyCollectionChangedEventHandler> handlersDict = new Dictionary<DataGrid, NotifyCollectionChangedEventHandler>(); 

    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); 
    } 
} 

Uso:

<DataGrid c:DataGridBehavior.Autoscroll="{Binding AutoScroll}"/> 
+2

@ user161953 Penso che i visitatori abbiano bisogno di ulteriori spiegazioni per questo codice ..! – kamesh

+2

Spiega come funziona o come usarlo? –

+1

Funziona come previsto, grazie mille! – JoanComasFdz

6

So che questa è una risposta in ritardo, ma solo per le persone che sono alla ricerca in giro, ho trovato il modo easyest per scorrere fino alla fine di un DataGrid. in caso DataContextChanged mettere questo in:

myDataGrid.ScrollIntoView(CollectionView.NewItemPlaceholder); 

facile eh?

Questo è il motivo per cui funziona: Su ogni griglia di dati c'è un posto nella parte inferiore del DataGrid in cui è possibile aggiungere una nuova voce alla tua lista che è destinato a. Questo è un CollectionView.NewItemPlaceholder e ci sarà solo uno di quelli nel tuo DataGrid. Quindi puoi semplicemente scorrere fino a quello.

+2

-1: il datacontext cambia solo quando si aggiunge un nuovo viewmodel alla vista, quindi questa scorrerà verso il basso solo la prima volta, non ogni volta che un elemento viene aggiunto all'origine articoli –

+0

Ok, non lo farebbe, ma il datacontext era solo un Ad esempio, funziona come un fascino nella mia app ... –

+2

Inoltre, non è nella domanda. –

1

In realtà ...

Ho avuto lo stesso problema pure quando stavo imparando sulla raccolta Vista di fare DataContext in WPF.

Anch'io mi trovavo di fronte a un compito di eseguire lo slapping di un programma WPF che ho bisogno di spostare su e giù sul DataGrid usando i pulsanti poiché dovevo metterlo su un touchscreen resistivo SOLO per i costruttori di produzione \ t my compagnia, e non c'è mouse o tastiera da usare.

Ma questo esempio ha lavorato per me con il metodo ScrollIntoView Come detto in precedenza in questo 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); 
    } 

Dove Ordini è una raccolta

List<T> in XAML:

<StackPanel Grid.Row="1" 
     Orientation="Horizontal"> 
      <Button Click="OnMoveUp"> 
       <Image Source="Up.jpg" /> 
      </Button> 
      <Button Click="OnMoveDown"> 
       <Image Source="Down.jpg" /> 
       </Button> 
    </StackPanel> 

    <DataGrid Grid.Row="2" 
       x:Name="theDataGrid" 
       ItemSource="{Binding Orders}" 
       ScrollViewer.CanContentScroll="True" 
       ScrollViewer.VerticalScrollBarVisibility="Auto" Margin="0,0,0,5"> 

    <<code>> 


    </DataGrid> 

seguono il consiglio precedente e mantenere il DataGrid da solo e non in un pannello di stack. Per la definizione di riga per il DataGrid (la terza riga in questo caso), imposto l'altezza a 150 e la barra di scorrimento funziona.

0

WPF DataGrid Auto Scrolling

Auto Scrolling finché il pulsante del mouse è premuto su un controllo pulsante.

Il XAML

<Button x:Name="XBTNPageDown" Height="50" MouseLeftButtonDown="XBTNPageDown_MouseLeftButtonDown" MouseUp="XBTNPageDown_MouseUp">Page Down</Button> 

Il Codice

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<ScrollViewer>(); 
       sv.PageDown(); 
       pageDownTimer.Start(); 
      } 
      else 
      { 
       pageDownTimer.Stop(); 
      } 
     }; 
    } 

    private void XBTNPageDown_MouseUp(object sender, MouseButtonEventArgs e) 
    { 
     pagedown = false; 
    } 

Questo è il metodo di estensione

posto in una classe statica di vostra scelta e aggiungere riferimento alla codice sopra.

public static T FindVisualChild<T>(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<T>(child); 
       if (childItem != null) return childItem; 
      } 
     } 
     return null; 
    } 

Nota: la proprietà sv potrebbe essere spostato per evitare lavoro ripetuto.

Chiunque ha un modo RX per farlo?

2

Ho trovato che il modo più semplice per farlo è chiamare il metodo ScrollIntoView dall'evento associato ScrollViewer.ScrollChanged. Questo può essere impostato in XAML come segue:

<DataGrid 
... 
ScrollViewer.ScrollChanged="control_ScrollChanged"> 

L'oggetto ScrollChangedEventArgs ha diverse proprietà che possono essere utili per il calcolo il layout e la posizione (estensione, Offset, Finestra) scorrere. Si noti che questi sono in genere misurati in numero di righe/colonne quando si utilizzano le impostazioni di virtualizzazione DataGrid predefinite.

Ecco un'implementazione di esempio che mantiene l'elemento in fondo in vista quando vengono aggiunti nuovi elementi a DataGrid, a meno che l'utente non sposti la barra di scorrimento per visualizzare gli elementi più in alto nella griglia.

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]); 
    } 
1

Ecco un'altra soluzione eccellente.

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]); 
    } 
} 
0

Se è stato utilizzato per il dataview datagrid.datacontext, è possibile utilizzare questo:

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); 
    } 
}