Xamarin — команда привязки внутри stacklayout datatemplate не работает

#xaml #xamarin #xamarin.forms #command

#xaml #xamarin #xamarin.forms #команда

Вопрос:

Попытка запустить команду внутри stacklayout с помощью itemssource. Интересно, почему NavigateToProductListViewShopTappedCommand не запускается.

Пробовал несколько подходов к командам:

1)

 Command="{Binding Source={RelativeSource AncestorType={x:Type local:MyShopsListViewModel}}, Path=NavigateToProductListViewShopTappedCommand}"
 
 Command="{Binding BindingContext.NavigateToProductListViewShopTappedCommand, Source={x:Reference Page}}"
 
 Command="{Binding Path=BindingContext.NavigateToProductListViewShopTappedCommand, Source={x:Reference Page}}"
 

Ни один из них не работает

Код:

 <?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:controls="clr-namespace:BoerPlaza.Controls.Shop"
             xmlns:local="clr-namespace:BoerPlaza.ViewModels"
             xmlns:behaviors="clr-namespace:BoerPlaza.Behaviors"
             x:Class="BoerPlaza.Views.Shop.MyShopsPage"
             x:Name="Page"
             Title="Mijn winkels">
    <ContentPage.Content>
        <ScrollView>
            <StackLayout Margin="{StaticResource margin-side-std}"
                         Padding="{StaticResource padding-top-bottom-std}"
                         BindableLayout.ItemsSource="{Binding Shops}">
                <BindableLayout.ItemTemplate>
                    <DataTemplate>
                        <controls:ShopCardTemplateView Shop="{Binding .}"
                                                       ControlTemplate="{StaticResource ShopCardTemplateView}">
                            <controls:ShopCardTemplateView.GestureRecognizers>
                                <TapGestureRecognizer NumberOfTapsRequired="1"
                                                      Command="{Binding Source={RelativeSource AncestorType={x:Type local:MyShopsListViewModel}}, Path=NavigateToProductListViewShopTappedCommand}"
                                                      CommandParameter="{Binding .}">
                                                      <!--Command="{Binding BindingContext.NavigateToProductListViewShopTappedCommand, Source={x:Reference Page}}"-->
                                </TapGestureRecognizer>
                            </controls:ShopCardTemplateView.GestureRecognizers>
                        </controls:ShopCardTemplateView>
                    </DataTemplate>
                </BindableLayout.ItemTemplate>
            </StackLayout>
        </ScrollView>
    </ContentPage.Content>
</ContentPage>
 

Код, лежащий в основе

 public partial class MyShopsPage : ContentPage
    {
        private readonly MyShopsListViewModel _viewModel;

        public MyShopsPage()
        {
            InitializeComponent();
            BindingContext = _viewModel = new MyShopsListViewModel(App.ShopDataStore, App.DialogService);
            _viewModel.LoadShopsOnUserIdCommand.Execute("B22698B8-42A2-4115-9631-1C2D1E2AC5F7");
        }

        protected override void OnAppearing()
        {
            base.OnAppearing();
            _viewModel.OnAppearing();
        }
    }
 

ViewModel:

 [QueryProperty(nameof(UserId), nameof(UserId))]
public class MyShopsListViewModel : BaseViewModel
{
    private string _userId;
    private ObservableCollection<ShopDbViewModel> _shops;
    private readonly IShopDataStore _shopDataStore;
    private readonly IDialogService _dialogService;

    public ObservableCollection<ShopDbViewModel> Shops
    {
        get
        {
            return _shops;
        }
        set
        {
            _shops = value;
            OnPropertyChanged(nameof(Shops));
        }
    }

    public void OnAppearing()
    {
        IsBusy = true;
    }
    public ICommand LoadShopsOnUserIdCommand { get; set; }
    public ICommand NavigateToProductListViewShopTappedCommand { get; set; }

    public MyShopsListViewModel(IShopDataStore shopDataStore, IDialogService dialogService)
    {
        this._shopDataStore = shopDataStore;
        this._dialogService = dialogService;

        Shops = new ObservableCollection<ShopDbViewModel>();

        LoadShopsOnUserIdCommand = new Command<string>(async (string userId) => await ExecuteLoadShopsOnUserId(userId));
        NavigateToProductListViewShopTappedCommand = new Command<ShopDbViewModel>(async (ShopDbViewModel shop) => await ExecuteNavigateToProductListViewShopTappedCommandAsync(shop));

    }

    private async Task ExecuteNavigateToProductListViewShopTappedCommandAsync(ShopDbViewModel shop)
    {
        if (shop == null)
            return;

        await Shell.Current.GoToAsync($"{nameof(ProductsPage)}?{nameof(MyProductsListViewModel.ShopId)}={shop.Id}");
    }

    public string UserId
    {
        get
        {
            return _userId;
        }
        set
        {
            _userId = value;
            LoadShopsOnUserIdCommand.Execute(value);
        }
    }

    private async Task ExecuteLoadShopsOnUserId(string userId)
    {
        var current = Connectivity.NetworkAccess;

        if (current == NetworkAccess.Internet)
        {
            try
            {
                Shops.Clear();
                var shops = await _shopDataStore.GetShopOnUserIdAsync(userId);
                foreach(var shop in shops)
                {
                    Shops.Add(shop);
                }
            }
            catch (Exception ex)
            {
                await _dialogService.ShowDialog(ex.Message, "An error has occurred", "OK");
            }
            finally
            {
                IsBusy = false;
            }
        }
        else
        {
            await _dialogService.ShowDialog("No active internet connection", "Connection error", "OK");
            IsBusy = false;
        }
       
    }
}
 

Комментарии:

1. Не забудьте принять ответ, который поможет большему количеству людей, у которых есть подобная проблема.

Ответ №1:

Если вы определяете ICommand в ViewModel напрямую, вы можете задать путь привязки следующим образом

 Command="{Binding Path=BindingContext.NavigateToProductListViewShopTappedCommand, Source={x:Reference Page}}"
 

Комментарии:

1. Не могли бы вы поделиться своим образцом, чтобы я мог протестировать его на своей стороне?

2. github.com/TweakStories/BoerPlaza . Проект можно загрузить отсюда. Это для школы и моего первого проекта, поэтому может быть немного грязно: P. Спасибо

3. Убедитесь, что вы удалили всю личную информацию, такую как пароль, учетную запись и личный IP-адрес

Ответ №2:

Я нашел проблему

     <?xml version="1.0" encoding="UTF-8"?>
<ContentView xmlns="http://xamarin.com/schemas/2014/forms" 
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:ffimage="clr-namespace:FFImageLoading.Forms;assembly=FFImageLoading.Forms"
             xmlns:customcontrols="clr-namespace:BoerPlaza.Controls"
             x:Class="BoerPlaza.Controls.Shop.ShopCardTemplateView">
    <ContentView.Resources>
        <ControlTemplate x:Key="ShopCardTemplateView">
            <!-- Card Header -->
            <!-- for displaying products and categories on homepage -->
            <StackLayout Spacing="1"
                         HorizontalOptions="FillAndExpand"
                         Margin="{StaticResource margin-card}">
                <!-- On click - shows the product detail view page -->
                <StackLayout.GestureRecognizers>
                    <TapGestureRecognizer NumberOfTapsRequired="1" />
                </StackLayout.GestureRecognizers>
                <!-- Image frame -->
                <Frame  BackgroundColor="{StaticResource image-box-color}"
                        CornerRadius="0"
                        HasShadow="False"
                        HorizontalOptions="FillAndExpand"
                        VerticalOptions="FillAndExpand"
                        HeightRequest="100">
                    <!-- Product Image -->
 

….

Как вы можете видеть, это шаблон управления, который я использую для страницы MyShopsPage. Это карточка магазина. Внутри этой карточки магазина у меня уже был StackLayout.Распознаватели жестов. Каким-то образом, когда я нажимал на шаблон элемента управления, я на самом деле нажимал на это.

Я всегда думал, что в событиях все течет сверху вниз, но это кажется другим. То, что находится поверх чего-то другого, ничего не значит в xaml.