.NET MAUI视图间数据绑定:模型属性变更未同步问题排查
.NET MAUI中子视图无法感知绑定模型属性变更的问题排查
在.NET MAUI中,我将父视图IdentifyControl的IdentHeightModel模型绑定到子视图ChildControl,但当模型的IdentHeightPortrait或IdentHeightLandscape属性变更时,子视图无法感知到这些变化。以下是相关代码片段,请问遗漏了什么?
IdentifyControl XAML代码
<ContentView xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:identify="clr-namespace:Views.Identify" x:DataType="identify:IdentifyControl" x:Name="IdentifyCntrl" x:Class="Views.Identify.IdentifyControl"> <identify:ChildControl x:Name="identifyAttrib" IdentifyHeightModel="{Binding IdentHeightModel, Source={x:Reference IdentifyCntrl}, x:DataType='identify:IdentifyControl'}"/> </ContentView>
IdentifyControl后台关键代码
public partial class IdentifyControl : ContentView { public IdentifyControl() { InitializeComponent(); SizeChanged += IdentifyControl_SizeChanged; } public static readonly BindableProperty IdentHeightModelProperty = BindableProperty.Create(nameof(IdentHeightModel), typeof(IdentHeightModel), typeof(IdentifyControl), null); public IdentHeightModel? IdentHeightModel { get => GetValue(IdentHeightModelProperty) as IdentHeightModel; set { SetValue(IdentHeightModelProperty, value); } } private void IdentifyControl_SizeChanged(object? sender, EventArgs e) { if (IdentHeightModel == null) SetIdentifyHeightModel(); //省略部分代码,横竖屏切换时执行以下逻辑 int ht = GetControlHt(); if (_isPortrait) IdentHeightModel.IdentHeightPortrait = ht; else IdentHeightModel.IdentHeightLandscape = ht; } private void SetIdentifyHeightModel() { IdentHeightModel = new IdentHeightModel(); } }
ChildControl后台关键代码
public partial class ChildControl : ContentView { private IdentHeightModel? _identHeightModel = null; public ChildControl() { InitializeComponent(); } public static readonly BindableProperty IdentifyHeightModelProperty = BindableProperty.Create(nameof(IdentifyHeightModel), typeof(IdentHeightModel), typeof(ChildControl), null, propertyChanged: (bindable, oldValue, newValue) => { var control = (ChildControl)bindable; control.IdentifyHeightModel = newValue as IdentHeightModel; }); public IdentHeightModel? IdentifyHeightModel { get => GetValue(IdentifyHeightModelProperty) as IdentHeightModel; set { //变更未传递到此处 SetValue(IdentifyHeightModelProperty, value); } } }
IdentHeightModel代码
public class IdentHeightModel : INotifyPropertyChanged { private int _identHeightPortrait = 0; private int _identHeightLandscape = 0; /// <summary> /// 竖屏模式下的实际高度 /// </summary> public int IdentHeightPortrait { get => _identHeightPortrait; set { _identHeightPortrait = value; OnPropertyChanged(); } } /// <summary> /// 横屏模式下的实际高度 /// </summary> public int IdentHeightLandscape { get => _identHeightLandscape; set { _identHeightLandscape = value; OnPropertyChanged(); } } public IdentHeightModel() { IdentHeightPortrait = 0; IdentHeightLandscape = 0; } protected void OnPropertyChanged([CallerMemberName] string? propertyName = null) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); public event PropertyChangedEventHandler? PropertyChanged; }
问题根源与修复方案
核心问题
ChildControl仅在IdentifyHeightModel的对象引用发生变更时,才会通过BindableProperty的propertyChanged回调收到通知。但模型内部的IdentHeightPortrait/IdentHeightLandscape属性变更时,ChildControl没有监听这些内部属性的变更事件,因此无法感知变化。
修复步骤
修改ChildControl的代码,添加模型内部属性变更的监听逻辑:
public partial class ChildControl : ContentView { public ChildControl() { InitializeComponent(); Unloaded += ChildControl_Unloaded; } public static readonly BindableProperty IdentifyHeightModelProperty = BindableProperty.Create( nameof(IdentifyHeightModel), typeof(IdentHeightModel), typeof(ChildControl), null, propertyChanged: OnIdentifyHeightModelChanged); public IdentHeightModel? IdentifyHeightModel { get => GetValue(IdentifyHeightModelProperty) as IdentHeightModel; set => SetValue(IdentifyHeightModelProperty, value); } private static void OnIdentifyHeightModelChanged(BindableObject bindable, object oldValue, object newValue) { var control = (ChildControl)bindable; // 取消旧模型的事件订阅,避免内存泄漏 if (oldValue is IdentHeightModel oldModel) { oldModel.PropertyChanged -= control.OnModelPropertyChanged; } // 订阅新模型的属性变更事件 if (newValue is IdentHeightModel newModel) { newModel.PropertyChanged += control.OnModelPropertyChanged; // 初始加载时同步一次模型值到UI control.UpdateUIWithModel(newModel); } } private void OnModelPropertyChanged(object? sender, PropertyChangedEventArgs e) { if (sender is IdentHeightModel model) { // 仅监听目标属性的变更 if (e.PropertyName == nameof(IdentHeightModel.IdentHeightPortrait) || e.PropertyName == nameof(IdentHeightModel.IdentHeightLandscape)) { UpdateUIWithModel(model); } } } private void UpdateUIWithModel(IdentHeightModel model) { // 这里编写子视图UI更新逻辑,例如根据横竖屏调整高度 // 示例: // bool isPortrait = Application.Current?.MainPage?.Width < Application.Current?.MainPage?.Height; // this.HeightRequest = isPortrait ? model.IdentHeightPortrait : model.IdentHeightLandscape; } private void ChildControl_Unloaded(object? sender, EventArgs e) { // 控件卸载时取消事件订阅,防止内存泄漏 if (IdentifyHeightModel != null) { IdentifyHeightModel.PropertyChanged -= OnModelPropertyChanged; } } }
额外说明
- 原
ChildControl中propertyChanged回调里的赋值逻辑存在问题:直接给IdentifyHeightModel属性赋值会触发SetValue,可能导致不必要的循环调用,修复后改用静态回调方法处理模型切换。 - 增加
Unloaded事件处理,确保控件销毁时取消模型的事件订阅,避免内存泄漏。
内容的提问来源于stack exchange,提问作者Karen
相关产品推荐
相关产品推荐

