Moonling
Moonling

Reputation: 59

WPF DataGrid not updating UI when its ItemsSource is updated in code

What should I do to make the WPF DataGrid display changes in its ObservableCollection's items when they are changed in code?

<DataGrid HorizontalAlignment="Left" Height="100" Margin="54,25,0,0" VerticalAlignment="Top" Width="264" 
    IsSynchronizedWithCurrentItem="True" AutoGenerateColumns="False" 
    ItemsSource="{Binding DataSource}" SelectedItem="{Binding SelectedPerson}">
    <DataGrid.Columns>
        <DataGridTextColumn Binding="{Binding FirstName}" />
        <DataGridTextColumn Binding="{Binding LastName}" />
    </DataGrid.Columns>
</DataGrid>

I update SelectedPerson.FirstName in code and the setter of DataSource executes. The current DataSource.FirstName has been updated but the DataGrid does not display the change. If I click on a column header the DataGrid refreshes and shows the changed data. How can I make it refresh promptly when I am using MVVM and I don't have a reference to the DataGrid?

public class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

public class VM : Notifier
{
    private ObservableCollection<Person> dataSource;
    public ObservableCollection<Person> DataSource
    {
        get { return dataSource; }
        set
        {
            dataSource = value;
            OnPropertyChanged("DataSource");
        }
    }

    private Person selectedPerson;
    public Person SelectedPerson
    {
        get { return selectedPerson; }
        set
        {
            selectedPerson = value;
            OnPropertyChanged("SelectedPerson");
        }
    }

    private string string1;
    public string String1
    {
        get { return string1; }
        set
        {
            string1 = value;
            OnPropertyChanged(String1);
        }
    }

    public VM()
    {
        String1 = "abc";

        DataSource = new ObservableCollection<Person>();
        DataSource.Add(new Person { FirstName = "Alpha", LastName = "Apple" });
        DataSource.Add(new Person { FirstName = "Beta", LastName = "Banana" });
        DataSource.Add(new Person { FirstName = "Charlie", LastName = "Cucumber" });
    }

    public ICommand SetSelectedCommand => new RelayCommandBase(SetSelected);
    private void SetSelected(object parameter = null)
    {
        SelectedPerson.FirstName = String1;
        SelectedPerson = SelectedPerson;    // force setter to run
        DataSource = DataSource;            // force ObservableCollection setter to run
    }
}

Upvotes: 0

Views: 4501

Answers (2)

undefined
undefined

Reputation: 142

Your changes are not getting reflected as the item that is getting updated (Person) DOES NOT raise change notifications to update the UI. To do so, you need to update the Person class to Implement the interface INotifyPropertyChanged in the class Person, the same way you implemented in the viewmodel, and this should work.

public class Person:Notifier
{
    private string _firstName;
    public string FirstName
    {
        get { return _firstName; }
        set
        {
            _firstName = value;
            OnPropertyChanged(nameof(this.FirstName));
        }
    }

    private string _lastName;
    public string LastName
    {
        get { return _lastName; }
        set
        {
            _lastName = value;
            OnPropertyChanged(nameof(this.LastName));
        }
    }
}

One more thing. If supported in your version of .NET Framework, you can use the C# nameof to get the property name in string instead of hard-coding the name. This helps when you refactor/rename the properties.

Upvotes: 0

mm8
mm8

Reputation: 169400

The Person class should implement the INotifyPropertyChanged interface:

public class Person : INotifyPropertyChanged
{
    private string _firstName;
    public string FirstName
    {
        get { return _firstName; }
        set
        {
            _firstName = value;
            NotifyPropertyChanged();
        }
    }

    private string _lastName;
    public string LastName
    {
        get { return _lastName; }
        set
        {
            _lastName = value;
            NotifyPropertyChanged();
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
    private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
}

Upvotes: 2

Related Questions