bluray
bluray

Reputation: 1953

WPF - binding get property

I have DataGrid with form and I would like to notify another get property. Here is my example:

ViewModel

public ObservableCollection<Data> Data
{
   get => _data;
   set {
     _data = value;
     OnPropertyChanged(nameof(Data));
   }
}

public int Summary => Data.Sum(data => data.Number);

XAML

  <DataGrid ItemsSource="{Binding Data}">
    ...
     <TextBox Title="{Binding Number, UpdateSourceTrigger=PropertyChanged"/>
    ...
  </DataGrid>

<StackPanel>
  <Label Content="Summary: "/>
  <TextBlock Text="{Binding Summary}"/>
</StackPanel>

When I write number to datagrid, Summary is still 0. How to notify Summary property, when I change value in collection? Thanks

Upvotes: 0

Views: 1823

Answers (2)

ChristianMurschall
ChristianMurschall

Reputation: 1671

Perhaps it is easier to use a BindingList. It has an event ListChanged that is raised if

  • one of its items raise itself a PropertyChanged event
  • an item was added
  • an item was removed
  • an item was moved

It will give you information what kind of change was detected (ListChangedType) and where in the list the change happend (NewIndex and OldIndex). This solution implies, that your Data class also implements INotifyPropertyChanged

This would imply the following changes in your viewmodel

// if you do not change the list object, there is no need to raise PropertyChanged in setter
public BindingList<Data> Data { get; set; } = new BindingList<Data>();

// constructor
public ViewModel(){
    // Update property Summary if our list changes.
    List.ListChanged += (sender, arg) => OnPropertyChanged(nameof(Summary));
}

Upvotes: 1

Prateek Shrivastava
Prateek Shrivastava

Reputation: 1937

Check this: https://www.codeproject.com/Tips/694370/How-to-Listen-to-Property-Chang

What you need is a notification whenever any item changes in the Collection.

Update:

Say Data that will appear in the Grid is:

public class Data
{
    public string Name { get; set; }
    private int _Salary;
    public int Salary
    {
        get
        {
            return _Salary;
        }
        set
        {
            _Salary = value;
            SalaryChanged?.Invoke();
        }
    }

    public Action SalaryChanged { get; set; }
}

Instead of ObservableCollection - use this:

public class ObsCollection : ObservableCollection<Data>
{
    private Action SalaryChanged;
    public ObsCollection(Action NotifyPropertyChanged)
    {
        SalaryChanged = NotifyPropertyChanged;
    }

    protected override void InsertItem(int index, Data item)
    {
        item.SalaryChanged = SalaryChanged;
        base.InsertItem(index, item);
    }

    protected override void RemoveItem(int index)
    {            
        base.RemoveItem(index);
        SalaryChanged?.Invoke();
    }

    protected override void ClearItems()
    {
        base.ClearItems();
        SalaryChanged?.Invoke();
    }
}

Finally - In the View Model

public class MainWindowVM : INotifyPropertyChanged
{
    private ObsCollection _Data = null;

    public ObsCollection Data
    {
        get
        {
            return _Data;
        }
        set
        {
            _Data = value;
        }
    }

    public int TotalSalary
    {
        get
        {
            return _Data.Sum(d => d.Salary);
        }
    }

    public MainWindowVM()
    {
        _Data = new ObsCollection(NotifyTotalSalaryChanged);
        LoadData();
    }

    public void LoadData()
    {
        _Data.Add(new Data { Name = "Test1", Salary = 1000 });
        _Data.Add(new Data { Name = "Test2", Salary = 2000 });
        _Data.Add(new Data { Name = "Test3", Salary = 3000 });
        Notify("Data");
    }

    private void NotifyTotalSalaryChanged()
    {
        Notify("TotalSalary");
    }

    #region Property Changed Stuff
    public event PropertyChangedEventHandler PropertyChanged;

    public void Notify(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
    #endregion
}

============================================= Xaml is merely:

<Window x:Class="CstomObserve.MainWindow"
    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:CstomObserve"
    mc:Ignorable="d"
    Title="MainWindow" Height="450" Width="800">
<Window.DataContext>
    <local:MainWindowVM/>
</Window.DataContext>
<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="*"/>
        <RowDefinition Height="Auto"/>
    </Grid.RowDefinitions>
    <DataGrid Grid.Row="0" ItemsSource="{Binding Data, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
    <TextBlock Grid.Row="1" Text="{Binding TotalSalary, UpdateSourceTrigger=PropertyChanged}"/>
</Grid>
</Window>

Upvotes: 2

Related Questions