場景
在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。但這樣代碼就顯得很冗餘。 - 目前我暫時沒有好的方法能實現逐層嵌套的屬性數據刷新,勉強用重新賦值的方式,刷新最上層。