编辑数据网格上的单元格后运行方法

Posted

技术标签:

【中文标题】编辑数据网格上的单元格后运行方法【英文标题】:Run a method after editing a cell on a datagrid 【发布时间】:2020-10-23 17:28:36 【问题描述】:

我正在使用 WPF 和 CaliburnMicro 来绑定数据。我想在每次编辑完单元格时运行一个 Add() 方法,但问题是数据网格的属性没有执行它。

这是我的代码:

<Window x:Class="DataGrid_NotifyOfPropertyChanged.Views.ShellView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:DataGrid_NotifyOfPropertyChanged.Views"
    mc:Ignorable="d"
    Title="ShellView" Height="450" Width="800">
<Grid>
    <DataGrid x:Name="Numbers" CanUserAddRows="False"/>
</Grid>
public class NumbersModel

    public int Number  get; set; 

ShellViewModel

public class ShellViewModel: Screen

    public ShellViewModel()
    
        Numbers.Add(new NumbersModel  Number = 1 );
        Numbers.Add(new NumbersModel  Number = 2 );
    

    private BindableCollection<NumbersModel> _numbers = new BindableCollection<NumbersModel>();

    public BindableCollection<NumbersModel> Numbers
    
        get  return _numbers; 
        set  
            _numbers = value;
            Add();
        
    

    public void Add()
    
        double result = 0;
        foreach(var i in _numbers.ToList())
        
            result += i.Number;
        
        MessageBox.Show(result.ToString());
    

【问题讨论】:

你可以把你的逻辑放在 Number 的 setter 中。您应该使用更深思熟虑的属性命名。始终在视图模型上执行 inotifypropertychanged。 【参考方案1】:

您必须让NumbersModel 实现INotifyPropertyChanged 接口。 这样,您可以在属性更改时收到通知。您通常总是必须在每个用作绑定源的类上实现此接口。 如果没有这个接口,数据绑定也可以工作,但是性能会急剧下降。对于具有许多绑定的应用程序,这将是一个问题。

解决办法是监听NumbersModel.Number的属性变化。因此我介绍了一个专门的活动NumberChanged。 事件NumberChanged 是可选的。您也可以收听PropertyChanged,然后使用switch 语句按属性名称过滤所需的属性。我认为与混乱的switch 块相比,专门的事件显着提高了可读性和对上下文的理解。

为防止删除项目时发生内存泄漏,您必须取消订阅您正在收听的NumbersModel 的每个事件。 因此,您还需要监听 Numbers 集合的 CollectionChanged 事件:

NumbersModel.cs

public class NumbersModel : INotifyPropertyChanged

  private int number;
  public int Number
  
    get => this.number;
    set
    
      if (value == this.number)
      
        return;
      

      this.number = value;
      OnPropertyChanged();
      OnNumberChanged();
    
  

  public event EventHandler NumberChanged;
  public event PropertyChangedEventHandler PropertyChanged;

  protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
  
    this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
  

  protected virtual void OnNumberChanged()
  
    this.NumberChanged?.Invoke(this, EventArgs.Empty);
  

ShellViewModel.cs

public class ShellViewModel : Screen

  public ShellViewModel()
  
    this.Numbers = new BindableCollection<NumbersModel>();
    this.Numbers.Add(new NumbersModel Number = 1);
    this.Numbers.Add(new NumbersModel Number = 2);
  

  public void Add()
  
    double result = this._numbers.Sum(numbersModel => numbersModel.Number);
    MessageBox.Show(result.ToString());
  

  private BindableCollection<NumbersModel> _numbers;
  public BindableCollection<NumbersModel> Numbers
  
    get => this._numbers;
    set
    
      // Unsubscribe from old collection
      if (this.Numbers != null)
      
        this.Numbers.CollectionChanged -= OnCollectionChanged;
      

      this._numbers = value;

      // Subscribe to new collection
      if (this.Numbers != null)
      
        this.Numbers.CollectionChanged += OnCollectionChanged;
        Add();
      
    
  

  private void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
  
    switch (e.Action)
    
      case NotifyCollectionChangedAction.Add:
      
        foreach (NumbersModel newItem in e.NewItems.Cast<NumbersModel>())
        
          newItem.NumberChanged += OnNumberChanged;
        

        break;
      
      case NotifyCollectionChangedAction.Remove:
      case NotifyCollectionChangedAction.Reset:
      
        foreach (NumbersModel newItem in e.OldItems.Cast<NumbersModel>())
        
          newItem.NumberChanged -= OnNumberChanged;
        

        break;
      
    
  

  private void OnNumberChanged(object sender, EventArgs e)
  
    Add();
  

【讨论】:

【参考方案2】:

我想在每次编辑完一个单元格时运行一个Add() 方法

使用 Caliburn.Micro 执行此操作的方法是使用 EventTriggerActionMessage 来处理 CellEditEnding 事件:

 <DataGrid x:Name="Numbers" CanUserAddRows="False"
     cal:Message.Attach="[Event CellEditEnding] = [Action Add()]" />

【讨论】:

我已经尝试过了,但输出不是我所期望的。假设我有两个实例(Number=1 和 Number=2)。更新绑定到 Number=1 的数据网格中的单元格并将其替换为 2 并执行 Add() 方法结果为 3 而不是 4。我在这里做错了吗? 如何以及何时更新源属性是另一个主题。如果您有其他问题,请提出一个新问题。您最初的问题是关于如何“每次完成编辑单元格时运行 Add() 方法”,不是吗? 很清楚你不明白“完成编辑单元格”。从输出来看,单元格根本没有被编辑过。

以上是关于编辑数据网格上的单元格后运行方法的主要内容,如果未能解决你的问题,请参考以下文章

WPF DataGrid 全行编辑

EasyUI datagrid editCell 编辑完单元格后如何取消编辑状态?

easyui datagrid 编辑单元格后失去焦点结束编辑状态,怎么做啊?

在 QTableView 中编辑单元格后使用 TAB 键前进时如何避免编辑模式?

删除单元格后刷新tableView

我可以在不双击 React JS 的情况下编辑 MUI 数据网格单元格吗?