【问题标题】:Does WPF DataGrid fire an event when a row is added / removed?添加/删除行时,WPF DataGrid 是否会触发事件?
【发布时间】:2012-07-02 12:21:30
【问题描述】:

我希望每次 DataGrid 获得更多行或删除一些行时重新计算。我尝试使用Loaded 事件,但只触发了一次。

我找到了AddingNewItem,但它在添加之前就被触发了。我需要做我的事情之后

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

【问题讨论】:

    标签: c# .net wpf


    【解决方案1】:

    如果您的DataGrid 绑定到某些东西,我会想到两种方法。

    您可以尝试获取DataGrid.ItemsSource 集合,并订阅其CollectionChanged 事件。这只有在您首先知道它是什么类型的集合时才有效。

    // 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>()
    

    【讨论】:

    • 我试图在我的代码中实现这一点,添加通知集合更改事件处理程序,但我的 sourceCollection 始终等于“null”。我没有 ViewModelBase 作为 ObservableCollection 模板的可用类。 . .我正在使用 VS 20113 (express) 绑定到 SQLServer 2014 express 数据库和 WPF 应用程序。我无法找到一个缺失的“使用”指令,它会给我 ViewModelBase 。 . .我究竟做错了什么?谢谢保罗
    • @PaulGibson ViewModelBase 是我通常在所有 ViewModel 上实现的自定义类。在您的情况下,只需将其转换为您的 ItemsSource 中的任何类型的项目。
    • 感谢您的解释,这是我在谷歌搜索后得出的结论。所以我尝试了数据表的类,以及行类(通用 DataRow 和我的 dataRow 的特定类)和 sourceCollection 的 yeild null 。 . .我在link 发布了一个包含代码块的问题。 . .我确定我做错了一些简单的事情,但找不到正确的组合。
    • 我尝试了第二种方法,Prism 中的 EventAggregator。在这种情况下,当向 DataGrid 添加新行时,焦点已设置到下一个元素。当我单击该行时,该事件会触发,因为此时集合确实发生了变化。当 CanUserAddRows 设置为 true 时,是否会在将新行添加到数据网格时立即触发?
    • @Vishal 我个人不会为此使用 CanUserAddRows。我会为用户提供一个“添加”按钮,将一条记录添加到源集合中,或者为他们提供一个空白项,每当填充该空白项时,添加一个新的空白项来代替“新建”行.就我个人而言,我更喜欢第一个解决方案,因为我发现网格中多余的空白行经常使用户感到困惑,并且使验证变得更加困难,因为我总是必须检查该项目是否是“新”项目
    【解决方案2】:

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

    卸载也是如此。

    DataGrid.UnLoadingRow(object sender, DataGridRowEventArgs e)?

    【讨论】:

    • 您可以在不触发此事件的情况下向DataGrid 的数据源添加新行。此事件与可视行(DataGridRow 对象)相关,与数据源中的行无关。
    • 在这种情况下,将您的数据网格绑定到ObservableCollection
    • 卸载只是从行中移开而不仅仅是为了删除
    【解决方案3】:

    您是否尝试过 MVVM 方法并绑定到 Observable 集合?

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

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

    【讨论】:

    • 虽然这是处理应用程序逻辑的集合更改事件的正确方法,但我认为 OP 是基于another question of his 中的一些 cmets 询问视图的集合更改事件
    【解决方案4】:

    如果您愿意,您可以按照其他人在此处描述的那样走 RowUnloading 路线,但请注意,每次一行失去焦点时也会触发此事件。

    但是,通过玩耍,我发现当删除一行时,网格的SelectedItem 属性为空,而CurrentItem 属性不为空,到目前为止,我只看到此组合用于已删除的行,(虽然我不能保证我没有错过一个异国情调的情况......但是对于远离行的基本情况我到目前为止还没有看到)。

    那么什么时候可以使用下面的代码只过滤已删除的行:

    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
    }
    

    【讨论】:

      【解决方案5】:

      如果你想使用 ObservableCollection 并获得关于添加或其他操作的通知,最好的方法是使用 INotifyCollectionChanged

      var source = datagrid.ItemsSource as INotifyCollectionChanged;
      

      因为,当你解包到ObservableCollection&lt;MyClass&gt;()时,你必须写srogly MyClass(不是ObservableCollection&lt;ParentOfMyClass&gt;())

      【讨论】:

        【解决方案6】:

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

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

        ScrollChangedEventArgs 对象具有各种有助于计算布局和滚动位置(Extent、Offset、Viewport)的属性。请注意,在使用默认虚拟化设置时,这些通常以行数/列数来衡量。

        【讨论】:

          【解决方案7】:

          我一直在寻找解决方案,我找到了处理这个问题的完美事件,该事件称为 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
          
          }
          

          【讨论】:

          • 这适用于任何行以视觉方式而非逻辑方式卸载;因此,它会在一行被卸载时触发,如果是虚拟化,无论项目在操作之前是否已经存在,都可能发生这种情况。理想的行为是仅在从基础集合中添加或删除项目时触发。
          • 卸载只是从行中移开而不仅仅是为了删除
          猜你喜欢
          • 1970-01-01
          • 2011-04-15
          • 2012-09-19
          • 2022-12-16
          • 1970-01-01
          • 1970-01-01
          • 2012-02-29
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多