Подтвердить что ты не робот

Как автопрокрутить на WPat datagrid

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

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

И вот вопрос 1.000.000 $:

Как мне получить datagrid для прокрутки до последней строки? Существует:

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

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

4b9b3361

Ответ 1

;)

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

Вам следует использовать метод datagrid

datagrid.ScrollIntoView(itemInRow);

или

datagrid.ScrollIntoView(itemInRow, column);

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

Ответ 3

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

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}"/>

Ответ 4

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

Ответ 5

Для добавления AutoScroll к последнему добавлен:

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

Может эта помощь:)

Ответ 6

Я знаю, что это поздний ответ, но только для людей, которые ищут, я нашел ЛУЧШИЙ способ прокрутки до нижней части DataGrid. в событии DataContextChanged поместите это в:

myDataGrid.ScrollIntoView(CollectionView.NewItemPlaceholder);

Легко ли?

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

Ответ 7

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

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

Ответ 8

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

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

Объект ScrollChangedEventArgs имеет различные свойства, которые могут быть полезны для вычисления макета и положения прокрутки (Extent, Offset, Viewport). Обратите внимание, что они обычно измеряются в количествах строк/столбцов при использовании настроек виртуализации по умолчанию 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]);
    }

Ответ 9

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

У меня была такая же проблема, когда я узнал о Collection Views о том, как делать DataContext в WPF.

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

Но этот пример работал у меня с использованием метода 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 (в этом случае третья строка) я устанавливаю Height на 150, а полоса прокрутки работает.

Ответ 10

Здесь еще одно отличное решение.

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

Ответ 11

Вам нужно получить ссылку на объект ScrollViewer для вашего DataGrid. Затем вы можете манипулировать свойством VerticalOffset для прокрутки до нижней части.

Чтобы добавить еще больше вспышек в ваше приложение... вы можете добавить анимацию Spline в прокрутку, чтобы все выглядело точно так же, как и остальная часть приложения.

Ответ 12

Если вы используете шаблон MVVM, вы можете иметь комбинацию этой статьи с этим другим: http://www.codeproject.com/KB/WPF/AccessControlsInViewModel.aspx.

Идея состоит в том, чтобы использовать прикрепленные свойства для доступа к элементу управления в вашем классе ViewModel. Как только вы это сделаете, вам нужно будет проверить, что datagrid не является нулевым, и он имеет любые элементы.

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

Ответ 13

Автоматическая прокрутка WPF DataGrid

Автоматическая прокрутка до тех пор, пока кнопка мыши не будет нажата на кнопку управления.

XAML

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

Код

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

Это метод расширения

Поместите его в статический класс по вашему выбору и добавьте ссылку на код выше.

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

ПРИМЕЧАНИЕ. Свойство sv можно перемещать, чтобы избежать повторной работы.

У кого-нибудь есть способ RX для этого?

Ответ 14

Если вы использовали dataview для datagrid.datacontext, вы можете использовать это:

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

Ответ 15

Следующий код работает для меня;

Private Sub DataGrid1_LoadingRow(sender As Object, e As DataGridRowEventArgs) Handles DataGrid1.LoadingRow
    DataGrid1.ScrollIntoView(DataGrid1.Items.GetItemAt(DataGrid1.Items.Count - 1))
End Sub