在 ListView 中如何将单击的对象发送回视图模型中的命令 - Xamarin Forms

In a ListView how to send the clicked object back to the command in the view model - Xamarin Forms

给定以下 ListView,我希望有一个命令可以将单击的对象(在本例中为地址)发送回视图模型中的命令 - SelectNewAddressDeleteAddress.

            <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 -->

然后我们把TapGestureRecognizerCommandCommandParameter改成如下:

<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中,我们必须使用PathSource传递当前绑定的对象。请注意,在 Source 中,我们引用的名称 Item 我们之前定义为 Gridx:Name

  1. 确保页面将视图模型作为其 BindingContext。 (如果你正在做 mvvm,那么你已经做过了。)

  2. <flv:FlowListView一个名字:

<flv:FlowListView x:Name="theListView" ... >
  1. 该项需要引用页面视图模型中的命令。 BindingContext 通过层次结构向下传播,因此相对于列表视图名称很容易完成:
    <flv:FlowListView.FlowColumnTemplate>
        <DataTemplate>
            ...
         <TapGestureRecognizer 
              Command="{Binding BindingContext.SelectNewAddress, Source={x:Reference theListView}}" ...
  1. 项目的 BindingContext 是项目模型,因此很容易作为参数传递:
         <TapGestureRecognizer 
              Command=... CommandParameter="{Binding .}" />

注意:这与 Wizard 的回答之间的差异:

  • {Binding .} 是引用项目本身所需要的。
  • 而不是使用需要指定 TypeRelativeSource,我个人的偏好是 命名 视图,然后引用该名称。我发现这更容易阅读和记住如何去做。
  • 我省略了所有与问题无关的细节。上面的步骤就足够了。 (x:DataType 命令对性能有好处,所以我绝不建议不要执行它们。但这是一个单独的主题,恕我直言。)