【问题标题】:NotifyOfPropertyChange not triggered in DataGrid在 DataGrid 中未触发 NotifyOfPropertyChange
【发布时间】:2017-11-27 12:11:14
【问题描述】:

我创建了一个 WPF 项目,以明确说明我的项目。

免责声明:我使用 Caliburn.Micro 仅用于使用 PropertyChangedBaseBindableCollection。该项目中的其他所有内容都是样板 WPF 内容。

对,所以,我有一个班级DataItem。它只有两个属性:

  • int 称为 Qty,并且
  • 名为Sum 的计算属性,它 是Qty 的两倍。

就是这样。为了清楚起见,下面是代码:

public class DataLine : PropertyChangedBase
{
    private int qty;

    public int Qty
    {
        get
        {
            return qty;
        }

        set
        {
            if (value == qty)
            {
                return;
            }

            qty = value;
            NotifyOfPropertyChange();
            NotifyOfPropertyChange(() => Sum);
        }
    }

    public int Sum => qty * 2;

    public DataLine(int a)
    {
        Qty = a;            
    }
}

现在,ViewModel。它有

  • DataLineBindableCollection(这是 Caliburn 的 ObservableCollection 风格),
  • 一个int,它将把DataLines的所有Sum加起来,并且
  • ICommand。

ICommand 只为BindableCollectionint 触发NotifyPropertyChanged

代码如下:

class ViewModel: PropertyChangedBase
{
    public ViewModel()
    {
        Data = new BindableCollection<DataLine>
        {
            new DataLine(1),
            new DataLine(2),
            new DataLine(4),
            new DataLine(6)
        };

        RefreshCommand = new RefreshCommand(this);
    }

    private BindableCollection<DataLine> _data;

    public BindableCollection<DataLine> Data
    {
        get
        {
            return _data;
        }

        set
        {
            if (value == _data)
            {
                return;
            }

            _data = value;
            NotifyOfPropertyChange();
            NotifyOfPropertyChange(() => Amount);
        }
    }

    public ICommand RefreshCommand { get; private set; }

    public void RefreshAction()
    {
        NotifyOfPropertyChange(() => Data);
        NotifyOfPropertyChange(() => Amount);
    }

    public int? Amount => Data?.Sum(c => c.Sum);

}

现在,对于View: 很简单。

  • 有一个DataGrid,绑定到BindableCollection
  • 有一个Button,绑定到ICommand
  • 有一个“TextBox, bound toAmount”。

这是View 的正文:

<DockPanel>        
    <TextBox DockPanel.Dock="Bottom" Text="{Binding Amount, TargetNullValue='', Mode=OneWay}"/>
    <Button DockPanel.Dock="Bottom" Command="{Binding RefreshCommand}" Content="Refresh"/>
    <DataGrid ItemsSource="{Binding Data, UpdateSourceTrigger=PropertyChanged}" AutoGenerateColumns="True"/>
</DockPanel>

当我运行它时,我会得到这样的结果:

现在是棘手的部分 - 让我编辑其中一行。

为了清楚起见,我添加了一个箭头来显示编辑 DataGrid 后光标所在的位置。查看第一行的Sum如何更新,但屏幕底部的Amount保持不变。

现在,当我点击 Refresh 时,会发生这种情况:

Amount 现已正确更新。

所有这些最终让我想到了我的问题:DataGrid 有什么问题?为什么在编辑行时DataGrid 不触发其设置器?以及如何确保它被触发?

【问题讨论】:

  • 您是否期望更改 BindableCollection 中对象的属性也会神奇地更改集合属性本身?您的 Data setter 不会因为您编辑集合元素而被调用。那么,“DataGrid 有什么问题”?没什么,只是你的错误期望。
  • 当编辑任何子对象时,有没有办法在集合本身上引发NotifyOfPropertyChange()
  • 当然,DataLine 可以通过某种方式访问​​父视图模型,并调用执行 NotifyOfPropertyChange(() =&gt; Amount) 的方法
  • 但这会将DataLineViewModel 紧密结合。是否可以修改 BindableCollection,使其在其任何子项引发 PropertyChanged() 时引发自己的 PropertyChanged() 事件?
  • 我不这么认为。你必须自己做。

标签: c# wpf mvvm datagrid caliburn.micro


【解决方案1】:

我发现 Datagrids 和更新元/聚合数据有时很烦人。 目前,我同意 Clemens 的观点,我不确定您是否可以轻松更改 BindableCollection 以满足您的需求。

但是,一个快速的解决方案是使用一个可通过的操作,而不是在您的 DataLineViewModel 之间创建紧密的一对。您总是可以对一些抽象或泛型感兴趣。但我创建了一个简单的代码编辑来说明我的意思。

本质上,您创建了一种机制,以便ViewModel 可以将动作传递给对象。如果我正在实现这个,我可能会实现某种形式的接口或泛型,这样我就不必为每个模型添加一个动作方法。或者至少从具有通知操作的类继承。为了清晰和易于阅读,我只做一个简单的编辑。

在您的 DataLine 添加:

//NotifyOfPropertyChange(); //NotifyOfPropertyChange(() => Sum); NotifyFromParent?.Invoke();

致您的Qty 设置器,并且:

   public DataLine(int a, System.Action action = null)
   {
     Qty = a;
     NotifyFromParent = action;
   }

   public System.Action NotifyFromParent;

然后在你的 ViewModel 中你可以去:

Data = new BindableCollection<DataLine>
{
   new DataLine(1, () => RefreshAction()),
   new DataLine(2, () => RefreshAction()),
   new DataLine(4, () => RefreshAction()),
   new DataLine(6, () => RefreshAction())
};

就像我说的,你可以让这个更漂亮。据我所知,在编辑数据网格中的单元格时,您需要创建一种机制来更新聚合或外部数据,并且您希望在所述编辑之后立即进行更新。

【讨论】:

  • 这正是我想要的。我创建了一个ObjectWithNotifyParent 的基类,它具有Action 和一个基类ctor。这将用于创建BindableCollection 的任何对象。谢谢你。
猜你喜欢
  • 2014-09-14
  • 1970-01-01
  • 1970-01-01
  • 2016-10-22
  • 2016-02-06
  • 2012-02-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多