Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WPF: Bound datagrid does not update items properties

I'm trying to implement my first MVVM application. I could bound the data in a datagrid, but the changes I make in items does not fire the RaisePropertyChanged method of the Model.

This is My ViewModel

    public class UsersViewModel : BaseViewModel
{
    private static TOPEntities _context;
    private ObservableCollection<UserModel> _usersCollection;

           public UsersViewModel()
    {
        _usersCollection = new ObservableCollection<UserModel>(GetAllUsers());

    }
          public ObservableCollection<UserModel> UsersCollection
    {
        get { return _usersCollection; }
        set
        {
            if (_usersCollection != value)
            {
                _usersCollection = value;
                RaisePropertyChanged(() => UsersCollection);
            }
        }
    }
public static List<UserModel> GetAllUsers()
    {
        using (_context = new TOPEntities())
        {

            return _context.Users.Select
             (user => new UserModel
             {
                 Id_User = user.Id_User,
                 Name = user.Name,
                 Username = user.Username,
                 Language = user.Language,
                 Password = user.Password,
                 Profile = user.Profile

             }).ToList();

        }
    }

The Model, implements NotificationObject class that provides the INotifyPropertyChanged

    public class UserModel : NotificationObject
{
    #region Construction
    /// Constructs the default instance of a UserModel
    public UserModel()
    {

    }
    #endregion

    #region Model Attributes

    private int _id_User;
    private string _username;
    private string _password;
    private string _profile;
    private string _name;
    private string _language;

    #endregion

    #region Properties



    public int Id_User
    {
        get { return _id_User; }
        set
        {
            if (_id_User != value)
            {
               _id_User = value;
               RaisePropertyChanged(() => Id_User);
            }
        }
    }


    public string Username
    {
        get { return _username; }
        set
        {
            if (_username != value)
            {
                _username = value;
                RaisePropertyChanged(() => Id_User);
            }
        }
    }

    public string Password
    {
        get { return _password; }
        set
        {
            if (_password != value)
            {
                _password = value;
                RaisePropertyChanged(() => Id_User);
            }
        }
    }

    public string Profile
    {
        get { return _profile; }
        set
        {
            if (_profile != value)
            {
                _profile = value;
                RaisePropertyChanged(() => Id_User);
            }
        }
    }

    public string Name
    {
        get { return _name; }
        set
        {
            if (_name != value)
            {
                _name = value;
                RaisePropertyChanged(() => Name);
            }
        }
    }

    public string Language
    {
        get { return _language; }
        set
        {
            if (_language != value)
            {
                _language = value;
                RaisePropertyChanged(() => Language);
            }
        }
    }

    #endregion


}

}

And finally, the View:

<Window x:Class="TOP2.Views.UsersView"
    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"
    mc:Ignorable="d"
    xmlns:viewModels="clr-namespace:TOP2.ViewModels"
    xmlns:local="TOP2"
    Title="Sample App"
    WindowStartupLocation="CenterScreen"
    Height="459"
    Width="795">
<Window.Resources>
    <viewModels:UsersViewModel x:Key="Windows1ViewModel" />
</Window.Resources>
<Grid DataContext="{StaticResource Windows1ViewModel}">
    <DataGrid ItemsSource="{Binding UsersCollection, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" AutoGenerateColumns="False" HorizontalAlignment="Left" Margin="81,51,0,0" VerticalAlignment="Top" Height="332" Width="622">
        <DataGrid.Columns>
            <DataGridTextColumn Binding="{Binding Username, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
            <DataGridTextColumn Binding="{Binding Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
        </DataGrid.Columns>

    </DataGrid>

</Grid>

What do I am forgetting or doing wrong?

Thanks in advance!

Oscar

like image 758
Oscar Mateu Avatar asked Jun 20 '13 10:06

Oscar Mateu


3 Answers

Thank you so much Loetn and Andras Sebö, your clues resulted very helpful! The solution below has been which I have adopted and it has worked perfectly!!!

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel; // ObservableCollection
using System.ComponentModel; // INotifyPropertyChanged
using System.Collections.Specialized; // NotifyCollectionChangedEventHandler
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ObservableCollectionTest
{
    class Program
    {
        static void Main(string[] args)
        {
            // ATTN: Please note it's a "TrulyObservableCollection" that's instantiated. Otherwise, "Trades[0].Qty = 999" will NOT trigger event handler "Trades_CollectionChanged" in main.
            // REF: http://stackoverflow.com/questions/8490533/notify-observablecollection-when-item-changes
            TrulyObservableCollection<Trade> Trades = new TrulyObservableCollection<Trade>();
            Trades.Add(new Trade { Symbol = "APPL", Qty = 123 });
            Trades.Add(new Trade { Symbol = "IBM", Qty = 456});
            Trades.Add(new Trade { Symbol = "CSCO", Qty = 789 });

        Trades.CollectionChanged += Trades_CollectionChanged;
        Trades.ItemPropertyChanged += PropertyChangedHandler;
        Trades.RemoveAt(2);

        Trades[0].Qty = 999;

        Console.WriteLine("Hit any key to exit");
        Console.ReadLine();

        return;
    }

    static void PropertyChangedHandler(object sender, PropertyChangedEventArgs e)
    {
        Console.WriteLine(DateTime.Now.ToString() + ", Property changed: " + e.PropertyName + ", Symbol: " + ((Trade) sender).Symbol + ", Qty: " + ((Trade) sender).Qty);
        return;
    }

    static void Trades_CollectionChanged(object sender, EventArgs e)
    {
        Console.WriteLine(DateTime.Now.ToString() + ", Collection changed");
        return;
    }
}

#region TrulyObservableCollection
public class TrulyObservableCollection<T> : ObservableCollection<T>
    where T : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler ItemPropertyChanged;

    public TrulyObservableCollection()
        : base()
    {
        CollectionChanged += new NotifyCollectionChangedEventHandler(TrulyObservableCollection_CollectionChanged);
    }

    void TrulyObservableCollection_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
        if (e.NewItems != null)
        {
            foreach (Object item in e.NewItems)
            {
                (item as INotifyPropertyChanged).PropertyChanged += new PropertyChangedEventHandler(item_PropertyChanged);
            }
        }
        if (e.OldItems != null)
        {
            foreach (Object item in e.OldItems)
            {
                (item as INotifyPropertyChanged).PropertyChanged -= new PropertyChangedEventHandler(item_PropertyChanged);
            }
        }
    }

    void item_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        NotifyCollectionChangedEventArgs a = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset);
        OnCollectionChanged(a);

        if (ItemPropertyChanged != null)
        {
            ItemPropertyChanged(sender, e);
        }
    }
}
#endregion

#region Sample entity
class Trade : INotifyPropertyChanged
{
    protected string _Symbol;
    protected int _Qty = 0;
    protected DateTime _OrderPlaced = DateTime.Now;

    public DateTime OrderPlaced
    {
        get { return _OrderPlaced; }
    }

    public string Symbol
    {
        get
        {
            return _Symbol;
        }
        set
        {
            _Symbol = value;
            NotifyPropertyChanged("Symbol");
        }
    }

    public int Qty
    {
        get
        {
            return _Qty;
        }
        set
        {
            _Qty = value;
            NotifyPropertyChanged("Qty");
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    private void NotifyPropertyChanged(String propertyName = "")
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}
#endregion
}
like image 114
Oscar Mateu Avatar answered Nov 13 '22 05:11

Oscar Mateu


Bind this event to the CollectionChanged event of your ObservableCollection:

private void ObservableCollection_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            if (e.NewItems != null)
            {
                foreach (var item in e.NewItems)
                {
                    item.PropertyChanged += this.Item_PropertyChanged;
                }
            }

        if (e.OldItems != null)
        {
            foreach (var item in e.OldItems)
            {
                item.PropertyChanged -= this.Item_PropertyChanged;
            }
        }
    }

    private void Item_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        // do something
    }
like image 3
Loetn Avatar answered Nov 13 '22 03:11

Loetn


Because the items are in the collection and the collection itself does not change. You have to subscribe in the UsersViewModel class every UserModel changes before you add it to the collection.

Here is a possible solution:

http://social.msdn.microsoft.com/Forums/vstudio/en-US/c03b9edd-e9a9-4674-82d3-56caaf67d6d9/observablecollectiont-listen-for-changes-in-child-elements

like image 2
Andras Sebo Avatar answered Nov 13 '22 04:11

Andras Sebo