在我的主页上,我有一个 MainViewModel,其中包含一些属性,其中一个是 EffectsCtrl(另一个视图模型,EffectsControl 类的实例),它包含一些与管理效果列表相关的逻辑。为了显示此列表,我的主页上有以下 ListView:
<ContentPage
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:Effects"
x:Class="Effects.MainPage"
xmlns:vm="clr-namespace:Effects.ViewModel"
x:DataType="vm:MainViewModel">
...
<ListView
ItemsSource="{Binding EffectsCtrl.Effects}"
HasUnevenRows="True"
VerticalOptions="FillAndExpand">
<ListView.ItemTemplate>
...
</ListView.ItemTemplate>
</ListView>
</ContentPage>
以上工作正常 - 列表正确显示并更新更改。
但在某个时候我把它改成了这样
<ListView
BindingContext={Binding EffectsCtrl}
ItemsSource="{Binding Effects}"
HasUnevenRows="True"
VerticalOptions="FillAndExpand">
...
</ListView>
令我大吃一惊的是,我的 IDE(Visual Studio)向我显示了一个提示:Effects '在数据上下文 MainViewModel 中找不到成员',并且在启动构建时失败并出现错误'绑定:在“Effects”上找不到属性“Effects” .ViewModel.MainViewModel"'。 此时我有点困惑和好奇,所以我尝试了一些其他组合,例如
<ListView
BindingContext={Binding EffectsCtrl}
ItemsSource="{Binding EffectsCtrl.Effects}"
HasUnevenRows="True"
VerticalOptions="FillAndExpand">
...
</ListView>
并且编译正常,没有来自 IDE 的警告或构建错误,但列表根本不显示 - 我认为是由于错误的绑定,因为我在调试器中检查了 EffectsCtrl,一切都很好,并且元素像往常一样添加到列表中。调试器也没有显示任何警告或错误。
这让我想到了我的问题:这是一个错误还是我误解了绑定?当我进行第一次更改时,我假设当我更改 ListView 的 BindingContext 时,它将影响其 ItemsSource 字段上的绑定。我更困惑了,因为第三个案例似乎在某种程度上证实了我的假设。那么有人可以确认这是一个错误或向我提供解释为什么第二种情况无效吗?
编辑: 我做了一些更多的测试,当 ContentPage 声明中的
x:DataType="vm:MainViewModel"
被删除时,第二种情况按预期工作。这是为什么?
我自己在 Compiled bindings 文档中找到了答案。事实证明
x:DataType="vm:MainViewModel"
是问题所在,尽管这种行为是绝对正确的,问题是由于我的误解造成的。在 ContentPage
上设置的此属性会导致绑定的编译。正如上述文件所述
将 VisualElement 上的 x:DataType 属性设置为 VisualElement 及其子级将绑定到的对象的类型。
这意味着
ContentPage
中的每个元素都应该将 BindingContext
设置为 MainViewModel 的实例(我认为,除非另一个 x:DataType
属性另有说明)。因此,当在第二种情况下使用绑定到 Effects
时,即使 BindingContext
设置为 EffectsCtrl,编译器也会抛出错误 - 它假设 BindingContext
将是 MainViewModel 的实例(而不是 EffectsControl 实例)并且它确实不包含 Effects
属性。
我为此提供了两种可能的解决方案:
只需从
x:DataType="vm:MainViewModel"
属性中删除 ContentPage
即可切换到运行时验证。但这样的操作会产生一些负面后果 - 您会失去编译时绑定验证,运行时验证会影响性能,并且 IDE 不会提供自动完成功能(至少 Visual Studio 是这样)。
代码如下所示:
<ContentPage
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:Effects"
x:Class="Effects.MainPage"
xmlns:vm="clr-namespace:Effects.ViewModel">
...
<ListView
BindingContext="{Binding EffectsCtrl}"
ItemsSource="{Binding Effects}"
HasUnevenRows="True"
VerticalOptions="FillAndExpand">
<ListView.ItemTemplate>
...
</ListView.ItemTemplate>
</ListView>
</ContentPage>
MainViewModel 通过依赖注入传递,一切正常。
为了保留已编译的绑定,我刚刚将
x:DataType="vm:EffectsControl"
属性添加到 ListView
,但随后我无法使用 BindingContext="{Binding EffectsCtrl}"
属性,因为 EffectsCtrl 本身没有 EffectsCtrl 属性。因此,我将 ListView
包装在一个新的 ContentView
类中,如下所示:
<ContentView
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Effects.EffectsListView"
xmlns:vm="clr-namespace:Effects.ViewModel"
x:DataType="vm:EffectsControl">
<ListView
ItemsSource="{Binding Effects}"
HasUnevenRows="True"
VerticalOptions="FillAndExpand">
<ListView.ItemTemplate>
...
</ListView.ItemTemplate>
</ListView>
</ContentView>
并用新类替换了主页上的列表
<ContentPage
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:Effects"
x:Class="Effects.MainPage"
xmlns:vm="clr-namespace:Effects.ViewModel"
x:DataType="vm:MainViewModel">
...
<local:EffectsListView BindingContext="{Binding EffectsCtrl}"/>
</ContentPage>
我发现这种行为不是很直观,但现在当我理解这个概念时,我认为这个问题有点愚蠢。无论如何,我希望这可以帮助像我这样迷路的初学者。
ListView
项目模板将绑定到列表的类型,但是我也发现,其他位置的类会混淆绑定的编译时验证。
我不确定这是否正是您的情况,但我通过添加“XAML命名空间”声明解决了它
xmlns
:
<ContentPage
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"
xmlns:android="clr-namespace:Microsoft.Maui.Controls.PlatformConfiguration.AndroidSpecific;assembly=Microsoft.Maui.Controls"
xmlns:m="clr-namespace:My.Assembly.Namespace;assembly=My.Assembly"
然后我可以将
DataType
添加到项目模板中:
<ListView x:Name="searchResults" ItemsSource="{Binding Results}">
<ListView.ItemTemplate>
<DataTemplate x:DataType="m:ResultDto">
<TextCell Text="{Binding Code}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
在这种情况下,
Results
是我的视图模型上的可枚举对象,但ResultDto是My.Assembly.Namespace
中的一个类,具有属性Code
。
参考资料:
请参阅数据绑定基础知识/视图到视图绑定。使用
x:Reference
来表示元素的 x:Name
。
如果您的 EffectsCtrl 有 x:Name
myEffectsCtrl
,则执行:
<EffectsCtrl x:Name="myEffectsCtrl" ... />
<ListView
BindingContext="{x:Reference myEffectsCtrl}" ... />
您可以在 ListView 中将 x:DataType 设置为 x:DataType="{x:Null}"
<ContentPage
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:Effects"
x:Class="Effects.MainPage"
xmlns:vm="clr-namespace:Effects.ViewModel"
x:DataType="vm:MainViewModel">
...
<ListView
x:DataType="{x:Null}"
BindingContext={Binding EffectsCtrl}
ItemsSource="{Binding Effects}"
HasUnevenRows="True"
VerticalOptions="FillAndExpand">
...
</ListView>
</ContentPage>