WPF DataGrid是否在添加/删除行时触发事件?

10

每当DataGrid增加或删除行时,我希望能重新计算一些东西。我尝试使用Loaded事件,但那只会触发一次。

我找到了AddingNewItem,但那是在添加之前触发的。我需要在之后执行我的操作。

还有LayoutUpdated,它可以工作,但我担心使用它不明智,因为它对我的目的来说触发得太频繁了。

7个回答

11
如果您的绑定了数据,我想到两种方法来实现此目的。您可以尝试获取集合,并订阅其事件。这仅适用于您已经知道它所在的集合类型的情况下。
// Be warned that the `Loaded` event runs anytime the window loads into view,
// so you will probably want to include an Unloaded event that detaches the
// collection
private void DataGrid_Loaded(object sender, RoutedEventArgs e)
{
    var dg = (DataGrid)sender;
    if (dg == null || dg.ItemsSource == null) return;

    var sourceCollection = dg.ItemsSource as ObservableCollection<ViewModelBase>;
    if (sourceCollection == null) return;

    sourceCollection .CollectionChanged += 
        new NotifyCollectionChangedEventHandler(DataGrid_CollectionChanged);
}

void DataGrid_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
    // Execute your logic here
}

另一种解决方案是使用事件系统,例如Microsoft Prism的EventAggregator或MVVM Light的Messenger。这意味着你的ViewModel会在绑定的集合更改时广播一个DataCollectionChanged事件消息,你的View将订阅接收这些消息,并在其发生时执行代码。

使用EventAggregator

// Subscribe
eventAggregator.GetEvent<CollectionChangedMessage>().Subscribe(DoWork);

// Broadcast
eventAggregator.GetEvent<CollectionChangedMessage>().Publish();

使用 Messenger

//Subscribe
Messenger.Default.Register<CollectionChangedMessage>(DoWork);

// Broadcast
Messenger.Default.Send<CollectionChangedMessage>()

1
我正在尝试在我的代码中实现这个功能,添加notify collection changed事件处理程序,但我的sourceCollection始终等于“null”。我没有ViewModelBase作为ObservableCollection模板的可用类...我正在使用VS 2013(express)绑定到SQLServer 2014 express数据库和WPF应用程序。我一直没有找到一个缺少的“using”指令,可以给我提供ViewModelBase...我做错了什么?谢谢Paul - Paul Gibson
1
您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - Rachel
1
谢谢解释,这就是我在谷歌搜索后得出的结论。所以我尝试了数据表类,以及行类(通用DataRow和我的dataRow的特定类),两者都对sourceCollection产生了null的结果……我在这里发布了一个带有代码块的问题[链接](http://stackoverflow.com/questions/26620496/wpf-datagrid-roweditending-event-to-call-update)……我确定我做错了一些简单的事情,但找不到正确的组合。 - Paul Gibson
1
我已经尝试了第二种方法-Prism中的EventAggregator。在这种情况下,当DataGrid添加新行时,焦点已经设置到下一个元素。当我单击该行时,事件会触发,因为此时集合确实发生了更改。当CanUserAddRows设置为true时,是否有立即触发添加新行的事件呢? - Vishal
1
@Vishal 个人而言,我不会使用 CanUserAddRows 来实现这个功能。我会提供一个“添加”按钮,让用户将记录添加到源集合中,或者提供一个空白项,每当该空白项被填充时,就添加一个新的空白项来代替“新”行。个人而言,我更喜欢第一种解决方案,因为我发现网格中的额外空白行经常会让用户感到困惑,并且使验证变得更加困难,因为我总是需要检查该项是否为“新”项。 - Rachel
显示剩余2条评论

2

那么 DataGrid.LoadingRow(object sender, DataGridRowEventArgs e) 怎么样?

卸载时同理。

DataGrid.UnLoadingRow(object sender, DataGridRowEventArgs e) 呢?


6
在不触发此事件的情况下,您可以向“DataGrid”的数据源中添加新行。此事件与视觉行(“DataGridRow”对象)相关,而不是与数据源中的行相关。 - Martin Liversage
在这种情况下,将您的数据网格绑定到“ObservableCollection”。 - Nikhil Agrawal
1
移开行只是为了卸载文件而不仅仅是删除。 - yoel halb

2

你尝试过使用MVVM模式并绑定到可观察集合吗?

public ObservableCollection<Thing> Items{
get { return _items; }
set{ _items = value; RaisePropertyChanged("Items");  // Do additional processing here 
}
}

所以您可以在不受UI约束的情况下观察项目的添加/删除吗?

虽然这是处理应用程序逻辑的集合更改事件的正确方法,但我认为OP在他的另一个问题的一些评论中询问了基于视图的集合更改事件。 - Rachel

1
如果您愿意,可以按照其他人在此处描述的方式选择RowUnloading路线,但请注意,每当行失去焦点时,此事件也会触发。

然而,通过尝试,我发现当删除一行时,网格的SelectedItem属性为null,而CurrentItem属性不为null,到目前为止,我只看到这种组合适用于已删除的行(尽管我不能保证我没有错过任何奇特的情况...但对于移动到行之外的基本情况,我迄今为止还没有看到它)。

因此,您可以使用以下代码仅筛选已删除的行:

private void CategoriesGrid_UnloadingRow(object sender, DataGridRowEventArgs e)
{     
        if (((DataGrid)sender).SelectedItem != null || ((DataGrid)sender).CurrentItem == null)
        {
            return;
        }

        // The rest of your code goes here
}

0

如果您想使用ObservableCollection并获取有关添加或其他操作的通知,则最好使用INotifyCollectionChanged。

var source = datagrid.ItemsSource as INotifyCollectionChanged;

因为当你解包到 ObservableCollection<MyClass>() 时,你必须强制写 MyClass(而不是 ObservableCollection<ParentOfMyClass>())。

-1

根据您想要重新计算的“事物”,您可以考虑使用ScrollViewer.ScrollChanged附加事件。这可以在XAML中设置如下:

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

ScrollChangedEventArgs 对象具有多个属性,可用于计算布局和滚动位置(Extent,Offset,Viewport)。请注意,使用默认的虚拟化设置时,这些属性通常以行数/列数为度量单位。


-1

我正在寻找解决方案,我已经找到了处理此问题的完美事件,该事件称为UnloadingRow

<DataGrid ....
    UnloadingRow="DataGrid_UnloadingRow">
 ...
</DataGrid>

在你的 C# 代码中,你会得到这个:

private void ProductsDataGrid_UnloadingRow(object sender, DataGridRowEventArgs e)
{
   MyObject obj = (MyObject)e.Row.Item; // get the deleted item to handle it
   // Rest of your code ...
   // For example : deleting the object from DB using entityframework

}

这个方法只在视觉上卸载任何行时才有效,而不是逻辑上;因此,每当卸载一行时它都会触发,如果使用虚拟化,则可能会在操作之前或之后发生。理想的行为是仅在基础集合中添加或删除项目时触发。 - user1618054
仅通过移动而非删除行来卸载文件。 - yoel halb

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接