Child 的 Parent 的 C# 触发器 RaisePropertyChanged?
C# Trigger RaisePropertyChanged of Parent from Child?
我有这段代码,其中有我的 ViewModel,而 ViewModel 有一个 属性,它在其中获取其所有属性。
这很粗糙pseudo-code:
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 始终为 null.
我怀疑这是因为 MyClassViewModel 是绑定到 UI 的那个。所以 BaseClass 中的 PropertyChanged 永远不会被绑定。
有没有办法触发 Parent 的 RaisePropertyChanged?
谢谢
我建议在 类 上实施 INotifyPropertyChanged
,然后让 MyClassViewModel
订阅 BaseClass
中的事件并将其转发给 UI:
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
}
首先,您可以这样简化 RaisePropertyChanged
:
public void RaisePropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
所以你不需要写RaisePropertyChanged("Description")
,只写:RaisePropertyChanged()
,propertyName
会自动注入。如果您经常重构,那就太棒了:您不必处理记住整个解决方案中所有 "Title" 和 "Description" 字符串的噩梦:)
其次,如果BaseClass
有PropertyChangedEvent
,可以在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();
}
}
我有这段代码,其中有我的 ViewModel,而 ViewModel 有一个 属性,它在其中获取其所有属性。
这很粗糙pseudo-code:
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 始终为 null.
我怀疑这是因为 MyClassViewModel 是绑定到 UI 的那个。所以 BaseClass 中的 PropertyChanged 永远不会被绑定。
有没有办法触发 Parent 的 RaisePropertyChanged?
谢谢
我建议在 类 上实施 INotifyPropertyChanged
,然后让 MyClassViewModel
订阅 BaseClass
中的事件并将其转发给 UI:
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
}
首先,您可以这样简化 RaisePropertyChanged
:
public void RaisePropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
所以你不需要写RaisePropertyChanged("Description")
,只写:RaisePropertyChanged()
,propertyName
会自动注入。如果您经常重构,那就太棒了:您不必处理记住整个解决方案中所有 "Title" 和 "Description" 字符串的噩梦:)
其次,如果BaseClass
有PropertyChangedEvent
,可以在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();
}
}