xaml - .NET MAUI: Wanting to pass parent objects to converters and controls, but then don't receive OnPropertyChanged -

I keep running into this issue in MAUI, which I suspect is from some fundamental misunderstanding of MV

I keep running into this issue in MAUI, which I suspect is from some fundamental misunderstanding of MVVM.

Say I have:

public class Student : ObservableObject {
    [ObservableProperty] string _name;
    [ObservableProperty] double _GPA;
    [ObservableProperty] bool _isPassing;
}

I made a custom control (StudentControl) to reuse my student view around the app. My gut says I should create a BindableProperty in the control that is of type Student. Bind the student to the control, and then the control looks up the fields it needs to display, and binds to them itself. My thinking is that it's the StudentControl's job to decide what it wants to display about the student. If I want to change what gets displayed later, or add/remove a field, I can go there to do it.

<controls:StudentControl Student={Binding SomeBindingToAStudentObject}>

However, This doesn't seem to be the way I'm meant to do it in MAUI. When I pass the Student directly to the control, binding seems to break, since it's the properties that change, and not the Student object itself. So although the properties notify correctly, since the control is bound to Student (rather than the individual properties), the control doesn't change even when the properties change.

I have a similar problem with converters, where I want to pass the parent Student object in, and then decide how to convert it, but again, this breaks the property change notifications in binding, since its link is to Student, not the changing properties themselves.

I know I can fix this by passing the fields individually into the control in a MultiBinding, But then if I add a field in the future, I need to go to every use of the control and add the field to it, rather than adjusting just the control.

Perhaps an important note: I usually have my Student objects in List, rather than a direct reference, and am consuming them through bindabale layouts or collection views etc.

Any suggestions to improve this situation? Or am I stuck passing individual properties to my controls/converters?

EDIT: Sounds like I need to give more context about the views.

This is a really complex problem, so I'm trying to simplify it a bit.

I have a ClassViewModel that is backing a ClassView, This is meant to display a ClassList : ObservableObject

ClassList has an

[ObservableProperty] BindingList<Student> _students;

So in my ClassView, I have something like this:

            <StackLayout BindableLayout.ItemsSource="{Binding ClassList.Students}">
                <BindableLayout.ItemTemplate>
                    <DataTemplate x:DataType="{x:Type models:Student}" >
                        <controls:StudentControl
                           Student="{Binding .}"/>
                    </DataTemplate>
                </BindableLayout.ItemTemplate>
            </StackLayout>

But the control doesn't update when Student.Name changes. Same for the other properties. It works when I make 3 bindable properties (one for each field), and pass the bindings in as Student.Name, Student.GPA etc.

          <!--This Works -->
          <StackLayout BindableLayout.ItemsSource="{Binding ClassList.Students}">
            <BindableLayout.ItemTemplate>
                <DataTemplate x:DataType="{x:Type models:Student}" >
                    <controls:StudentControl
                       Name="{Binding Student.Name}"
                       GPA="{Binding Student.GPA}"
                       IsPassing="{Binding Student.IsPassing}"/>
                </DataTemplate>
            </BindableLayout.ItemTemplate>
        </StackLayout>

In the StudentControl, I have a label that's bound to Student.Name, and I set BindingContext to This in the XAML

in header: x:Name="This"

below:

<VerticalStackLayout BindingContext="{x:Reference This}"

Note that the view DOES change, if I swap out a completely different student, but not when an existing student is edited.

EDIT: Solution. Refer to comments to @Stephen Quan's answer.

Added this extension method. Based mostly on

public static class ObservableCollectionExtensions
{
    public static void AddItemChangedListener<T>(this ObservableCollection<T> collection, PropertyChangedEventHandler h)
    {
        collection.CollectionChanged += (sender, e) =>
        {
            AddHandlerToItems(sender, e, h);
        };
    }
    
    static void AddHandlerToItems(object _, NotifyCollectionChangedEventArgs e, PropertyChangedEventHandler h)
    {
        if (e.OldItems != null)
        {
            foreach (INotifyPropertyChanged item in e.OldItems)
                item.PropertyChanged -= h;
        }
        if (e.NewItems != null)
        {
            foreach (INotifyPropertyChanged item in e.NewItems)
                item.PropertyChanged += h;
        }
    }
    
}

In ClassList

public ClassList() {
    Students.AddItemChangedListener((_, _) => {
        OnPropertyChanged(nameof(Students));
    });
}

public ObservableCollection<Student> Students {get;} = new();

I think if the ClassList is getting created and destroyed a lot you'd probably want to modify this to be able to remove the listener too.

I keep running into this issue in MAUI, which I suspect is from some fundamental misunderstanding of MVVM.

Say I have:

public class Student : ObservableObject {
    [ObservableProperty] string _name;
    [ObservableProperty] double _GPA;
    [ObservableProperty] bool _isPassing;
}

I made a custom control (StudentControl) to reuse my student view around the app. My gut says I should create a BindableProperty in the control that is of type Student. Bind the student to the control, and then the control looks up the fields it needs to display, and binds to them itself. My thinking is that it's the StudentControl's job to decide what it wants to display about the student. If I want to change what gets displayed later, or add/remove a field, I can go there to do it.

<controls:StudentControl Student={Binding SomeBindingToAStudentObject}>

However, This doesn't seem to be the way I'm meant to do it in MAUI. When I pass the Student directly to the control, binding seems to break, since it's the properties that change, and not the Student object itself. So although the properties notify correctly, since the control is bound to Student (rather than the individual properties), the control doesn't change even when the properties change.

I have a similar problem with converters, where I want to pass the parent Student object in, and then decide how to convert it, but again, this breaks the property change notifications in binding, since its link is to Student, not the changing properties themselves.

I know I can fix this by passing the fields individually into the control in a MultiBinding, But then if I add a field in the future, I need to go to every use of the control and add the field to it, rather than adjusting just the control.

Perhaps an important note: I usually have my Student objects in List, rather than a direct reference, and am consuming them through bindabale layouts or collection views etc.

Any suggestions to improve this situation? Or am I stuck passing individual properties to my controls/converters?

EDIT: Sounds like I need to give more context about the views.

This is a really complex problem, so I'm trying to simplify it a bit.

I have a ClassViewModel that is backing a ClassView, This is meant to display a ClassList : ObservableObject

ClassList has an

[ObservableProperty] BindingList<Student> _students;

So in my ClassView, I have something like this:

            <StackLayout BindableLayout.ItemsSource="{Binding ClassList.Students}">
                <BindableLayout.ItemTemplate>
                    <DataTemplate x:DataType="{x:Type models:Student}" >
                        <controls:StudentControl
                           Student="{Binding .}"/>
                    </DataTemplate>
                </BindableLayout.ItemTemplate>
            </StackLayout>

But the control doesn't update when Student.Name changes. Same for the other properties. It works when I make 3 bindable properties (one for each field), and pass the bindings in as Student.Name, Student.GPA etc.

          <!--This Works -->
          <StackLayout BindableLayout.ItemsSource="{Binding ClassList.Students}">
            <BindableLayout.ItemTemplate>
                <DataTemplate x:DataType="{x:Type models:Student}" >
                    <controls:StudentControl
                       Name="{Binding Student.Name}"
                       GPA="{Binding Student.GPA}"
                       IsPassing="{Binding Student.IsPassing}"/>
                </DataTemplate>
            </BindableLayout.ItemTemplate>
        </StackLayout>

In the StudentControl, I have a label that's bound to Student.Name, and I set BindingContext to This in the XAML

in header: x:Name="This"

below:

<VerticalStackLayout BindingContext="{x:Reference This}"

Note that the view DOES change, if I swap out a completely different student, but not when an existing student is edited.

EDIT: Solution. Refer to comments to @Stephen Quan's answer.

Added this extension method. Based mostly on https://stackoverflow/a/3478581/4163879

public static class ObservableCollectionExtensions
{
    public static void AddItemChangedListener<T>(this ObservableCollection<T> collection, PropertyChangedEventHandler h)
    {
        collection.CollectionChanged += (sender, e) =>
        {
            AddHandlerToItems(sender, e, h);
        };
    }
    
    static void AddHandlerToItems(object _, NotifyCollectionChangedEventArgs e, PropertyChangedEventHandler h)
    {
        if (e.OldItems != null)
        {
            foreach (INotifyPropertyChanged item in e.OldItems)
                item.PropertyChanged -= h;
        }
        if (e.NewItems != null)
        {
            foreach (INotifyPropertyChanged item in e.NewItems)
                item.PropertyChanged += h;
        }
    }
    
}

In ClassList

public ClassList() {
    Students.AddItemChangedListener((_, _) => {
        OnPropertyChanged(nameof(Students));
    });
}

public ObservableCollection<Student> Students {get;} = new();

I think if the ClassList is getting created and destroyed a lot you'd probably want to modify this to be able to remove the listener too.

Share Improve this question edited Nov 18, 2024 at 5:42 Adam B asked Nov 17, 2024 at 5:03 Adam BAdam B 3,8712 gold badges28 silver badges39 bronze badges 2
  • There's nothing wrong with Student. I would like to see your StudentControl implementation. That is the one that needs BindableProperty. – Stephen Quan Commented Nov 17, 2024 at 7:26
  • Added some more info above – Adam B Commented Nov 18, 2024 at 1:32
Add a comment  | 

1 Answer 1

Reset to default 0

Firstly, the collection should not use the [ObservableProperty] attribute but use ObservableCollection. The best practice for collection is not to change the collection, but, change members in the collection, so, this is why we will not be expecting INotifyPropertyChanged events coming from the collection.

public ObservableCollection<Student> Students { get; } = new();

Next, I would expect if your control needs to update Student, then, the various bindings you have need to be changed to TwoWay, e.g.

<controls:StudentControl
    Name="{Binding Student.Name, Mode=TwoWay}"
    GPA="{Binding Student.GPA, Mode=TwoWay}"
    IsPassing="{Binding Student.IsPassing, Mode=TwoWay}"/>

发布者:admin,转转请注明出处:http://www.yc00.com/questions/1745637962a4637511.html

相关推荐

  • UMIT:统一多模态多任务视觉

    随着深度学习的迅速发展,尤其是在医学影像分析领域的应用,越来越多的视觉-语言模型(VLMs)被广泛应用于解决复杂的健康和生物医学挑战。然而,现有研究主要集中在特定任务或单一模态上,这限制了它们在多种医学场景中的适用性和泛化能力。为了解决这

    1小时前
    00
  • 面试官:从三万英尺角度谈一下Ceph架构设计(1)

    把面试官当陪练,在找工作中才会越战越勇大家好我是小义同学,这是大厂面试拆解——项目实战系列的第3篇文章,如果有误,请指正。本文主要解决的一个问题,Ceph为例子 如何描述项目的架构。一句话描述:主要矛盾发生变化10年前的技术和方案,放到10

    1小时前
    00
  • 如何打造高效AI智能体?

    作者|Barry Zhang, Anthropic地址|出品|码个蛋(ID:codeegg)整理|陈宇明最近看到了 Anthropic 那篇著名的《Building effective agents》作者之一 Barry Zhang 在 2

    58分钟前
    00
  • 20 万 POC,直接拿来用,这不是测试,这是拒绝服务!!!

    之前看到很多人分享 github 上的一个项目,自动收录全网 nuclei 模板文件,目前已经 19 万了,如果直接拿来对目标进行漏洞探测,无疑会对目标造成巨大伤害,意味着可能要对目标发起十九万次请求以上,可以说是一次小型的 DDoS 攻击

    55分钟前
    00
  • AlignRAG:浙江大学提出的可泛化推理对齐框架,助力 RAG 系统解决推理失配问题

    近年来,检索增强生成(Retrieval-Augmented Generation, RAG)成为知识驱动文本生成的核心范式。然而,现有的 RAG 系统在推理过程中常常出现“推理失配”问题,即模型的推理路径与检索到的证据不一致,导致生成内容

    53分钟前
    00
  • 国产车载通信测试方案:车规级CAN SIC芯片测试技术解析

    随着智能网联汽车的快速发展,车辆内部电子控制单元(ECU)数量激增,动力总成、高级驾驶辅助系统(ADAS)、车身控制等功能对车载通信网络的稳定性与速率提出了更高要求。传统CAN FD总线在复杂拓扑中面临信号振铃、通信速率受限(实际速率通常低

    50分钟前
    00
  • Go 语言 Mock 实践

    Mock 是软件测试中的一项关键技术,尤其在单元测试领域,可谓是“顶梁柱”般的存在,几乎不可或缺。它通过模拟真实对象的行为,使我们能在不依赖外部系统的情况下,专注测试代码的核心逻辑。对于测试开发、自动化测试,乃至性能测试中的某些场景,合理使

    39分钟前
    00
  • 如何快速判断 Flutter 库是否需要适配鸿蒙?纯 Dart 库无需适配!

    在鸿蒙开发中,选择合适的 Flutter 库至关重要。纯 Dart 库因其跨平台特性,无需适配即可直接使用。但对于新手来说,如何判断一个库是否为纯 Dart 库呢?本文将为你提供清晰的判断方法和实用技巧。一、检查 pubspec.yaml

    37分钟前
    00
  • MongoDB “升级项目” 大型连续剧(2)

    上期写的是非必要不升级,想到这个事情,有一些事情的仔细琢磨琢磨,为什么数据库升级的事情在很多公司都是一个困扰,从一个技术人的观点,升级是一件好事,功能提升了,性能提升了,开发效率和一些数据库使用的痛点也被解决了,为什么就不愿意升级呢?如果只

    36分钟前
    00
  • CUT&amp;amp;Tag 数据处理和分析教程(7)

    过滤某些项目可能需要对比对质量分数进行更严格的过滤。本文细讨论了bowtie如何分配质量分数,并举例说明。MAPQ(x) = -10 * log10log10(P(x is mapped wrongly)) = -10 * log10(p)

    34分钟前
    10
  • 深度学习在DOM解析中的应用:自动识别页面关键内容区块

    爬虫代理摘要本文介绍了如何在爬取东方财富吧()财经新闻时,利用深度学习模型对 DOM 树中的内容区块进行自动识别和过滤,并将新闻标题、时间、正文等关键信息分类存储。文章聚焦爬虫整体性能瓶颈,通过指标对比、优化策略、压测数据及改进结果,展示了

    33分钟前
    10
  • 推荐一个轻量级的监控平台并且支持移动端

    简介XUGOU 是基于Cloudflare构建的轻量化监控平台,专精于系统资源监控与可视化状态页面服务。该平台提供英文简体中文双语支持,满足全球化部署需求。面向开发者及中小团队,项目致力于提供高可用性的监控解决方案。核心功能与实现平台功能

    28分钟前
    00
  • 重装系统只影响C盘吗?深入解析系统重装的全过程

    重装系统只影响C盘吗?深入解析系统重装的全过程 在计算机的日常使用中,重装系统是一个常见的操作,尤其是在系统出现故障、感染病毒或需要优化系统性能时。然而,许多用户对于重装系统的具体过程和影响存在误解,认为重装系统仅仅是对C盘进行清空和重置

    24分钟前
    10
  • 拥抱国产化:转转APP的鸿蒙NEXT端开发尝鲜之旅

    本文由转转技术团队赵卫兵分享,原题“鸿蒙新篇章:转转 APP 的 HarmonyOS Next 开发之旅”,下文进行了排版优化和内容修订。1、引言2023 年在华为开发者大会(HDC.Together)上,除了面向消费者的 HarmonyO

    22分钟前
    00
  • maxwell遇到的一则问题

    结论和原因maxwell的元数据库里面没有存储全部的schema数据(就是少数据了),导致相关表的DDL校验失败。PS:我这里maxwell的作用只是采集库表修改情况的统计粗粒度指标,因为之前maxwell在运行报错的时候,直接修改了pos

    20分钟前
    00
  • 在VMware虚拟机中安装Windows 7全攻略(避坑指南)

    ⚠️写在前面 最近发现很多开发者在调试老旧系统时都需要用到Windows 7环境&#xff08;特别是银行、医疗等行业的遗留系统&#xff09;&#xff0c;但实体机安装既不现实也不安全。今天就手把手教你在虚拟机

    16分钟前
    00
  • Nat. Mater.

    大家好,今天给大家分享一篇近期发表在Nat. Mater.上的研究进展,题为:De novo design of self-assembling peptides with antimicrobial activity guided

    15分钟前
    00
  • 最后讲一遍:ChatGPT 快速生成国内外研究现状的方法

    在科研工作中,梳理国内外研究现状有助于明确研究方向,发现研究空白,为后续研究提供理论支持与创新思路。本文将详细介绍如何借助 ChatGPT 高效生成国内外研究现状,帮助您在有限时间内构建全面、专业的文献综述框架,提升学术写作效率与质量。St

    13分钟前
    00
  • Windows系统密钥检测工具PIDKey 2.1中文版

    Windows系统密钥检测工具PIDKey 2.1中文版 【下载地址】Windows系统密钥检测工具PIDKey2.1中文版 Windows系统密钥检测工具PIDKey 2.1中文版是一款功能强大的工具&#xff0c;专为管理Win

    12分钟前
    00
  • ​2025 轻松部署 Odoo 18 社区版

    随着 Odoo 18 社区版的发布,越来越多的企业希望借助这款开源 ERP 系统实现数字化转型。本文将深入解析传统部署方式的底层逻辑,并揭示如何通过自动化工具实现零门槛快速部署。一、手工部署 Odoo 18 技术全解 Docker 环境搭建

    7分钟前
    00

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信