WPF 获取DataGrid可见行数据
我需要考虑如何检测DataGrid中当前可见的行。WPF的DataGrid内部使用ScrollViewer来管理滚动,因此需要监听ScrollViewer的ScrollChanged事件。当用户滚动时,可以通过计算可视区域的位置来确定哪些行被显示。在MVVM模式下,直接访问DataGrid的行可能会破坏模式,因此需要使用附加行为或事件来将信息传递到ViewModel。
目前决定构建一个附加行为类,监听ScrollViewer的滚动事件,计算可见行,并通过依赖属性或消息传递给ViewModel。
开始建立DataGridVisibleRowsBehavior类 (注意:要通过NuGet安装Microsoft.Xaml.Behaviors.Wpf实现附加行为)
/// <summary> /// 获取DataGrid可见行数据附加属性 /// </summary> public class DataGridVisibleRowsBehavior : Behavior<DataGrid> { // 绑定到ViewModel的可观察属性 public static readonly DependencyProperty VisibleItemsProperty = DependencyProperty.Register("VisibleItems", typeof(IList<object>), typeof(DataGridVisibleRowsBehavior), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault)); public IList<object> VisibleItems { get => (IList<object>)GetValue(VisibleItemsProperty); set => SetValue(VisibleItemsProperty, value); } private ScrollViewer _scrollViewer; private DispatcherTimer _throttleTimer; protected override void OnAttached() { base.OnAttached(); AssociatedObject.Loaded += OnLoaded; _throttleTimer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(200) };//延迟200毫秒刷新 _throttleTimer.Tick += (s, e) => UpdateVisibleItems(); } private void OnLoaded(object sender, RoutedEventArgs e) { _scrollViewer = FindVisualChild<ScrollViewer>(AssociatedObject); if (_scrollViewer != null) { _scrollViewer.ScrollChanged += OnScrollChanged; } } private void OnScrollChanged(object sender, ScrollChangedEventArgs e) { _throttleTimer.Stop(); _throttleTimer.Start(); } /// <summary> /// 更新行的新数据 /// </summary> private void UpdateVisibleItems() { var visibleRows = new List<object>(); var itemsControl = AssociatedObject; foreach (var item in itemsControl.Items) { if (itemsControl.ItemContainerGenerator.ContainerFromItem(item) is DataGridRow row && IsRowVisible(row, AssociatedObject)) { visibleRows.Add(item); } } VisibleItems = visibleRows; } // 视觉树遍历方法 private static T FindVisualChild<T>(DependencyObject parent) where T : DependencyObject { for (int i = 0; i < VisualTreeHelper.GetChildrenCount(parent); i++) { var child = VisualTreeHelper.GetChild(parent, i); if (child is T result) return result; if (FindVisualChild<T>(child) is T descendant) return descendant; } return null; } // 判断行是否可见 private bool IsRowVisible(DataGridRow row, DataGrid dataGrid) { var rowTransform = row.TransformToAncestor(dataGrid); var rowRectangle = rowTransform.TransformBounds(new Rect(0, 0, row.ActualWidth, row.ActualHeight)); var dataGridRectangle = new Rect(0, 0, dataGrid.ActualWidth, dataGrid.ActualHeight); return dataGridRectangle.IntersectsWith(rowRectangle); } }
XAML代码使用
<!-- 引用命名空间 Local 是你附加属性类所在的文件位置--> xmlns:i="http://schemas.microsoft.com/xaml/behaviors" xmlns:local="clr-namespace:YourNamespace.Behaviors" <!-- DataGrid定义 --> <DataGrid ItemsSource="{{Binding Items}}"> <i:Interaction.Behaviors> <local:DataGridVisibleRowsBehavior VisibleItems="{{Binding VisibleItems, Mode=OneWayToSource}}"/> </i:Interaction.Behaviors> </DataGrid>
ViewModel类实现(当前使用了CommunityToolkit.Mvvm来进行绑定,你也可以用其他)
public class MainViewModel : ObservableObject { private IList<object> _visibleItems; public IList<object> VisibleItems { get => _visibleItems; set => SetProperty(ref _visibleItems, value); } }
- 节流机制:200ms延迟计算避免高频滚动导致的性能问题
- 精确可视判断:通过
TransformToAncestor计算行在DataGrid中的实际位置 - 动态数据支持:兼容数据更新时的自动重新计算

浙公网安备 33010602011771号