XAML、绑定、源和路径

Chr*_*ris 1 xaml binding mvvm maui .net-maui

我正在学习了解 .NET MAUI 的 XAML 中的绑定机制如何工作。我假设这对于所有 XAML 项目、WPF、MAUI 等都是相同的。

\n

最后是整个 XAML。

\n

这个 XAML 工作正常:

\n
<Button WidthRequest="150" Text="Add Activity" \n                Command="{Binding AddActivityEntityCommand}"\n                IsEnabled="{Binding IsNotBusy}"\n                Grid.Row="2"\n                Margin="8"/>\n
Run Code Online (Sandbox Code Playgroud)\n
    \n
  • 这是因为 Button 是 ContentPage 的一部分,它被x:DataType设置为MainPageViewModel,这是命令所在的位置吗?

    \n
  • \n
  • Binding 设置为AddActivityEntityCommand,而实际方法签名为\n
    async Task AddActivityEntityAsync()。这是如何解决的?因为它显然与名称不匹配,但它有效。其工作/被认可的方法签名要求是什么?

    \n
  • \n
\n

另一方面,这并不像开箱即用那么简单:

\n
<Label HorizontalOptions="End" TextColor="Red" Padding="0,0,10,0" Text="" \n       IsVisible="{Binding IsSynchronized}">\n            <Label.GestureRecognizers>\n                 <TapGestureRecognizer \n                       Command="{Binding Source={x:Type viewmodel:MainPageViewModel},\n                                            Path=DeleteActivityCommand}" />\n            </Label.GestureRecognizers>\n </Label>\n
Run Code Online (Sandbox Code Playgroud)\n
    \n
  • 在这种情况下,添加Command="{Binding DeleteActivityCommand}不起作用,因为<DataTemplate x:DataType="model:ActivityEntity">我假设它从 派生其 Path,这是数据对象,而不是命令实际所在的 ViewModel。
  • \n
  • 这里的问题是,一旦我输入此 XAML Command="{Binding Source={x:Type viewmodel:MainPageViewModel}, Path=DeleteActivityCommand}", CollectionView 显示为空,并且在加载视图时抛出未处理的异常:
  • \n
\n
\n

System.Reflection.TargetException:对象与目标类型不匹配。

\n
\n
    \n
  • 该命令的方法签名是这样的async Task DeleteActivityAsync()
  • \n
\n

我缺少什么?

\n
<?xml version="1.0" encoding="utf-8" ?>\n<ContentPage\n    x:Class="OnesieMobile.View.MainPage"\n    xmlns="http://schemas.microsoft.com/dotnet/2021/maui"\n    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"\n    xmlns:model="clr-namespace:OnesieMobile.Model"\n    xmlns:viewmodel="clr-namespace:OnesieMobile.ViewModel"\n    x:DataType="viewmodel:MainPageViewModel"\n    Title="{Binding Title}">\n    <Grid\n        ColumnDefinitions="*"\n        RowDefinitions="20,50,50,*"\n        RowSpacing="0">\n        <Label HorizontalOptions="End" Margin="10,0,10,0"  Text="{Binding CurrentDateTime}" Grid.Row="0"/>\n        <Entry Margin="10,0,10,0" \n            Grid.Row="1"  x:Name="entryNewActivity" \n            Placeholder="New Activityssss" HeightRequest="30" Text="{Binding NewActivityTitle}" />\n\n        <Button WidthRequest="150" Text="Add Activity" \n                    Command="{Binding AddActivityEntityCommand}"\n                    IsEnabled="{Binding IsNotBusy}"\n                    Grid.Row="2"\n                    Margin="8"/>\n\n        <CollectionView\n            Grid.Row="3"\n                ItemsSource="{Binding ActivityEntities}"\n                SelectionMode="None">\n            <CollectionView.ItemTemplate>\n                <DataTemplate x:DataType="model:ActivityEntity">\n                    <Grid Padding="10,0,10,0">\n                        <Frame Style="{StaticResource CardView}">\n                            <Grid  ColumnDefinitions="*,30,50">\n                                <StackLayout Padding="10,5,0,0" Grid.Column="0">\n                                    <Label Text="{Binding Title}"  />\n                                </StackLayout>\n                                <StackLayout Padding="10,5,0,0" Grid.Column="1">\n                                    <Label HorizontalOptions="End" TextColor="Red"\n                                           Padding="0,0,10,0" Text="" IsVisible="{Binding IsSynchronized}"  >\n                                        <Label.GestureRecognizers>\n                                            <TapGestureRecognizer\n                                                Command="{Binding Source={x:Type viewmodel:MainPageViewModel},\n                                                Path=DeleteActivityCommand}" />\n                                        </Label.GestureRecognizers>\n                                    </Label>\n                                </StackLayout>\n                                <StackLayout Padding="10,5,0,0" Grid.Column="2">\n                                    <Label HorizontalOptions="End" \n                                           Padding="0,0,10,0" Text="\xe2\x9c\x94" IsVisible="{Binding IsSynchronized}"  />\n                                </StackLayout>\n                            </Grid>\n                        </Frame>\n                    </Grid>\n                </DataTemplate>\n            </CollectionView.ItemTemplate>\n        </CollectionView>\n        <ActivityIndicator IsVisible="{Binding IsBusy}"\n                               IsRunning="{Binding IsBusy}"\n                               HorizontalOptions="FillAndExpand"\n                               VerticalOptions="CenterAndExpand"\n                               Grid.RowSpan="3"\n                               Grid.ColumnSpan="2"/>\n\n    </Grid>\n</ContentPage>\n
Run Code Online (Sandbox Code Playgroud)\n

更新:
\nMainPageViewModel.cs 包含这些命令

\n
[ICommand]\nasync Task DeleteActivityAsync()\n{\n}\n\n\n[ICommand]\nasync Task AddActivityEntityAsync()\n{\n}\n
Run Code Online (Sandbox Code Playgroud)\n

Too*_*eve 5

在项目模板内,有多种方法可以引用原始 BindingContext。我喜欢通过从集合本身获取它来做到这一点:

<CollectionView x:Name="myCollection" ...>
    ...
    <CollectionView.ItemTemplate>
        ...
        Command="{Binding Source={x:Reference myCollection},
                  Path=BindingContext.DeleteActivityCommand}" />
Run Code Online (Sandbox Code Playgroud)

未来待定:

我不喜欢执行代码中不存在的命令名称。希望最终有一种方法可以在 ICommand 属性中指定命令名称,以使其显而易见:

// This won't work today.
[ICommand Name="DeleteActivityCommand"]
...
Run Code Online (Sandbox Code Playgroud)

现在,我们必须学习 的[ICommand]神奇命名规则,它似乎是“Async从末尾删除(如果存在);添加Command到末尾”。