简体   繁体   中英

Why INotifyPropertyChanged does not update local variables in code behind

I have two classes, Node and Connection. Node has its name and number as properties, and Connection is literally about connection information between two nodes. I using the two classes as OservableCollection declared static. CollectionPropertyChangedEventHandler for NodeNubmer and NodeName properties is implemented in Node class, so UI and node information code behind is changed when any node's information is changed in UI through data binding. However, the node information in Connection does not changed such as when node's name is changed in UI. Here is part of code. How to update the node information in Connection when it's changed in Node?

public Class Node: INotifyPropertyChanged
{
   private int _nodeNumber;
   private string _nodeName;

   public int NodeNumber
   {
        get
        {
            return _nodeNumber;
        }
        set
        {
            _nodeNumber = value;
            OnPropertyChanged("NodeNumber");
        }
   }

   public string NodeName
   {
        get
        {
            return _nodeName;
        }
        set
        {
            _nodeName = value;
            OnPropertyChanged("NodeName");
        }
   }

   public event PropertyChangedEventHandler PropertyChanged;

   // constructor
   public Node(int nodeNumber, string nodeName)
   {
       _nodeNumber = nodeNumber;
       _nodeName = nodeName;
   }

   public void OnPropertyChanged(string propertyName)
   {
        PropertyChangedEventHandler handler = PropertyChanged;

        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(propertyName));   
        }

   }
}

public Class Connection: INotifyPropertyChanged
{
   private int _sourceNumber;
   private int _destNamber;
   private string _sourceName;
   private string _destName;

   public int SourceNumber
   {
        get
        {
            return _sourceNumber;
        }
        set
        {
            _sourceNumber = value;
            OnPropertyChanged("SourceNumber");
        }
   }


   public int DestNumber
   {
        get
        {
            return _destNumber;
        }
        set
        {
            _destNumber = value;
            OnPropertyChanged("DestNumber");
        }
   }

   public string SourceName
   {
        get
        {
            return _sourceName;
        }
        set
        {
            _sourceName = value;
            OnPropertyChanged("SourceName");
        }
   }

   public string DestName
   {
        get
        {
            return _destName;
        }
        set
        {
            _destName = value;
            OnPropertyChanged("DestName");
        }
   }

   public event PropertyChangedEventHandler PropertyChanged;

   // constructor
   public Connection(int sourceNumber, int destNumber)
   {
       _sourceNumber = sourceNumber;
       _destNumber = destNumber;

       // I guess this code is the reason why SourceName/DestName isn't changed when its node's name changed in UI, but I don't know how to fix this code.
       // NodeInfo is ObservableCollection of Node
       SourceName = NodeInfo.Collection[sourceNumber-1].NodeName;
       DestName = NodeInfo.Collection[destNumber-1].NodeName;
   }

   public void OnPropertyChanged(string propertyName)
   {
        PropertyChangedEventHandler handler = PropertyChanged;

        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(propertyName));   
        }

   }
}

I completely agree with @mike z. I think your life would be much easier if your connection class looked like this (BaseVM is a class that implements the INotifyPropertyChanged interface, left out here for simplicity):

public class Connection :  BaseVM
{
    public Connection(string name, Node a, Node b)
    {
        this.Name = name;
        this.NodeA = a;
        this.NodeB = b;
    }

    string _name;
    public string Name
    {
        get { return _name; }
        set
        {
            if (_name == value) return;
            _name = value;
            Notify( "Name" );
        }
    }

    Node _nodeA;
    public Node NodeA
    {
        get { return _nodeA; }
        set
        {
            if (_nodeA == value) return;
            _nodeA = value;
            Notify( "NodeA" );
        }
    }


    Node _nodeB;
    public Node NodeB
    {
        get { return _nodeB; }
        set
        {
            if (_nodeB == value) return;
            _nodeB = value;
            Notify( "NodeB" );
        }
    }

Then you could use {Binding NodeA.Name} in your XAML (assuming a Connection as your data context).

If you really wanted to have data binding inside your Connection class, you can do it manually . Your bound targets will need to be dependency properties and your class will need to derive from DependencyObject:

    public string AName
    {
        get { return (string)GetValue (ANameProperty); }
        set { SetValue (ANameProperty, value); }
    }

    public static readonly DependencyProperty ANameProperty =
        DependencyProperty.Register ("AName", typeof (string), typeof (Connection), new PropertyMetadata (""));

And then you have to manually set up the bindings:

    public Connection(string name, Node a, Node b)
    {
        // ...

        this.AName = a.Name;

        // to keep these updated, there needs to be a binding between the two
        var binding = new Binding ("Name");
        binding.Source = this.NodeA;
        BindingOperations.SetBinding (this, ANameProperty, binding);
    }

But as @mike z suggests, the former is probably more what you're looking for. I have a fully functioning example here:

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM