Как выполнить автоматическую прокрутку на сетке данных WPF - PullRequest
33 голосов
/ 22 июня 2009

Я думаю, что я глупый. Сейчас я искал 15 минут и нашел несколько разных решений для прокрутки данных, но ни одно из них мне не помогло.

Я использую WPF с .NET 3.5 и WPF Toolkit DataGrid. Моя сетка обновляется, когда моя наблюдаемая коллекция изменяется, работает отлично. Теперь моя DataGrid находится внутри обычной Grid, и полосы прокрутки появляются, если DataGrid становится слишком большим. Тоже хорошо ...

А теперь приходит вопрос 1.000.000 $:

Как получить сетку данных для прокрутки до последнего ряда? Есть:

  • Нет свойства AutoScroll
  • нет CurrentRowSelected Index
  • CurrentCell, но нет Коллекции, которую я мог бы использовать для CurrentCell = AllCells.Last

Есть идеи? Я чувствую себя действительно глупо, и кажется странным, что этот вопрос такой сложный. Чего мне не хватает?

Ответы [ 15 ]

48 голосов
/ 23 февраля 2010

Вы должны использовать метод datagrid

datagrid.ScrollIntoView(itemInRow);

или

datagrid.ScrollIntoView(itemInRow, column);

этот способ не мешает найти средство просмотра прокрутки и т. Д.

42 голосов
/ 15 сентября 2009

;)

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();
    }
}
18 голосов
/ 09 июля 2014

Я написал прикрепленное свойство для автопрокрутки сетки:

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

Использование:

    <DataGrid c:DataGridBehavior.Autoscroll="{Binding AutoScroll}"/>
7 голосов
/ 15 мая 2013

Для добавления элемента автопрокрутки до последнего:

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

Может помочь:)

6 голосов
/ 18 августа 2014

Я знаю, что это поздний ответ, но только для людей, которые ищут вокруг, я нашел самый ПРОСТОЙ способ прокрутки до нижней части DataGrid. в случае DataContextChanged введите это:

myDataGrid.ScrollIntoView(CollectionView.NewItemPlaceholder);

Полегче, да?

Вот почему это работает: в каждой сетке данных есть место внизу DataGrid, где вы можете добавить новый элемент в свой список, к которому он привязан. Это CollectionView.NewItemPlaceholder, и в вашей DataGrid будет только один из них. Так что вы можете просто прокрутить до этого.

6 голосов
/ 25 марта 2010
listbox.Add(foo);
listbox.SelectedIndex = count - 1;
listbox.ScrollIntoView(listbox.SelectedItem);
listbox.SelectedIndex = -1;
3 голосов
/ 21 июня 2010

если большой массив данных. ScrollIntoView (itemInRow, column); не работает нормально, тогда нам нужно использовать только одно:

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(); 
            } 
        } 
2 голосов
/ 09 октября 2015

Я обнаружил, что самый простой способ сделать это - вызвать метод ScrollIntoView из вложенного события ScrollViewer.ScrollChanged. Это может быть установлено в XAML следующим образом:

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

Объект ScrollChangedEventArgs имеет различные свойства, которые могут быть полезны для вычисления макета и положения прокрутки (экстент, смещение, область просмотра). Обратите внимание, что они обычно измеряются в количестве строк / столбцов при использовании настроек виртуализации DataGrid по умолчанию.

Вот пример реализации, которая сохраняет нижний элемент в поле зрения, когда новые элементы добавляются в DataGrid, если пользователь не перемещает полосу прокрутки для просмотра элементов выше в сетке.

    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 голос
/ 25 марта 2016

Вот еще одно отличное решение.

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]);
    }
}
1 голос
/ 05 ноября 2014

На самом деле ...

У меня тоже была такая же проблема, когда я узнал о коллекциях представлений о создании DataContext в WPF.

Передо мной также стояла задача собрать воедино программу WPF, которая мне нужна программно для перемещения вверх и вниз по сетке данных с помощью кнопок, поскольку мне нужно было поместить ее на резистивный сенсорный экран ТОЛЬКО для строителей производства моей компании и для них нет мыши или клавиатуры.

Но этот пример работал для меня, используя метод ScrollIntoView, как упоминалось ранее в этом посте:

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

Где заказы List<T> коллекция

в 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>

Следуйте предыдущему совету и храните DataGrid отдельно, а не в панели стека. Для определения строки для DataGrid (в данном случае третьей строки) я установил Высота на 150, и полоса прокрутки работает.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...