【问题标题】:C# Trigger RaisePropertyChanged of Parent from Child?C#从子级触发父级的RaisePropertyChanged?
【发布时间】:2017-03-25 11:00:44
【问题描述】:

我有这段代码,我有我的 ViewModel,而 ViewModel 有一个属性,它可以获取它的所有属性。

这是粗略的伪代码:

public class MyClassViewModel : INotifyPropertyChanged
{

    public MyClassViewModel ()
    {

    }

    public BaseClass myClassBase { get ; set; }

    public string Title
    {
        get
        {
            return myClassBase.Title;
        }
        set
        {
            myClassBase.Title = value;
            RaisePropertyChanged("Title");
        }
    }

    public string Description
    {
        get
        {
            return myClassBase.Description;
        }
        set
        {
            myClassBase.Description = value;
            RaisePropertyChanged("Description");
        }
    }
}

这是基类:

public class BaseClass
{
    public BaseClass()
    {

    }

    public string Title {get;set;}
    public string Description {get;set;}
}

CheckItemViewModel 是绑定到 UI 的。因此,如果我执行类似 MyClassViewModel .Title = "Test"; 之类的操作,它会正确刷新 UI。

但是,出于特定原因(Javascript - Chakra 接口),我需要执行 MyClassViewModel.myClassBase.Title = "Test" 之类的操作。那么问题在于 UI 不再刷新,因为它没有 RaisePropertyChanged。

即使我在 BaseClass 内部实现了 RaisePropertyChanged,它仍然不起作用。它不起作用,因为 BaseClass 中的PropertyChanged 始终为空

我怀疑这是因为 MyClassViewModel 是绑定到 UI 的。所以 BaseClass 中的 PropertyChanged 永远不会被绑定。

有没有办法触发 Parent 的 RaisePropertyChanged?

谢谢

【问题讨论】:

    标签: c# uwp winrt-xaml inotifypropertychanged chakra


    【解决方案1】:

    我建议在两个类上实现INotifyPropertyChanged,然后让MyClassViewModel 订阅BaseClass 中的事件并将其转发到用户界面:

    public class MyClassViewModel : INotifyPropertyChanged, IDisposable
    {
        private BaseClass myClassBase;
    
        public void Dispose()
        {
            if (myClassBase != null) myClassBase.PropertyChanged -= OnBaseClassPropertyChanged;
        }
    
        public BaseClass MyClassBase {
            get {
                return myClassBase;
            }
    
            set {
                if (myClassBase != null) myClassBase.PropertyChanged -= OnBaseClassPropertyChanged;
                myClassBase = value;
                myClassBase.PropertyChanged += OnBaseClassPropertyChanged;
            }
        }
    
        private void OnBaseClassPropertyChanged(object sender, PropertyChangedEventArgs args) {
            RaisePropertyChanged(args.PropertyName);
        }
    
        // forwarded properties (Title and Description) go here
    }
    

    【讨论】:

      【解决方案2】:

      首先,您可以这样简化RaisePropertyChanged

      public void RaisePropertyChanged([CallerMemberName] string propertyName = null)
      {
          PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
      }
      

      所以你不需要写RaisePropertyChanged("Description"),而只需:RaisePropertyChanged()propertyName会自动注入。如果你经常重构,那就太棒了:你不必面对记住整个解决方案中所有“标题”和“描述”字符串的噩梦:)

      其次,如果BaseClassPropertyChangedEvent,可以在MyClassViewModel收听。

      myClassBase.PropertyChanged += (s, e) => { RaisePropertyChanged(e.PropertyName); };
      

      但是,如果你不立即在MyClassViewModel 的构造函数中注入myClassBase,或者如果myClassBase 可以在某个时候改变,事情就会变得有点复杂。

      您还必须创建MyClassViewModel 才能实现INotifyPropertyChanging

      public event PropertyChangingEventHandler PropertyChanging;
      
      public void RaisePropertyChanging([CallerMemberName] string propertyName = null)
      {
          PropertyChanging?.Invoke(this, new PropertyChangingEventArgs(propertyName));
      }
      

      您还必须为myClassBase 发出通知:

      public BaseClass myClassBase
      {
          get { return _myClassBase; }
          set
          {
              RaisePropertyChanging();
              _myClassBase = value;
              RaisePropertyChanged();
          }
      }
      private BaseClass _myClassBase;
      

      那么,你只需要这段代码:

      public MyClassViewModel()
      {
          PropertyChanging += OnPropertyChanging;
          PropertyChanged += OnPropertyChanged;
      }
      
      private void OnPropertyChanging(object sender, PropertyChangingEventArgs e)
      {
          if (e.PropertyName != nameof(MyClassViewModel.myClassBase))
              return; //or do something with the other properties
      
          if (myClassBase == null)
              return;
      
          myClassBase.PropertyChanged -= OnMyBaseClassPropertyChanged;
      }
      
      private void OnPropertyChanged(object sender, PropertyChangedEventArgs e)
      {
          if (e.PropertyName != nameof(MyClassViewModel.myClassBase))
              return; //or do something with the other properties
      
          if (myClassBase == null)
              return;
      
          myClassBase.PropertyChanged += OnMyBaseClassPropertyChanged;
      }
      
      private void OnMyBaseClassPropertyChanged(object sender, PropertyChangedEventArgs e)
      {
          RaisePropertyChanged(e.PropertyName);
      }
      

      注意:我用的是 C#-6.0 nameof() 运算符,希望你能用,简直太棒了!

      编辑:

      这里有一个演示正确功能的简单测试方法:

          [TestMethod]
          public void ChildClassPropertyChanged()
          {
              var bc = new BaseClass();
      
              var c = new MyClassViewModel();
      
              bc.Title = "t1";
      
              c.myClassBase = bc;
      
              Assert.AreEqual("t1", c.Title);
      
              c.Title = "t2";
              Assert.AreEqual("t2", c.Title);
      
              c.myClassBase.Title = "t3";
              Assert.AreEqual("t3", c.Title);
      
              c.myClassBase = new BaseClass();
      
              bc.Title = "t4";
              Assert.AreEqual(null, c.Title);
      
              c.myClassBase.Title = "t5";
              Assert.AreEqual("t5", c.Title);
          }
      

      请记住,如果您设置了 null myClassBase,则在您的属性的 getter 和 setter 中,代码会抛出 NullReferenceException。也许你应该这样修改它:

      public string Title
      {
          get
          {
              return myClassBase?.Title;
          }
          set
          {
              if (myClassBase != null)
                      myClassBase.Title = value;
              RaisePropertyChanged();
          }
      }
      

      【讨论】:

        猜你喜欢
        • 2013-11-24
        • 2016-10-04
        • 2019-02-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多