简体   繁体   中英

Detect changes in (elements of) a collection bound to a DataGrid to update other collections in WPF (MVVM)

Responsive UI design: DataGrid inputs to cause changes automatically (MVVM)

I am trying to create a simple application in which I have a charting tool that accepts collections as series to be plotted. My plan is to have an input DataGrid that is bound to a collection which can be populated manually.

Based on the values inside the DataGrid , I would then like to simultaneously populate a different collection that represents the definite integral of whatever function is being plotted, in other words a different function that shows the area between the input function and the x-axis.

As the DataGrid is updated, I would like to use MVVM to responsively update the chart as well, to display changes in both the input data and the output data.

A. Application parts (mock-up and structure)

I have created a gist that contains my implementations so far .

1. Main parts

In short, the application has the following components:

  • Coordinate<T, U> that has an X and Y property (and uses INotifyPropertyChanged )
  • ObservableCollection<Coordinate<double, double>> for plotting purposes
  • NumericalCalculator for executing numerical methods

2. Converters

In the WPF chatroom, Maverik suggested I could use value converters to let WPF do the heavy lifting, so I have written the following converters:

  • CoordinateSeriesToStringConverter converts a collection of coordinates to a string where all the coordinates are separated by comma's
  • FunctionToEquallySpacedConverter attempts to convert a collection of coordinates into a new collection containing some n number of equally spaced coordinates
  • FunctionToIntegralConverter attempts to integrate the input function (collection of coordinates) to its numerical integral between two bounds (currently does not integrate yet, for testing purposes)

B. My actual question: How can I detect changes in the DataGrid 's items source's elements?

I have gone the value converter route, but it turns out that even with INotifyPropertyChanged implemented on the elements of my bound ObservableCollection , those changes do not activate the converters.

I've already gotten a nice answer from sparedev with a different approach, which I will likely try out as well, but I'm still curious how I could solve the value converter issue. I'm sure that if I get that sorted out, both options are equally viable.

UPDATE: I've tried sparedev's method, but even with INotifyPropertyChanged , manipulating the DataGrid 's items from the UI does not raise any new events. Adding new rows or deleting them raises a CollectionChanged event, but unfortunately this alone does not let me update the plots responsively yet.

Let's register on CollectionChanged event and update result collection, see below.

    public MainViewModel()
    {
      InputCollection.CollectionChanged += Items_CollectionChanged;
    }

    private void Items_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
      if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Add)
      {
        foreach (var i in e.NewItems)
        {
          var c = i as Coordinate<double, double>;
          ResultCollection.Add(c);
          c.PropertyChanged += Item_PropertyChanged;
        }
      }

      if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Remove)
      {
        foreach (var i in e.OldItems)
        {
          var c = i as Coordinate<double, double>;
          ResultCollection.Remove(c);
          c.PropertyChanged += Item_PropertyChanged;
        }
      }
    }

    private void Item_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
      var c = sender as Coordinate<double, double>;
      Console.WriteLine(c.X + " " + c.Y);
    }

May be your Coordinate class should implement INotifyPropertyChanged interface. During setting X or Y property PropertyChanged should raised.

  public class Coordinate<T, U> : IComparable<Coordinate<T, U>>, INotifyPropertyChanged
    where T : IComparable<T>
    where U : IComparable<U>
  {
    public Coordinate() { }
    public Coordinate(T x, U y)
        => (X, Y) = (x, y);

    public event PropertyChangedEventHandler PropertyChanged;

    private T _X;
    public T X
    {
      get => _X;
      set
      {
        if (value.CompareTo(_X) != 0)
        {
          _X = value;
          PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(X)));
        }
      }
    }

    private U _Y;
    public U Y
    {
      get => _Y;
      set
      {
        if (value.CompareTo(_Y) != 0)
        {
          _Y = value;
          PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Y)));
        }
      }
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM