0

My situation is slightly different than from other posts and I was not able to solve it with the other trhreads. So that why I ask.

I have a class that is obtained from deserializing an XML like:

<?xml version="1.0" encoding="UTF-8"?>
<node>
    <leaf>
        <name>node 1</name>
        <text>text 1</text>
        <url>url 1</url>
    </leaf>
    <leaf>
        <name>node 2</name>
        <text>text 2</text>
        <url>url 2</url>
    </leaf>
</node>

so the class is:

[XmlRoot("node")]
public class csNodeList
{
    public csNodeList()
    {
        Leaf = new csLeafCollection();
    }

    [XmlElement("leaf")]
    public csLeafCollection Leaf
    {
        get;
        set;
    }
}

public class csLeaf
{
    public csLeaf()
    {
        Name ="";
        Description = "";
        Address = "";
    }

    [XmlElement("name")]
    public string Name
    {
        get;
        set;
    }

    [XmlElement("text")]
    public string Description
    {
        get;
        set;
    }

    [XmlElement("url")]
    public string Address
    {
        get;
        set;
    }
}

public class csLeafCollection : System.Collections.ObjectModel.ObservableCollection<csLeaf>
{
}

Then I have 2 Views, one to show all the leafs and one to edit one leaf. I've implemented commit and rollback so I use messaging back and forth to pass the new values and I store the old ones.

To do so I copy the objects a a backup variable and then I modify the ones associated via binding to the XAML view, in this way (in theory) any change to the ViewModel data should be reflected. Also is better because if I commit the changes I just discard the backup variables (this is 90% of the times) and if I need to roll back I copy back from the backup variables.

MainView:

public const string listPropertyName = "list";
private csNodeList _list = new csNodeList();
public csNodeList list
{
    get
        {
        return _list;
    }
    set
    {
        Set(listPropertyName, ref _list, value, false);
    }
}

Using the message I send back the new values of a node and I put them in the correct position:

private void DoSomething(csMessage message)
{
    csMessage rmessage;
    if (message != null)
    {
        switch (message.destination)
        {
            case csMessage.e2MessageDest.updateNode:
            //_editP should be fine.
            list.Leaf[list.Leaf.IndexOf(_editP)].Name = ((csLeaf)message.payload).Name;
            list.Leaf[list.Leaf.IndexOf(_editP)].Text= ((csLeaf)message.payload).Text;
            list.Leaf[list.Leaf.IndexOf(_editP)].Address = ((csLeaf)message.payload).Address;
            RaisePropertyChanged(listPropertyName , null, _list, true);
            break;
        }
    }
}

The code is executed correctly and the item is changed.

BUT the RaisePropertyChanged is ignored. I've tried even just the one with the listPropertyName without any change.

If I save the changes exit from the app and get back I see the new value correctly stored

Can you please help me?

Thanks, Massimo

2 Answers2

0

The reason why your RaisePropertyChanged is ignored is hat yor Leaf class des not implement INotifyOropertyChanged. Commonly the model is wrapped into a view model which then implements INotifyPropertyChanged to notify the view hat something has happened.

However, you also can implement INotifyPropertyChanged on the model class directly. To implement INotifyPropertyChanged each property has to raise the propty changed event.

public string Property {
    get { ... }
    set {
        if (_propertyField == value)
            return;
        _propertyField = value;
        RaisePropertyChanged("Property");
    }
}

The code assumes hat there is a method RaisePropertyChanged which actually taises the PropertyChangedEvent.

AxelEckenberger
  • 16,628
  • 3
  • 48
  • 70
  • This is good... but I use the Set of MVVM, can I use it even in the Model? Also I did not tried that because I though it would have "mixed up" the ViewModel layer and the Model layer, am I wrong? – Massimo Savazzi Feb 23 '12 at 10:30
  • More thoughts: if I add or remove a node it is updated immediately. Is it because the collection is IObservable? can I make the items IObservable? but again: is this messing up the ViewModel layer with the Model layer? – Massimo Savazzi Feb 23 '12 at 10:35
  • As said, normally you would wrap your Model inside a ViewModel (that inherits from ViewModelBase) and implement a property for each of your model's properties. Each of these wrapper properties now adds the property changed notification capability. Instead of writing to a property field (`_propertyField` in the example) you would write to your model's property (i.e. `_model.Property`). Alternatively you **can** implement the notification on the model. Personally I prefer the first approach though, as it allows for additional properties that only relate to the view. – AxelEckenberger Feb 23 '12 at 10:56
0

Thank you everyone for the help. Investigating your suggestion I've found a slightly different solution; as you correctly said the issue is that the leaf fields are not "observable" so they do not generate a notification event.

I've noticed that if I add or Delete a profile the binding is updated.

So what I've decided to do is not to edit directly the leafs but to replace the node. What I do not like is that I have to create a node to replace the old one and this allocates a little bit more memory... but for small data like the one I have it can work without any major impact on the app performance/memory foot print.

Here is what I do:

        csLeaf _leaf = new slLeaf();
        _leaf.Name = ((csLeaf)message.payload).Name; 
        _leaf.Text= ((csLeaf)message.payload).Text; 
        _leaf.URL = ((csLeaf)message.payload).Address; 
        list.Leaf[list.Leaf.IndexOf(_editP)] = _leaf;

To optimized readabilty of code I've enhanced it adding a constructor with 3 parameters so that the code can be:

        csLeaf _leaf = new slLeaf(((csLeaf)message.payload).Name, ((csLeaf)message.payload).Text, ((csLeaf)message.payload).Address);
        list.Leaf[list.Leaf.IndexOf(_editP)] = _leaf;

The constructor is:

public csLeaf(string _name, string _description, string _address) 
{ 
    Name = _name; 
    Description = _description;
    Address = _address; 
}