Gonza Romero
Gonza Romero

Reputation: 81

Is there a way to refresh WPF DataGrid using ObservableCollection and MVVM without informing ItemsSource collection?

This is my first question so I'll do my best.

I'm trying to get a simple WPF DataGrid control to be "refreshed" using ObservableCollection and MVVM as the numerous tutorials on the web explain.

In context, these are my model clases:

(PersonViewModel.cs)

public class PersonViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    private int _id;
    private int _idAddress;
    private string _name;
    private int _age;
    private string _address;

    public int Id
    {
        get { return this._id; }
        set
        {
            if (value != this._id)
            {
                this._id = value;
                OnPropertyChanged();
            }
        }
    }
    public int IdAddress
    {
        get { return this._idAddress; }
        set
        {
            if (value != this._idAddress)
            {
                this._idAddress = value;
                OnPropertyChanged();
            }
        }
    }
    public string Name
    {
        get { return this._name; }
        set
        {
            if (value != this._name)
            {
                this._name = value;
                OnPropertyChanged();
            }
        }
    }
    public int Age
    {
        get { return this._age; }
        set
        {
            if (value != this._age)
            {
                this._age = value;
                OnPropertyChanged();
            }
        }
    }
    public string Address
    {
        get { return this._address; }
        set
        {
            if (value != this._address)
            {
                this._address = value;
                OnPropertyChanged();
            }
        }
    }

    private void OnPropertyChanged([CallerMemberName]String caller = null)
    {
        var handler = PropertyChanged;
        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(caller));
        }
    }
}

(AddressViewModel.cs)

public class AddressViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    private int _id;
    private string _street;
    private int _number;

    public int Id
    {
        get { return this._id; }
        set
        {
            if (value != this._id)
            {
                this._id = value;
                OnPropertyChanged();
            }
        }
    }
    public string Street
    {
        get { return this._street; }
        set
        {
            if (value != this._street)
            {
                this._street = value;
                OnPropertyChanged();
            }
        }
    }
    public int Number
    {
        get { return this._number; }
        set
        {
            if (value != this._number)
            {
                this._number = value;
                OnPropertyChanged();
            }
        }
    }

    private void OnPropertyChanged([CallerMemberName]String caller = null)
    {
        var handler = PropertyChanged;
        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(caller));
        }
    }
}

And here's a representation of the database model (cannot upload pic < 10 posts)

Address

Id
Street
Number

Person

Id
IdAddress
Name
Age
Address ---> this property concatenates Street and Number of Address entity.

So, as you can see, it's a very simple example. Just a proof-of-concept. Problem is, whenever I try to add a new entity to the database (through Entity Framework 6 and LINQ) I must inevitably add that ViewModel entity to the DataGrid's data context.

This is the code working as of today:

public static Person CreatePerson(PersonViewModel personVM)
{
    var person = new Person
    {
        IdAddress = personVM.IdAddress,
        Name = personVM.Name,
        Age = personVM.Age
    };

    try
    {
        using (var context = new OCDemoContext())
        {
            context.Database.Connection.Open();
            context.Person.Add(person);
            context.SaveChanges();
            context.Database.Connection.Close();
        }
    }
    catch
    {
        throw;
    }

    return person;
}

As you can see, the need here is to show in DataGrid a column that concatenates two properties of Address database entity: Street and Number, and giving that value to Address property of PersonViewModel class to show as a column in the DataGrid.

The code that adds the entity to the DataGrid itemssource collection after the insert to the database:

        // Add to the database
        PersonsGateway.CreatePerson(personVM);

        // Update view on DataGrid's itemssource collection
        ViewModel model = this.xGrid.DataContext as ViewModel;

        model.Persons.Insert(0, personVM);

XAML as:

<Window x:Class="OCDemo.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" Height="350" Width="525" xmlns:local="clr-namespace:OCDemo">
<Window.Resources>
    <local:ViewModel x:Key="xViewModel" />
</Window.Resources>
<Grid x:Name="xGrid" DataContext="{StaticResource xViewModel}">
    <DataGrid x:Name="xDataGrid" Grid.Row="0" ItemsSource="{Binding Persons}" AutoGenerateColumns="False">
        <DataGrid.Columns>
            <DataGridTextColumn Binding="{Binding Path=Name}" Header="Name"/>
            <DataGridTextColumn Binding="{Binding Path=Age}" Header="Age"/>
            <DataGridTextColumn Binding="{Binding Path=Address}" Header="Address"/>
        </DataGrid.Columns>
    </DataGrid>
</Grid>

And ViewModel.cs

public class ViewModel
{
    public ObservableCollection<PersonViewModel> Persons { get; set; }

    public ViewModel()
    {
        this.Persons = PersonsGateway.RetrievePersons();
    }
}

And T4 .tt file updated as MSDN - Updating code generation for data binding explains.

So, it is possible to depend only on adding the entity to the database and not to always add the entity to the itemssource collection after that on a ObservableCollection scenario?

Upvotes: 1

Views: 2716

Answers (2)

Phil
Phil

Reputation: 21

Ok, I should of suggested that instead of this code

    PersonsGateway.CreatePerson(personVM);

    ViewModel model = this.xGrid.DataContext as ViewModel;

    model.Persons.Insert(0, personVM);

you should do

PersonsGateway.CreatePerson(personVM);

ViewModel model = this.xGrid.DataContext as ViewModel;

model.Persons.Add(personVM);

Upvotes: 1

Phil
Phil

Reputation: 21

Change your view model to the following

public class ViewModel
{
    public ObservableCollection<PersonViewModel> Persons { get; private set; }

    public ViewModel()
    {
        Persons = new ObservableCollection<PersonViewModel>();
        Persons.AddRange(PersonsGateway.RetrievePersons().ToList());
    }
}

Its important to create the observable collection once and not recreate it, to rely on change notifications, so create it and then use the standard methods to manage it.

Upvotes: 1

Related Questions