XAML、绑定、源和路径
XAML, Binding, Source and Path
我正在学习了解绑定机制在 XAML for .NET MAUI 中的工作原理。我假设这对所有 XAML 项目、WPF、MAUI 等都是一样的
最后是整个XAML.
这个XAML工作正常:
<Button WidthRequest="150" Text="Add Activity"
Command="{Binding AddActivityEntityCommand}"
IsEnabled="{Binding IsNotBusy}"
Grid.Row="2"
Margin="8"/>
这是因为 Button 是 ContentPage 的一部分,它的 x:DataType
设置为 MainPageViewModel
,命令所在的位置?
Binding设置为AddActivityEntityCommand
,而实际的方法签名是
async Task AddActivityEntityAsync()
。这是如何解决的?由于它显然与名称不匹配,但它有效。 work/being 识别的方法签名要求是什么?
另一方面,这并不像开箱即用那样简单:
<Label HorizontalOptions="End" TextColor="Red" Padding="0,0,10,0" Text=""
IsVisible="{Binding IsSynchronized}">
<Label.GestureRecognizers>
<TapGestureRecognizer
Command="{Binding Source={x:Type viewmodel:MainPageViewModel},
Path=DeleteActivityCommand}" />
</Label.GestureRecognizers>
</Label>
- 在这种情况下,添加
Command="{Binding DeleteActivityCommand}
不起作用,因为它从 <DataTemplate x:DataType="model:ActivityEntity">
派生路径,我假设它是数据对象而不是 ViewModel,命令实际上是是。
- 这里的问题是,当我输入 XAML
Command="{Binding Source={x:Type viewmodel:MainPageViewModel}, Path=DeleteActivityCommand}"
时,CollectionView 显示为空,并且在加载视图时抛出未处理的异常:
System.Reflection.TargetException: Object does not match target type.
- 这个命令的方法签名是这个
async Task DeleteActivityAsync()
我错过了什么?
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage
x:Class="OnesieMobile.View.MainPage"
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:model="clr-namespace:OnesieMobile.Model"
xmlns:viewmodel="clr-namespace:OnesieMobile.ViewModel"
x:DataType="viewmodel:MainPageViewModel"
Title="{Binding Title}">
<Grid
ColumnDefinitions="*"
RowDefinitions="20,50,50,*"
RowSpacing="0">
<Label HorizontalOptions="End" Margin="10,0,10,0" Text="{Binding CurrentDateTime}" Grid.Row="0"/>
<Entry Margin="10,0,10,0"
Grid.Row="1" x:Name="entryNewActivity"
Placeholder="New Activityssss" HeightRequest="30" Text="{Binding NewActivityTitle}" />
<Button WidthRequest="150" Text="Add Activity"
Command="{Binding AddActivityEntityCommand}"
IsEnabled="{Binding IsNotBusy}"
Grid.Row="2"
Margin="8"/>
<CollectionView
Grid.Row="3"
ItemsSource="{Binding ActivityEntities}"
SelectionMode="None">
<CollectionView.ItemTemplate>
<DataTemplate x:DataType="model:ActivityEntity">
<Grid Padding="10,0,10,0">
<Frame Style="{StaticResource CardView}">
<Grid ColumnDefinitions="*,30,50">
<StackLayout Padding="10,5,0,0" Grid.Column="0">
<Label Text="{Binding Title}" />
</StackLayout>
<StackLayout Padding="10,5,0,0" Grid.Column="1">
<Label HorizontalOptions="End" TextColor="Red"
Padding="0,0,10,0" Text="" IsVisible="{Binding IsSynchronized}" >
<Label.GestureRecognizers>
<TapGestureRecognizer
Command="{Binding Source={x:Type viewmodel:MainPageViewModel},
Path=DeleteActivityCommand}" />
</Label.GestureRecognizers>
</Label>
</StackLayout>
<StackLayout Padding="10,5,0,0" Grid.Column="2">
<Label HorizontalOptions="End"
Padding="0,0,10,0" Text="✔" IsVisible="{Binding IsSynchronized}" />
</StackLayout>
</Grid>
</Frame>
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
<ActivityIndicator IsVisible="{Binding IsBusy}"
IsRunning="{Binding IsBusy}"
HorizontalOptions="FillAndExpand"
VerticalOptions="CenterAndExpand"
Grid.RowSpan="3"
Grid.ColumnSpan="2"/>
</Grid>
</ContentPage>
更新:
MainPageViewModel.cs 包含这些命令
[ICommand]
async Task DeleteActivityAsync()
{
}
[ICommand]
async Task AddActivityEntityAsync()
{
}
在项目模板中,有几种方法可以引用原始的 BindingContext。我喜欢通过从集合本身获取它来做到这一点:
<CollectionView x:Name="myCollection" ...>
...
<CollectionView.ItemTemplate>
...
Command="{Binding Source={x:Reference myCollection},
Path=BindingContext.DeleteActivityCommand}" />
未来待定:
我不喜欢执行代码中任何地方都不存在的命令名称。希望最终会有一种方法可以在 ICommand 属性中 指定 命令名称,使其显而易见:
// This won't work today.
[ICommand Name="DeleteActivityCommand"]
...
现在,我们必须学习 [ICommand]
的神奇命名规则,这似乎是“从末尾删除 Async
(如果存在);将 Command
添加到末尾” .
我正在学习了解绑定机制在 XAML for .NET MAUI 中的工作原理。我假设这对所有 XAML 项目、WPF、MAUI 等都是一样的
最后是整个XAML.
这个XAML工作正常:
<Button WidthRequest="150" Text="Add Activity"
Command="{Binding AddActivityEntityCommand}"
IsEnabled="{Binding IsNotBusy}"
Grid.Row="2"
Margin="8"/>
这是因为 Button 是 ContentPage 的一部分,它的
x:DataType
设置为MainPageViewModel
,命令所在的位置?Binding设置为
AddActivityEntityCommand
,而实际的方法签名是async Task AddActivityEntityAsync()
。这是如何解决的?由于它显然与名称不匹配,但它有效。 work/being 识别的方法签名要求是什么?
另一方面,这并不像开箱即用那样简单:
<Label HorizontalOptions="End" TextColor="Red" Padding="0,0,10,0" Text=""
IsVisible="{Binding IsSynchronized}">
<Label.GestureRecognizers>
<TapGestureRecognizer
Command="{Binding Source={x:Type viewmodel:MainPageViewModel},
Path=DeleteActivityCommand}" />
</Label.GestureRecognizers>
</Label>
- 在这种情况下,添加
Command="{Binding DeleteActivityCommand}
不起作用,因为它从<DataTemplate x:DataType="model:ActivityEntity">
派生路径,我假设它是数据对象而不是 ViewModel,命令实际上是是。 - 这里的问题是,当我输入 XAML
Command="{Binding Source={x:Type viewmodel:MainPageViewModel}, Path=DeleteActivityCommand}"
时,CollectionView 显示为空,并且在加载视图时抛出未处理的异常:
System.Reflection.TargetException: Object does not match target type.
- 这个命令的方法签名是这个
async Task DeleteActivityAsync()
我错过了什么?
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage
x:Class="OnesieMobile.View.MainPage"
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:model="clr-namespace:OnesieMobile.Model"
xmlns:viewmodel="clr-namespace:OnesieMobile.ViewModel"
x:DataType="viewmodel:MainPageViewModel"
Title="{Binding Title}">
<Grid
ColumnDefinitions="*"
RowDefinitions="20,50,50,*"
RowSpacing="0">
<Label HorizontalOptions="End" Margin="10,0,10,0" Text="{Binding CurrentDateTime}" Grid.Row="0"/>
<Entry Margin="10,0,10,0"
Grid.Row="1" x:Name="entryNewActivity"
Placeholder="New Activityssss" HeightRequest="30" Text="{Binding NewActivityTitle}" />
<Button WidthRequest="150" Text="Add Activity"
Command="{Binding AddActivityEntityCommand}"
IsEnabled="{Binding IsNotBusy}"
Grid.Row="2"
Margin="8"/>
<CollectionView
Grid.Row="3"
ItemsSource="{Binding ActivityEntities}"
SelectionMode="None">
<CollectionView.ItemTemplate>
<DataTemplate x:DataType="model:ActivityEntity">
<Grid Padding="10,0,10,0">
<Frame Style="{StaticResource CardView}">
<Grid ColumnDefinitions="*,30,50">
<StackLayout Padding="10,5,0,0" Grid.Column="0">
<Label Text="{Binding Title}" />
</StackLayout>
<StackLayout Padding="10,5,0,0" Grid.Column="1">
<Label HorizontalOptions="End" TextColor="Red"
Padding="0,0,10,0" Text="" IsVisible="{Binding IsSynchronized}" >
<Label.GestureRecognizers>
<TapGestureRecognizer
Command="{Binding Source={x:Type viewmodel:MainPageViewModel},
Path=DeleteActivityCommand}" />
</Label.GestureRecognizers>
</Label>
</StackLayout>
<StackLayout Padding="10,5,0,0" Grid.Column="2">
<Label HorizontalOptions="End"
Padding="0,0,10,0" Text="✔" IsVisible="{Binding IsSynchronized}" />
</StackLayout>
</Grid>
</Frame>
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
<ActivityIndicator IsVisible="{Binding IsBusy}"
IsRunning="{Binding IsBusy}"
HorizontalOptions="FillAndExpand"
VerticalOptions="CenterAndExpand"
Grid.RowSpan="3"
Grid.ColumnSpan="2"/>
</Grid>
</ContentPage>
更新:
MainPageViewModel.cs 包含这些命令
[ICommand]
async Task DeleteActivityAsync()
{
}
[ICommand]
async Task AddActivityEntityAsync()
{
}
在项目模板中,有几种方法可以引用原始的 BindingContext。我喜欢通过从集合本身获取它来做到这一点:
<CollectionView x:Name="myCollection" ...>
...
<CollectionView.ItemTemplate>
...
Command="{Binding Source={x:Reference myCollection},
Path=BindingContext.DeleteActivityCommand}" />
未来待定:
我不喜欢执行代码中任何地方都不存在的命令名称。希望最终会有一种方法可以在 ICommand 属性中 指定 命令名称,使其显而易见:
// This won't work today.
[ICommand Name="DeleteActivityCommand"]
...
现在,我们必须学习 [ICommand]
的神奇命名规则,这似乎是“从末尾删除 Async
(如果存在);将 Command
添加到末尾” .