在 ListView 中如何将单击的对象发送回视图模型中的命令 - Xamarin Forms
In a ListView how to send the clicked object back to the command in the view model - Xamarin Forms
给定以下 ListView
,我希望有一个命令可以将单击的对象(在本例中为地址)发送回视图模型中的命令 - SelectNewAddress
或DeleteAddress
.
<StackLayout VerticalOptions="FillAndExpand" Padding="10,15,10,15">
<Label Text="Addresses" FontSize="22" HorizontalTextAlignment="Center" FontAttributes="Bold" Padding="0,0,0,7" TextColor="#404040" />
<StackLayout VerticalOptions="FillAndExpand">
<flv:FlowListView FlowColumnCount="1"
HeightRequest="200"
SeparatorVisibility="None"
HasUnevenRows="True"
FlowItemsSource="{Binding AllAddresses}">
<flv:FlowListView.FlowColumnTemplate>
<DataTemplate x:DataType="popups:AddressItem">
<Grid ColumnDefinitions="*,35" Padding="0,0,0,15" x:Name="Item">
<Grid Grid.Column="0">
<Grid.GestureRecognizers>
<TapGestureRecognizer
Command="{Binding SelectNewAddress}" />
</Grid.GestureRecognizers>
<Label Text="{Binding MainAddress}"
LineBreakMode="TailTruncation"
HorizontalTextAlignment="Start"
VerticalTextAlignment="Center"
FontSize="18"
TextColor="{StaticResource CommonBlack}"/>
</Grid>
<Grid Grid.Column="1" IsVisible="{Binding IsSelected}" >
<Grid.GestureRecognizers>
<TapGestureRecognizer
Command="{Binding SelectNewAddress}"/>
</Grid.GestureRecognizers>
<StackLayout Padding="10,0,0,0">
<flex:FlexButton Icon="check.png"
WidthRequest="25"
HeightRequest="25"
CornerRadius="18"
BackgroundColor="{StaticResource Primary}"
ForegroundColor="{StaticResource CommonWhite}"
HighlightBackgroundColor="{StaticResource PrimaryDark}"
HighlightForegroundColor="{StaticResource CommonWhite}"/>
</StackLayout>
</Grid>
<Grid Grid.Column="1" IsVisible="{Binding IsSelected, Converter={StaticResource invertBoolConverter}}">
<Grid.GestureRecognizers>
<TapGestureRecognizer Command="{Binding DeleteAddress} />
</Grid.GestureRecognizers>
<StackLayout Padding="10,0,0,0">
<flex:FlexButton Icon="deleteCard.png"
WidthRequest="25"
HeightRequest="25"
CornerRadius="18"
BackgroundColor="{StaticResource WooopDarkGray}"
ForegroundColor="{StaticResource CommonWhite}"
HighlightBackgroundColor="{StaticResource PrimaryDark}"
HighlightForegroundColor="{StaticResource CommonWhite}"/>
</StackLayout>
</Grid>
</Grid>
</DataTemplate>
</flv:FlowListView.FlowColumnTemplate>
</flv:FlowListView>
</StackLayout>
视图模型中的命令如下:
...
public ICommand SelectNewAddress { get; set; }
public ICommand DeleteAddress { get; set; }
...
public AddressSelectionViewModel()
{
DeleteAddress = new Command(DeleteAddressCommand);
SelectNewAddress = new Command(SelectNewAddressCommand);
}
...
private void SelectNewAddressCommand(object obj)
{
try
{
var item = (AddressItem)obj;
AddressHelper.UpdateAddress(item.DeliveryAddressLocation);
UpdateAddresses();
}
catch (Exception ex)
{
// TODO
}
}
private void DeleteAddressCommand(object obj)
{
try
{
var item = (AddressItem)obj;
AddressHelper.RemoveAddress(item.DeliveryAddressLocation);
UpdateAddresses();
}
catch (Exception ex)
{
// TODO
}
}
我希望 object obj
传递给 SelectNewAddressCommand
并且 DeleteAddressCommand
是在 ListView
上单击的地址
首先确保您已将视图模型包含为 DataType
并在 ContentPage
:
中查看为 Class
xmlns:pages="clr-namespace:your.namespace.ViewModels"
x:DataType="pages:AddressSelectionViewModel"
x:Class="your.namespace.Views.AddressSelectionPage"
<ContentPage xmlns="..."
xmlns:x="..."
xmlns:flv="..."
xmlns:popups="..."
xmlns:flex="..."
xmlns:views="..."
xmlns:xct="..."
xmlns:pages="clr-namespace:your.namespace.ViewModels"
x:DataType="pages:AddressSelectionViewModel"
x:Class="your.namespace.Views.AddressSelectionPage"
Shell.FlyoutItemIsVisible="..."
Shell.NavBarIsVisible="..."
Shell.TabBarIsVisible="...">
在最上面的Grid
元素中添加属性x:Name="Item"
("Item"只是作为例子,你可以随意命名):
<flv:FlowListView FlowColumnCount="1"
HeightRequest="200"
SeparatorVisibility="None"
HasUnevenRows="True"
FlowItemsSource="{Binding AllAddresses}">
<flv:FlowListView.FlowColumnTemplate>
<DataTemplate x:DataType="popups:AddressItem">
<Grid ColumnDefinitions="*,35" Padding="0,0,0,15" x:Name="Item"> <!-- Here -->
然后我们把TapGestureRecognizer
的Command
和CommandParameter
改成如下:
<TapGestureRecognizer
Command="{Binding Path=SelectNewAddress, Source={RelativeSource AncestorType={x:Type pages:AddressSelectionViewModel}}}"
CommandParameter="{Binding Source={x:Reference Item}, Path=BindingContext}" />
<TapGestureRecognizer
Command="{Binding Path=DeleteAddress, Source={RelativeSource AncestorType={x:Type pages:AddressSelectionViewModel}}}"
CommandParameter="{Binding Source={x:Reference Item}, Path=BindingContext}" />
在Command
中我们将函数指定为Path
,然后我们通过AncestoryType
明确了这个函数的源在视图模型内部。在列表视图中时,我们无法引用被迭代对象外部的属性。因此,我们需要指定所需的来源。
所以现在我们引用的是实际函数。但是我们还没有将 object obj
作为参数发送。
在CommandParameter
中,我们必须使用Path
和Source
传递当前绑定的对象。请注意,在 Source 中,我们引用的名称 Item
我们之前定义为 Grid
的 x:Name
。
确保页面将视图模型作为其 BindingContext。 (如果你正在做 mvvm,那么你已经做过了。)
给<flv:FlowListView
一个名字:
<flv:FlowListView x:Name="theListView" ... >
- 该项需要引用页面视图模型中的命令。 BindingContext 通过层次结构向下传播,因此相对于列表视图名称很容易完成:
<flv:FlowListView.FlowColumnTemplate>
<DataTemplate>
...
<TapGestureRecognizer
Command="{Binding BindingContext.SelectNewAddress, Source={x:Reference theListView}}" ...
- 项目的 BindingContext 是项目模型,因此很容易作为参数传递:
<TapGestureRecognizer
Command=... CommandParameter="{Binding .}" />
注意:这与 Wizard 的回答之间的差异:
{Binding .}
是引用项目本身所需要的。
- 而不是使用需要指定
Type
的 RelativeSource
,我个人的偏好是 命名 视图,然后引用该名称。我发现这更容易阅读和记住如何去做。
- 我省略了所有与问题无关的细节。上面的步骤就足够了。 (
x:DataType
命令对性能有好处,所以我绝不建议不要执行它们。但这是一个单独的主题,恕我直言。)
给定以下 ListView
,我希望有一个命令可以将单击的对象(在本例中为地址)发送回视图模型中的命令 - SelectNewAddress
或DeleteAddress
.
<StackLayout VerticalOptions="FillAndExpand" Padding="10,15,10,15">
<Label Text="Addresses" FontSize="22" HorizontalTextAlignment="Center" FontAttributes="Bold" Padding="0,0,0,7" TextColor="#404040" />
<StackLayout VerticalOptions="FillAndExpand">
<flv:FlowListView FlowColumnCount="1"
HeightRequest="200"
SeparatorVisibility="None"
HasUnevenRows="True"
FlowItemsSource="{Binding AllAddresses}">
<flv:FlowListView.FlowColumnTemplate>
<DataTemplate x:DataType="popups:AddressItem">
<Grid ColumnDefinitions="*,35" Padding="0,0,0,15" x:Name="Item">
<Grid Grid.Column="0">
<Grid.GestureRecognizers>
<TapGestureRecognizer
Command="{Binding SelectNewAddress}" />
</Grid.GestureRecognizers>
<Label Text="{Binding MainAddress}"
LineBreakMode="TailTruncation"
HorizontalTextAlignment="Start"
VerticalTextAlignment="Center"
FontSize="18"
TextColor="{StaticResource CommonBlack}"/>
</Grid>
<Grid Grid.Column="1" IsVisible="{Binding IsSelected}" >
<Grid.GestureRecognizers>
<TapGestureRecognizer
Command="{Binding SelectNewAddress}"/>
</Grid.GestureRecognizers>
<StackLayout Padding="10,0,0,0">
<flex:FlexButton Icon="check.png"
WidthRequest="25"
HeightRequest="25"
CornerRadius="18"
BackgroundColor="{StaticResource Primary}"
ForegroundColor="{StaticResource CommonWhite}"
HighlightBackgroundColor="{StaticResource PrimaryDark}"
HighlightForegroundColor="{StaticResource CommonWhite}"/>
</StackLayout>
</Grid>
<Grid Grid.Column="1" IsVisible="{Binding IsSelected, Converter={StaticResource invertBoolConverter}}">
<Grid.GestureRecognizers>
<TapGestureRecognizer Command="{Binding DeleteAddress} />
</Grid.GestureRecognizers>
<StackLayout Padding="10,0,0,0">
<flex:FlexButton Icon="deleteCard.png"
WidthRequest="25"
HeightRequest="25"
CornerRadius="18"
BackgroundColor="{StaticResource WooopDarkGray}"
ForegroundColor="{StaticResource CommonWhite}"
HighlightBackgroundColor="{StaticResource PrimaryDark}"
HighlightForegroundColor="{StaticResource CommonWhite}"/>
</StackLayout>
</Grid>
</Grid>
</DataTemplate>
</flv:FlowListView.FlowColumnTemplate>
</flv:FlowListView>
</StackLayout>
视图模型中的命令如下:
...
public ICommand SelectNewAddress { get; set; }
public ICommand DeleteAddress { get; set; }
...
public AddressSelectionViewModel()
{
DeleteAddress = new Command(DeleteAddressCommand);
SelectNewAddress = new Command(SelectNewAddressCommand);
}
...
private void SelectNewAddressCommand(object obj)
{
try
{
var item = (AddressItem)obj;
AddressHelper.UpdateAddress(item.DeliveryAddressLocation);
UpdateAddresses();
}
catch (Exception ex)
{
// TODO
}
}
private void DeleteAddressCommand(object obj)
{
try
{
var item = (AddressItem)obj;
AddressHelper.RemoveAddress(item.DeliveryAddressLocation);
UpdateAddresses();
}
catch (Exception ex)
{
// TODO
}
}
我希望 object obj
传递给 SelectNewAddressCommand
并且 DeleteAddressCommand
是在 ListView
首先确保您已将视图模型包含为 DataType
并在 ContentPage
:
Class
xmlns:pages="clr-namespace:your.namespace.ViewModels"
x:DataType="pages:AddressSelectionViewModel"
x:Class="your.namespace.Views.AddressSelectionPage"
<ContentPage xmlns="..."
xmlns:x="..."
xmlns:flv="..."
xmlns:popups="..."
xmlns:flex="..."
xmlns:views="..."
xmlns:xct="..."
xmlns:pages="clr-namespace:your.namespace.ViewModels"
x:DataType="pages:AddressSelectionViewModel"
x:Class="your.namespace.Views.AddressSelectionPage"
Shell.FlyoutItemIsVisible="..."
Shell.NavBarIsVisible="..."
Shell.TabBarIsVisible="...">
在最上面的Grid
元素中添加属性x:Name="Item"
("Item"只是作为例子,你可以随意命名):
<flv:FlowListView FlowColumnCount="1"
HeightRequest="200"
SeparatorVisibility="None"
HasUnevenRows="True"
FlowItemsSource="{Binding AllAddresses}">
<flv:FlowListView.FlowColumnTemplate>
<DataTemplate x:DataType="popups:AddressItem">
<Grid ColumnDefinitions="*,35" Padding="0,0,0,15" x:Name="Item"> <!-- Here -->
然后我们把TapGestureRecognizer
的Command
和CommandParameter
改成如下:
<TapGestureRecognizer
Command="{Binding Path=SelectNewAddress, Source={RelativeSource AncestorType={x:Type pages:AddressSelectionViewModel}}}"
CommandParameter="{Binding Source={x:Reference Item}, Path=BindingContext}" />
<TapGestureRecognizer
Command="{Binding Path=DeleteAddress, Source={RelativeSource AncestorType={x:Type pages:AddressSelectionViewModel}}}"
CommandParameter="{Binding Source={x:Reference Item}, Path=BindingContext}" />
在Command
中我们将函数指定为Path
,然后我们通过AncestoryType
明确了这个函数的源在视图模型内部。在列表视图中时,我们无法引用被迭代对象外部的属性。因此,我们需要指定所需的来源。
所以现在我们引用的是实际函数。但是我们还没有将 object obj
作为参数发送。
在CommandParameter
中,我们必须使用Path
和Source
传递当前绑定的对象。请注意,在 Source 中,我们引用的名称 Item
我们之前定义为 Grid
的 x:Name
。
确保页面将视图模型作为其 BindingContext。 (如果你正在做 mvvm,那么你已经做过了。)
给
<flv:FlowListView
一个名字:
<flv:FlowListView x:Name="theListView" ... >
- 该项需要引用页面视图模型中的命令。 BindingContext 通过层次结构向下传播,因此相对于列表视图名称很容易完成:
<flv:FlowListView.FlowColumnTemplate>
<DataTemplate>
...
<TapGestureRecognizer
Command="{Binding BindingContext.SelectNewAddress, Source={x:Reference theListView}}" ...
- 项目的 BindingContext 是项目模型,因此很容易作为参数传递:
<TapGestureRecognizer
Command=... CommandParameter="{Binding .}" />
注意:这与 Wizard 的回答之间的差异:
{Binding .}
是引用项目本身所需要的。- 而不是使用需要指定
Type
的RelativeSource
,我个人的偏好是 命名 视图,然后引用该名称。我发现这更容易阅读和记住如何去做。 - 我省略了所有与问题无关的细节。上面的步骤就足够了。 (
x:DataType
命令对性能有好处,所以我绝不建议不要执行它们。但这是一个单独的主题,恕我直言。)