【发布时间】:2018-07-30 22:20:03
【问题描述】:
根据this answer,我不应该为 NotifyPropertyChanges 冒泡层次结构而烦恼,但我仍然无法使用这样的(简化测试)结构:
数据保存类
public class TestNotifyChanged : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string _Test = "default";
public string Test
{
get
{
return _Test;
}
set
{
if(_Test!=value)
{
_Test = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Test"));
}
}
}
}
使用该 Test-Class 和 Test-Property 的 ViewModel:
public class ViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private TestNotifyChanged tnc = new TestNotifyChanged(); // only to init, otherwise VS screams at me
public ViewModel(TestNotifyChanged tnc)
{
tnc = tnc; // getting an instance of TestNotifyChanged from "Master" passed in, which hopefully will be replaces by a singleton class.
}
private string _Test;
public string Test
{
get
{
return tnc.Test; // this might be the crucial part!?
}
set
{
if (_Test != value) // never hits that, as I would expect, but you never know..
{
_Test = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Test")); // of course also never hit, as expected
}
}
}
}
最后是我的 MainWindow cs
public partial class MainWindow : Window
{
TestNotifyChanged tnc;
public MainWindow()
{
InitializeComponent();
tnc = new TestNotifyChanged();
DataContext = new ViewModel(tnc); // pass in my Test-Object that has the Values.
}
private void ButtonGet_Click(object sender, RoutedEventArgs e)
{
tnc.Test = "new Value";
MessageBox.Show($"{tnc.Test}"); // got "new Value" here!
}
}
在 xaml 中,除了那个按钮之外,我还有一个简单的 TextBlock,它绑定到 ViewModel 的测试属性:
<TextBlock x:Name="OutputId" Text="{Binding Path=Test, Mode=OneWay}"/>
现在发生了什么:
默认值“default”显示在TextBlock中。
当我点击按钮时,messageBox 显示“新值”
TextBlock 未更新为“新值”
我想要达到的目标:
- 看起来很简单:TextBlock 应该更新为“新值”
当我直接在 ViewModel 上设置测试值 时,我可以轻松完成这项工作 - 但这似乎不正确,并且与我认为可以构建我的应用程序/代码的方式相去甚远。未来的目标是拥有一个拥有大部分数据的单例(静态行不通)“RecordStore”(并从 API、本地数据库或仅从内存中获取数据,如果其中任何一项完成)
所以问题是:
为什么 NotifyPropertyChange 没有冒泡到 View/ViewModel?
还是有其他我没有看到的问题?
我读过INotifyPropertyChanged bubbling in class hierarchy 和 What is a good way to bubble up INotifyPropertyChanged events through ViewModel properties with MVVM? 和 https://docs.microsoft.com/en-us/dotnet/framework/winforms/how-to-implement-the-inotifypropertychanged-interface 和 OnPropertyChange called but not taking any effect on UI
大多数问题也很老了......
编辑:
我以这种方式尝试了@MineR 的建议:
// made tnc public in ViewModel
public TestNotifyChanged tnc = new TestNotifyChanged();
// changed Binding directly to that (and it's Property):
<TextBlock x:Name="OutputId" Text="{Binding Path=tnc.Test, Mode=OneWay}"/>
不幸的是,我现在连默认都没有,所以我一定是误会了。
EDIT2:
我在第一次编辑中做错了一件事:
// this isn't recognized as bindable parameter:
public TestNotifyChanged tnc = new TestNotifyChanged();
// it instead has to be
public TestNotifyChanged tnc { get; }
我做了TNC,去掉了本地的Test参数,直接绑定到Path=TNC.Test
所以我明白,PropertyChanges 不会按照我希望/认为的方式冒泡,最好直接绑定到嵌套对象。
【问题讨论】:
-
在您的 ViewModel 中,
_Test = value;实际上应该是tnc.Test = value;? -
@Sach 啊,好的。我会试试的! - 但
tnc.Text是唯一一个击中set{}的源 - 无论如何都不会被击中!? - 尝试后:不,没有任何改变。 -
在你拥有的代码中根本没有冒泡。如果你想让它在不冒泡的情况下工作,你必须公开 tnc,并绑定到 tnc.Test - 这就是你引用的第一个问题中所做的。或者,您可以通过订阅 tnc.PropertyChanged 并在引发 tnc.PropertyChanged 时引发 PropertyChanged 来冒泡它。如果您可以摆脱第一个选项,那就这样做 - 它更简单。
-
@MineR 不幸的是,我无法使用第一个选项完成它。你能给我举个第二种选择的例子吗? (虽然我更喜欢第一个选项....会更有意义)
-
如果你把tnc设为public,它也应该是一个要绑定的属性:
public TestNotifyChanged tnc {get;set;}。它对我有用,但是你的 VM 中的Test属性不再使用,所以它并不是真正的“冒泡”。