Как автопрокрутить на 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
Любые идеи? Я чувствую себя очень глупо, и мне кажется странным, что этот вопрос так тяжел. Что мне не хватает?
Ответы
Ответ 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