场景
在WPF中,使用属性Binding到UI自动刷新是常用的手段。平时除了使用普通属性,如public string Value {get; set;}
,还会用到嵌套属性(不知道这个名称对不对),类似这样的形式public string Value => _m.Value;
。这时候数据刷新就会碰到一定的问题。
准备工作
已知类NotifyPropertyChangedHelper,只需继承NotifyPropertyChangedHelper,即可在需要刷新属性时调用OnPropertyChanged()
即可,该方法会自动反射调用该方法的属性名,等价于自动获取nameof(xxx)
。
public class NotifyPropertyChangedHelper : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// 通知属性更改
/// 默认通知方法所在属性
/// </summary>
/// <param name="propertyName"></param>
public void OnPropertyChanged([System.Runtime.CompilerServices.CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public void OnPropertyChanged(PropertyChangedEventArgs eventArgs)
{
PropertyChanged?.Invoke(this, eventArgs);
}
}
若是想代码更简洁,可以安装Fody与PropertyChanged.Fody两个包。则连上面的OnPropertyChanged
方法也省略不写了。
测试界面代码
<Grid>
<StackPanel>
<StackPanel Orientation="Horizontal">
<Label Content="Layer3 => "/>
<Label Content="{Binding Layer3.Value}" Margin="20 0 0 0"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<Label Content="Layer2 => "/>
<Label Content="{Binding Layer2.Value}" Margin="20 0 0 0"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<Label Content="Layer1 => "/>
<Label Content="{Binding Layer1.Value}" Margin="20 0 0 0"/>
</StackPanel>
<Button Content="点击" Click="ButtonBase_OnClick" HorizontalAlignment="Left"/>
</StackPanel>
</Grid>
测试类代码
public class Layer3 : NotifyPropertyChangedHelper
{
public Layer2 SubLayer { get; set; } = new Layer2();
public string Value
{
get => SubLayer.Value;
set => SubLayer.Value = value;
}
}
public class Layer2 : NotifyPropertyChangedHelper
{
public Layer1 SubLayer { get; set; } = new Layer1();
public string Value
{
get => SubLayer.Value;
set => SubLayer.Value = value;
}
}
public class Layer1 : NotifyPropertyChangedHelper
{
public Layer1(string value = "NA")
{
Value = value;
}
public string Value { get; set; }
}
测试后台代码
public void Init()
{
Layer1 = new Layer1("hello");
Layer2 = new Layer2
{
SubLayer = Layer1
};
Layer3 = new Layer3
{
SubLayer = Layer2
};
}
public void Test()
{
var t = DateTime.Now.Millisecond.ToString();
// ex1
//Layer1.Value = t;
// ex2.1
//Layer2.SubLayer.Value = t;
// ex2.2
// Layer2.SubLayer = new Layer1(t);
// ex2.3
//Layer1.Value = t;
//Layer2.SubLayer = Layer1;
// ex3.1
//Layer3.SubLayer.SubLayer = new Layer1(t);
// ex3.2
//Layer3.SubLayer = new Layer2() { SubLayer = new Layer1(t) };
// ex4
//Layer1.Value = t;
//Layer3.OnPropertyChanged(nameof(Layer3.Value));
}
探索
初始界面
测试用例ex1
Layer1.Value = t;
对于Layer1
来说就是常见的普通属性绑定,而Layer2
,Layer3
这是嵌套属性绑定。为稳妥起见,Layer2
,Layer3
均继承了NotifyPropertyChangedHelper
。
public class Layer1
,该情况很明显,绑定失败。
public class Layer1 : NotifyPropertyChangedHelper
,只有Layer1
刷新成功。
测试用例ex2
ex2.1
Layer2.SubLayer.Value = t;
仍然只有Layer1
的数据刷新成功。
ex2.2
Layer2.SubLayer = new Layer1(t);
因为没有修改Layer1
的内容,所以Layer1
不刷新是正常的。
因为重新给Layer2.SubLayer
重新赋值,所以Layer2
刷新成功。
ex2.3
Layer1.Value = t;
Layer2.SubLayer = Layer1;
为了区分ex2.2,特别做了这个对比。如果赋值给Layer2.SubLayer的变量不是新的变量,那么不会有刷新。
测试用例ex3
ex3.1
Layer3.SubLayer.SubLayer = new Layer1(t);
从代码可知,只是对Layer2.SubLayer
重新赋值,所以Layer2
刷新成功。
ex3.2
Layer3.SubLayer = new Layer2() { SubLayer = new Layer1(t) };
这次对Layer3.SubLayer
重新赋值,所以Layer3
刷新成功。
测试用例ex4
Layer1.Value = t;
Layer3.OnPropertyChanged(nameof(Layer3.Value));
主动调用OnPropertyChanged
,刷新指定属性。
结论
由上面的例子可知
- 数据刷新是要通过
OnPropertyChanged
。当属性被赋值时,调用OnPropertyChanged
进行刷新。 - 但是嵌套属性中,下层属性赋值无法触发上层属性调用
OnPropertyChanged
,所以出现下层数据更新,但是上层没有刷新的情况。除非主动刷新,如ex4。但这样代码就显得很冗余。 - 目前我暂时没有好的方法能实现逐层嵌套的属性数据刷新,勉强用重新赋值的方式,刷新最上层。