You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.12 20:05:58