steam23
steam23

Reputation: 69

How to get View to update when the ViewModel gets changed

I'm building a WPF application to manage a student database stored in a SharePoint list. I'm new to using MVVM and have gone through a couple tutorials. I have gotten as far as having successfully created a view and model and have managed to bind it to a datagrid control. What I would like to do is to update the data in the view based on the output of a combobox.

Here's my model:

using System.ComponentModel;

namespace StudentManagement.Model
{
public class Student : INotifyPropertyChanged
{
    private string _Title;
    private string _FullName;

    public string Title
    {
        get { return _Title; }
        set
        {
            if (_Title != value)
            {
                _Title = value;
                RaisePropertyChanged("Title");
            }
        }
    }

    public string FullName
    {
        get { return _FullName; }
        set
        {
            if (_FullName != value)
            {
                _FullName = value;
                RaisePropertyChanged("FullName");
            }

        }

    }



    public event PropertyChangedEventHandler PropertyChanged;

    private void RaisePropertyChanged(string property)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(property));
        }
    } 
  }
}

Here's the viewmodel:

using System.Collections.ObjectModel;
using StudentManagement.Model;
using SP = Microsoft.SharePoint.Client;
using StudentManagement.publicClasses;

namespace StudentManagement.ViewModel
{
    public class StudentViewModel
    {
        public ObservableCollection<Student> Students { get; set; }

        public void LoadStudents(string query)
        {


           ObservableCollection<Student> _students = new 
        ObservableCollection<Student>();

        SP.ClientContext ctx = clientContext._clientContext;

        SP.CamlQuery qry = new SP.CamlQuery();
        qry.ViewXml = query;
        SP.ListItemCollection splStudents = 
 ctx.Web.Lists.GetByTitle("Students").GetItems(qry);
        ctx.Load(splStudents);
        ctx.ExecuteQuery();

        foreach (SP.ListItem s in splStudents)
        {
            _students.Add(new Student { Title = (string)s["Title"], FullName = (string)s["FullName"] });
        }
        Students = _students;

        }
    }
}

Here's my XAML

<UserControl x:Class="StudentManagement.Views.StudentsView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             mc:Ignorable="d" 
  
             xmlns:local="clr-namespace:StudentManagement.Views" >


    <Grid>

        <StackPanel HorizontalAlignment="Left" Width="200" >
            <TextBox Name="txtSearch" AcceptsReturn="True" ></TextBox>
            <ComboBox Name="cmbStatus"  SelectionChanged="cmbStatus_SelectionChanged" SelectedIndex="0">
                <ComboBoxItem>Active</ComboBoxItem>
                <ComboBoxItem>Inquiring</ComboBoxItem>
                <ComboBoxItem>Inactive</ComboBoxItem>
                <ComboBoxItem>Monitoring</ComboBoxItem>
            </ComboBox>
            <DataGrid Name="dgStudentList" ItemsSource="{Binding Path=Students}" AutoGenerateColumns="False">
                <DataGrid.Columns>
                    <DataGridTextColumn Header="Name" Binding="{Binding Title, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Width="100"/>
                    <DataGridTextColumn Header="Parent" Binding="{Binding FullName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Width="100" />
                </DataGrid.Columns>
            </DataGrid>
        </StackPanel>
    </Grid>
</UserControl>

...and the code behind for the view:

using System.Windows.Controls;
using StudentManagement.ViewModel;

namespace StudentManagement.Views
{
    /// <summary>
    /// Interaction logic for StudentsView.xaml
    /// </summary>
    public partial class StudentsView : UserControl
    {
        private StudentViewModel _viewModel = new 
StudentViewModel();

    public StudentsView()
    {
            InitializeComponent();
            DataContext = _viewModel;
        }

        private void cmbStatus_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            string combotext = ((sender as ComboBox).SelectedItem as ComboBoxItem).Content as string;   
            string qry = @"<View>  
                        <Query> 
                            <Where><Eq><FieldRef Name='Current_x0020_Status' /><Value Type='Choice'>" + combotext + @"</Value></Eq></Where> 
                        </Query> 
                       </View>";

            _viewModel.LoadStudents(qry);

        }
    }
}

As it stands now, the students get loaded into the datagrid on load just fine. When the event cmbStatus_SelectionChanged fires i've done tests and I can see that the LoadStudents function fires and returns the correct number of entries, but nothing gets updated on the datagrid.

I'm sure this a noob mistake and I'm missing something basic but this one is doing my head in and I'd appreciate any guidance.

Upvotes: 0

Views: 2137

Answers (2)

Mathivanan KP
Mathivanan KP

Reputation: 2044

You are initializing your Students collection every time if the ComboBox's selection changed.

ObservableCollection<Student> _students = new ObservableCollection<Student>();

You should not do this with a bound collection in ViewModel. You can clear the collection and add new items like this.

public class StudentViewModel
{
    public ObservableCollection<Student> Students { get; set; } = new ObservableCollection<Student>();

    public void LoadStudents(string query)
    {
        Students.Clear();

        SP.ClientContext ctx = clientContext._clientContext;

        SP.CamlQuery qry = new SP.CamlQuery();
        qry.ViewXml = query;
        SP.ListItemCollection splStudents =  ctx.Web.Lists.GetByTitle("Students").GetItems(qry);
        ctx.Load(splStudents);
        ctx.ExecuteQuery();

        foreach (SP.ListItem s in splStudents)
        {
            Students.Add(new Student { Title = (string)s["Title"], FullName = (string)s["FullName"] });
        }
    }
}

Upvotes: 2

Andy
Andy

Reputation: 30418

Since StudentViewModel.LoadStudents() changes the value of the Students property, the view model needs to notify the view that this changed. You can do this by having StudentViewModel implement INotifyPropertyChanged (just like Student does). The DataGrid will subscribe to the PropertyChanged event, and will update its contents when that event is fired.

Upvotes: 2

Related Questions