Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Detecting tap on label, inside ViewCell, inside ListView

I'm trying to handle something similar (from UI perspective), to: enter image description here

in order to invoke two different business logics for:

  • tapping at ViewCell element itself (inside ListView) - in example navigate to different page
  • tapping at Label element (Clickable Label), which is inside given ViewCell element - in example delete given object or smth else

I would like to have whole "tapping" logic inside page ViewModel.

Based on Xamarin forum proposes, I'm able to invoke some logic of "tapping" my delete action from cell, however directly inside my data model - which in my PoV is not good solution, as I would like to manipulate my List collection (so the most preferable way, would be to have this logic at page ViewModel).

What I have right now:

My page View XAML code looks like:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="App.Views.TestView">
  <ContentPage.Content>
    <StackLayout HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand">
        <ListView HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" ItemsSource="{Binding MyItemsCollection}" SelectedItem="{Binding SelectedItem}">
          <ListView.ItemTemplate>
            <DataTemplate>
              <ViewCell>
                <StackLayout Orientation="Horizontal">

                  <!-- Name Label -->
                  <Label Text="{Binding Name}" VerticalOptions="CenterAndExpand" HorizontalOptions="StartAndExpand" />

                  <!-- Delete "Icon" -->
                  <Label Text="Clickable Label" VerticalOptions="CenterAndExpand" HorizontalOptions="EndAndExpand">
                    <Label.GestureRecognizers>
                      <TapGestureRecognizer Command="{Binding OnClickableLabel}" CommandParameter="{Binding .}" />
                    </Label.GestureRecognizers>
                  </Label>

                </StackLayout>
              </ViewCell>
            </DataTemplate>
          </ListView.ItemTemplate>
        </ListView>
    </StackLayout>
  </ContentPage.Content>
</ContentPage>

My page View C# code looks like (not specific code there, except binding **BindingContext* to page ViewModel):

public partial class TestView : ContentPage
{
    public TestView()
    {
        InitializeComponent();
        BindingContext = ServiceLocator.Current.GetInstance<TestViewModel>();
    }
}

My page ViewModel C# code looks like:

public class TestViewModel : ViewModelBase
{
    public TestViewModel()
    {
        MyItemsCollection = GetMyItemsCollection();
    }

    private List<MyItem> GetMyItemsCollection()
    {
        return new List<MyItem>
        {
            new MyItem
            {
                ID = 1L,
                Name = "Item 1 Name"
            },
            new MyItem
            {
                ID = 2L,
                Name = "Item 2 Name"
            },
            new MyItem
            {
                ID = 3L,
                Name = "Item 3 Name"
            }
        };
    }

    private List<MyItem> _myItemsCollection { get; set; }

    public List<MyItem> MyItemsCollection
    {
        get
        {
            return _myItemsCollection;
        }
        set
        {
            _myItemsCollection = value;
            RaisePropertyChanged();
        }
    }

    private MyItem _SelectedItem { get; set; }

    public MyItem SelectedItem
    {
        get
        {
            return _SelectedItem;
        }
        set
        {
            if (_SelectedItem != value)
            {
                _SelectedItem = value;
                RaisePropertyChanged();

                Debug.WriteLine("SelectedItem: " + _SelectedItem.Name);
            }
        }
    }

    private RelayCommand<object> _OnClickableLabel;

    public RelayCommand<object> OnClickableLabel
    {
        get { return _OnClickableLabel ?? (_OnClickableLabel = new RelayCommand<object>((currentObject) => Test(currentObject))); }
    }

    private void Test(object currentObject)
    {
        Debug.WriteLine("This should work... but it's not working :(");
    }
}

My data model code looks like:

public class MyItem
{
    public long ID { get; set; }
    public string Name { get; set; }

    private RelayCommand<object> _OnClickableLabel;

    public RelayCommand<object> OnClickableLabel
    {
        get { return _OnClickableLabel ?? (_OnClickableLabel = new RelayCommand<object>((currentObject) => Test(currentObject))); }
    }

    private void Test(object currentObject)
    {
        Debug.WriteLine("This works... but it's not good idea, to have it here...");
    }
}

Any idea what needs to be changed, in order to invoke OnClickableLabel directly inside my page ViewModel ? I know, that it's something wrong at:

<TapGestureRecognizer Command="{Binding OnClickableLabel}" CommandParameter="{Binding .}" />

but don't know what :/.

Help! Thanks a lot.

like image 480
Namek Avatar asked Aug 19 '16 07:08

Namek


1 Answers

Ok, I found solution, by extending XAML code:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="App.Views.TestView" x:Name="Page">
  <ContentPage.Content>
    <StackLayout HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand">
        <ListView HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" ItemsSource="{Binding MyItemsCollection}" SelectedItem="{Binding SelectedItem}">
          <ListView.ItemTemplate>
            <DataTemplate>
              <ViewCell>
                <StackLayout Orientation="Horizontal">

                  <!-- Name Label -->
                  <Label Text="{Binding Name}" VerticalOptions="CenterAndExpand" HorizontalOptions="StartAndExpand" />

                  <!-- Delete "Icon" -->
                  <Label Text="Clickable Label" VerticalOptions="CenterAndExpand" HorizontalOptions="EndAndExpand">
                    <Label.GestureRecognizers>
                      <TapGestureRecognizer Command="{Binding Path=BindingContext.OnClickableLabel, Source={x:Reference Page}}" CommandParameter="{Binding .}" />
                    </Label.GestureRecognizers>
                  </Label>

                </StackLayout>
              </ViewCell>
            </DataTemplate>
          </ListView.ItemTemplate>
        </ListView>
    </StackLayout>
  </ContentPage.Content>
</ContentPage>

After that, I got OnClickableLabel command invoked inside my page ViewModel, as expected :).

If someone know "better" solution (better from XAML code point of view), I would like to see it ;).

Thanks a lot everyone!

like image 153
Namek Avatar answered Nov 03 '22 10:11

Namek