我在 MAUI 中不断遇到这个问题,我怀疑这是由于对 MVVM 的一些基本误解造成的。
说我有:
public class Student : ObservableObject {
[ObservableProperty] string _name;
[ObservableProperty] double _GPA;
[ObservableProperty] bool _isPassing;
}
我制作了一个自定义控件 (
StudentControl
) 来在应用程序中重复使用我的学生视图。我的直觉告诉我应该在 BindableProperty
类型的控件中创建一个 Student
。将学生绑定到控件,然后控件查找它需要显示的字段,并绑定到它们本身。我的想法是,StudentControl
的工作是决定它想要显示有关学生的哪些内容。如果我想更改稍后显示的内容,或者添加/删除字段,我可以去那里执行此操作。
<controls:StudentControl Student={Binding SomeBindingToAStudentObject}>
然而,这似乎不是我在毛伊岛应该做的事情。当我将
Student
直接传递给控件时,绑定似乎会中断,因为更改的是属性,而不是 Student
对象本身。因此,尽管属性通知正确,但由于控件绑定到 Student
(而不是单个属性),因此即使属性更改,控件也不会更改。
我对转换器也有类似的问题,我想传递父 Student 对象,然后决定如何转换它,但同样,这会破坏绑定中的属性更改通知,因为它的链接是指向 Student,而不是更改的属性本身。
我知道我可以通过将字段单独传递到 MultiBinding 中的控件来解决此问题,但是如果我将来添加一个字段,我需要转到该控件的每次使用并向其添加该字段,而不是调整只是控制。
也许是一个重要的注意事项:我通常将 Student 对象放在 List 中,而不是直接引用,并通过 Bindabale 布局或集合视图等使用它们。
有什么建议可以改善这种情况吗?或者我是否坚持将各个属性传递给我的控件/转换器?
编辑:听起来我需要提供有关视图的更多背景信息。
这是一个非常复杂的问题,所以我试图稍微简化一下。
我有一个
ClassViewModel
支持ClassView
,这是为了显示ClassList : ObservableObject
ClassList
有一个
[ObservableProperty] BindingList<Student> _students;
所以在我的 ClassView 中,我有这样的东西:
<StackLayout BindableLayout.ItemsSource="{Binding ClassList.Students}">
<BindableLayout.ItemTemplate>
<DataTemplate x:DataType="{x:Type models:Student}" >
<controls:StudentControl
Student="{Binding .}"/>
</DataTemplate>
</BindableLayout.ItemTemplate>
</StackLayout>
但是当
Student.Name
更改时控件不会更新。其他属性也一样。当我创建 3 个可绑定属性(每个字段一个)并将绑定作为 Student.Name
、Student.GPA
等传递时,它就可以工作了。
<!--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>
在
StudentControl
中,我有一个绑定到 Student.Name
的标签,并且我在 XAML 中将 BindingContext 设置为 This
标题中:
x:Name="This"
下:
<VerticalStackLayout BindingContext="{x:Reference This}"
请注意,如果我换出完全不同的学生,视图会发生变化,但编辑现有学生时不会发生变化。
首先,集合不应该使用
[ObservableProperty]
属性,而应该使用ObservableCollection
。集合的最佳实践不是更改集合,而是更改集合中的成员,因此,这就是为什么我们不会期望来自集合的 INotifyPropertyChanged 事件。
public ObservableCollection<Student> Students { get; } = new();
接下来,我希望如果你的控件需要更新Student,那么你所拥有的各种绑定需要更改为TwoWay,例如
<controls:StudentControl
Name="{Binding Student.Name, Mode=TwoWay}"
GPA="{Binding Student.GPA, Mode=TwoWay}"
IsPassing="{Binding Student.IsPassing, Mode=TwoWay}"/>