【问题标题】:Why INotifyPropertyChanged does not update local variables in code behind为什么 INotifyPropertyChanged 不更新后面代码中的局部变量
【发布时间】:2023-03-09 20:44:01
【问题描述】:

我有两个类,节点和连接。节点有它的名称和编号作为属性,连接字面意思是关于两个节点之间的连接信息。我使用这两个类作为 OservableCollection 声明为静态的。 NodeNubmer 和 NodeName 属性的 CollectionPropertyChangedEventHandler 是在 Node 类中实现的,所以当任何一个节点的信息在 UI 中通过数据绑定发生变化时,UI 和后面的节点信息代码都会发生变化。但是,Connection 中的节点信息不会更改,例如在 UI 中更改节点名称时。这是代码的一部分。在Node中更改时如何更新Connection中的节点信息?

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));   
        }

   }
}

【问题讨论】:

  • 如果 Connection 有两个 Node 属性(例如 Source 和 Destination),并且您的绑定类似于 {Binding Path=Source.NodeName} 而不是 {Binding Path=SourceName},它可能会按照您的意愿工作。
  • 我修正了拼写错误。 SourceName = NodeInfo.Collection[sourceNumber-1].NodeName; DestName = NodeInfo.Collection[destNumber-1].NodeName;很抱歉让您感到困惑,但我想这与 UI(xaml) 和代码隐藏 (c#) 之间的数据绑定无关。这是对象代码隐藏之间的问题,因为 ConnectionInfo.Collection 也不会改变,所以 UI 不会改变。感谢您的帮助。

标签: c# data-binding code-behind inotifypropertychanged local-variables


【解决方案1】:

我完全同意@mike z。如果你的连接类看起来像这样,我认为你的生活会容易得多(BaseVM 是一个实现 INotifyPropertyChanged 接口的类,为简单起见,此处省略):

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" );
        }
    }

然后你可以在你的 XAML 中使用{Binding NodeA.Name}(假设一个连接作为你的数据上下文)。

如果您真的想在您的 Connection 类中进行数据绑定,you can do it manually。您的绑定目标需要为 dependency properties,并且您的类需要从 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 (""));

然后你必须手动设置绑定:

    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);
    }

但正如@mike z 所暗示的,前者可能更符合您的要求。我在这里有一个功能齐全的例子:

【讨论】:

  • 非常感谢!现在,它工作正常。特别是,您功能齐全的示例非常有帮助。我真的很感谢你。
  • 欢迎堆栈溢出! (另外,您可能想对任何帮助您的 cmets 进行投票,如果您发现它解决了您的问题,请接受答案)。希望您能够使用非手动绑定版本。
猜你喜欢
  • 2010-10-25
  • 1970-01-01
  • 2012-05-09
  • 2019-06-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多