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號