我觉得我很蠢。我现在搜索了15分钟,发现了几种不同的数据网格滚动解决方案,但似乎没有一个对我有用。
我正在使用WPF和.NET 3.5以及WPF Toolkit DataGrid。当我的可观察集合发生变化时,我的网格会更新,完美运行。现在,我的DataGrid位于普通网格内,如果DataGrid太大,则会出现滚动条。也很好......
现在来了1.000.000 $问题:
如何让datagrid滚动到最后一行? 有:
有什么想法吗?我觉得自己真的很蠢,这个问题太难了似乎很奇怪。我错过了什么?
答案 0 :(得分:48)
您应该使用datagrid方法
datagrid.ScrollIntoView(itemInRow);
或
datagrid.ScrollIntoView(itemInRow, column);
这种方式不会乱七八糟地找到滚动查看器等。
答案 1 :(得分:42)
;)
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 :(得分:18)
我为网格自动滚动编写了附加属性:
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}"/>
答案 3 :(得分:7)
为了添加AutoScroll To Last元素:
YourDataGrid.ScrollIntoView(YourDataGrid.Items.GetItemAt(YourDataGrid.Items.Count-1));
愿这个帮助:)
答案 4 :(得分:6)
我知道这是一个迟到的答案,但只是为了搜索的人,我找到了最简单的方法来滚动到DataGrid的底部。在DataContextChanged
事件中将其放入:
myDataGrid.ScrollIntoView(CollectionView.NewItemPlaceholder);
好吧?
这就是它的工作原理:在每个数据网格上,DataGrid底部都有一个位置,您可以在其中将新项目添加到它所绑定的列表中。这是一个CollectionView.NewItemPlaceholder
,只有一个在你的DataGrid中。所以你可以滚动到那个。
答案 5 :(得分:6)
listbox.Add(foo);
listbox.SelectedIndex = count - 1;
listbox.ScrollIntoView(listbox.SelectedItem);
listbox.SelectedIndex = -1;
答案 6 :(得分:3)
如果是大数据datagrid.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();
}
}
答案 7 :(得分:2)
我发现最简单的方法是从ScrollViewer.ScrollChanged附加事件中调用ScrollIntoView方法。这可以在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]);
}
答案 8 :(得分:1)
实际上...
当我在学习有关在WPF中执行DataContext的集合视图时,我也遇到了同样的问题。
我也面临着将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>
集合
:
<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,滚动条可以正常工作。
答案 9 :(得分:1)
这是另一个出色的解决方案。
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]);
}
}
答案 10 :(得分:0)
如果您使用的是 MVVM 模式,则可以将本文与其他文章结合使用:http://www.codeproject.com/KB/WPF/AccessControlsInViewModel.aspx。
我们的想法是使用附加属性来访问ViewModel类中的控件。一旦你这样做,你需要检查datagrid是否为null,并且它有任何项目。
if ((mainDataGrid != null) && (mainDataGrid.Items.Count > 0)){
//Same snippet
}
答案 11 :(得分:0)
您需要的是获取DataGrid的ScrollViewer对象的引用。然后,您可以操纵VerticalOffset属性以滚动到底部。
要为您的应用添加更多耀斑......您可以在滚动条中添加样条曲线动画,以便所有内容都与应用程序的其余部分相同。
答案 12 :(得分:0)
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方式吗?
答案 13 :(得分:0)
如果您使用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);
}
}
答案 14 :(得分:0)
以下代码对我有用;
.example {
-webkit-transform: scale(1); /* Ch <36, Saf 5.1+, iOS < 9.2, An =<4.4.4 */
-ms-transform: scale(1); /* IE 9 */
transform: scale(1); /* IE 10, Fx 16+, Op 12.1+ */
}
答案 15 :(得分:0)
如果您正在寻找执行自动滚动的MVVM方式,则可以使用自动滚动行为。该行为滚动到选定的项目,只需添加对System.Windows.Interactivity.dll的引用:
public class ScrollIntoViewBehavior : Behavior<DataGrid>
{
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.SelectionChanged += new SelectionChangedEventHandler(AssociatedObject_SelectionChanged);
}
void AssociatedObject_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (sender is DataGrid)
{
DataGrid grid = (sender as DataGrid);
if (grid?.SelectedItem != null)
{
grid.Dispatcher.InvokeAsync(() =>
{
grid.UpdateLayout();
grid.ScrollIntoView(grid.SelectedItem, null);
});
}
}
}
protected override void OnDetaching()
{
base.OnDetaching();
AssociatedObject.SelectionChanged -=
new SelectionChangedEventHandler(AssociatedObject_SelectionChanged);
}
}
XAML
<DataGrid>
<i:Interaction.Behaviors>
<local:ScrollIntoViewBehavior/>
</i:Interaction.Behaviors>
</DataGrid>
答案 16 :(得分:0)
在使用带有滚动条的datagridview时,必须使用此技术。因为我尝试了其他技术。但是那些不能正常工作。...
var border = VisualTreeHelper.GetChild(mainDataGrid, 0) as Decorator;
if(border != null)
{ var scroll = border.Child as ScrollViewer;
if (scroll != null) scroll.ScrollToEnd();
}