0

After learning about ObservableCollection and INotifyPropertyChanged, I'm trying use them to divide my code into MVVM.

But I'm having some trouble with binding outside of code-behind class.

My app have three boxes that let you input a person's name, income, age. Then it will display them on a DataGrid.

xaml:

<Window x:Class="myApp.MainWindow"
 [...]

  <Grid>
     <DataGrid  x:Name="peopleDisplay">
     </DataGrid>
  </Grid>    

</Window>

in MainWindow.xaml.cs (no structure)

 public partial class MainWindow : Window
 {
    private ObservableCollection<Person> peopleList = new ObservableCollection<Person>();

    public MainWindow()
    {
        InitializeComponent();

        peopleDisplay.ItemsSource = peopleList;

    }

    private void btnAddProduct_Click(object sender, RoutedEventArgs e)
    {

      peopleList.Add(new Person { personName = nameBox.text, income = incomebox.text, age = ageBox.text });

      }

    [...]
   }


class People : INotifyPropertyChanged
 {
    private string personName;

    public event PropertyChangedEventHandler PropertyChanged;

    public void NotifyPropertyChanged(string propName)
    {
        if (this.PropertyChanged != null)
            this.PropertyChanged(this, new PropertyChangedEventArgs(propName));
    }

    public string PersonName {
        get
        {
            return this.personName;
        }
        set
        {
            if( this.personName != value)
            {
                this.PersonName = value;
                this.NotifyPropertyChanged("PersonName");
            }

        }
    }
    public int age { get; set; }
    public double income { get; set; }
}

My main questions:

so now Im trying to do two things: add a new function that will calculate the total income of everyone, move the ObservableCollection above to a viewModel class

  1. now in the new viewModel class I have the ObservableCollection personList (instead of inside behind code), but is it wrong to put the calculation method and the properties here too? If I put the calculation properties here this viewModel will be inheriting INotifyPropertyChanged, so when a the totalIncome properties changes it will change the UI automatically. it makes no sense to put it in the person model though, cause that class represent one person.

  2. How do I bind this people List in viewModel to the xaml? If the list is in code-behind I can just do peopleDisplay.ItemsSource = peopleList;, but this viewModel is a class and not a ObservableCollection object, I cant set it to the dataGrid's ItemsSource. Is there a way to bind it in the viewModel class? Im in the progress of learning mvvm so I might be doing something wrong here too. Please advice

user308553
  • 1,238
  • 4
  • 18
  • 32
  • you have to again separate your class, Like ViewModel and Model, and your Model contains the single instance like one person, and then your view model will hold the list of people. – Abin May 13 '16 at 20:04
  • yea the code is before separation, Im trying to separate it now so Im asking the two questions to make sure my approach is correct, cause Im having some trouble with the binding part after separation – user308553 May 13 '16 at 20:07

2 Answers2

3

Your Model class is People. like below:

public class People : INotifyPropertyChanged
{
    private string personName;

    public event PropertyChangedEventHandler PropertyChanged;


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

    public string PersonName
    {
        get
        {
            return this.personName;
        }
        set
        {
            if( this.personName != value)
            {
                this.PersonName = value;
                this.NotifyPropertyChanged();
            }
        }
    }

    public int Age { get; set; }
    public double Income { get; set; }
}

Your ViewModel like below:

public class PeopleViewModel 
{
    Public List<People> ListOfPeople { get; set; }
}

ViewModel can implement INotifyPropertyChanged interface to Notify the View.

Now you can set the data context as PeopleViewModel and bind your ListOfPeople to your DataGrid.

Set DataContext for your View you can do it from XAML or code behind.

Set ItemsSource for your DataGrid in your View .

XAML:

<Window x:Class="myApp.MainWindow" DataContext="{Binding PeopleViewModel }">
    <Grid>
        <DataGrid  x:Name="peopleDisplay" ItemSource={Binding ListOfPeople}>
            ......
        </DataGrid>
    </Grid> 
</Window>

Reference 1

Reference 2

Community
  • 1
  • 1
Abin
  • 2,868
  • 1
  • 23
  • 59
  • ok so in your example for XAML I would do: then in code-behind after initializing peopleViewModel with `PeopleViewModel pVM = new PeopleViewModel()` I will do `dataGrid1.ItemSource = pVM`? Im learning to bind not sure if that's how to do the binding. – user308553 May 13 '16 at 20:26
  • ohh thanks I guess I didnt understand what dataContext was really doing before. One more question though. i see that you use `List` instead of ObservableCollection. Does that mean the code I have with ObservableCollection was unnecessary? – user308553 May 13 '16 at 20:40
  • 1
    You have to use an ObservableCollection in your ViewModel in order to the UI knows when you add some person into the list – Bruno Joaquim May 13 '16 at 20:41
  • 1
    @AbinMathew - just to add a little more your post, if I may - when calling `NotifyPropertyChanged`, I prefer to pass in `nameof(TheProperty)`, rather than hard-coding in a string of a property name. You could run into a whole host of problems if it comes to refactoring property names, and having to go back through all your calls to update the strings. Other than that, good answer. – Geoff James May 14 '16 at 00:26
  • @GeoffJames totally agree with your point please check my edit. Thanks – Abin May 15 '16 at 04:00
  • @BrunoJoaquim it's not necessary to use observableCollection for UI notification it's an option for developer. Implementing INotifyPropertyChanged interface is to notify UI for changes in the background. – Abin May 15 '16 at 04:06
1

1) I dont see any problem with your approach, but, what would happen if someday you want to test the method that calculate the "TotalIncome"? You could separate the calculation in an helper class.

2) First of all, you have to expose the collection in your ViewModel, using public properties. With that being said, you have to declare the binding in your xaml file.

<DataGrid  x:Name="peopleDisplay"
           ItemsSource="{Binding MyPropertyOnViewModel}">
</DataGrid>

Dont forget to set the DataContext of your window with your viewmodel.

Bruno Joaquim
  • 1,423
  • 1
  • 14
  • 15