user2159696
user2159696

Reputation: 41

Xamarin Forms - Listview view cell swipe left and right events using xamal

I need to write code (or XAML) for listview viewcell swipe left and right gestures.

When swiping left, delete the records from listview (or observable collection) list and update the remaining listitems in listview. When swiping right, save the records.

Upvotes: 3

Views: 4957

Answers (2)

Andrue Cope
Andrue Cope

Reputation: 249

I've reworked James Lin's class to be command based so that it's MVVM friendly.

public class SwipeGestureGrid : Grid
{
    public static readonly BindableProperty SwipeLeftCommandProperty = 
        BindableProperty.Create("SwipeLeftCommand", typeof(ICommand), typeof(ICommand), null);
    public static readonly BindableProperty SwipeRightCommandProperty =
        BindableProperty.Create("SwipeRightCommand", typeof(ICommand), typeof(ICommand), null);
    public static readonly BindableProperty SwipeUpCommandProperty =
        BindableProperty.Create("SwipeUpCommand", typeof(ICommand), typeof(ICommand), null);
    public static readonly BindableProperty SwipeDownCommandProperty =
        BindableProperty.Create("SwipeDownCommand", typeof(ICommand), typeof(ICommand), null);
    public static readonly BindableProperty TappedCommandProperty =
        BindableProperty.Create("TappedCommand", typeof(ICommand), typeof(ICommand), null);

    private double _gestureStartX;
    private double _gestureStartY;
    private double _gestureDistanceX;
    private double _gestureDistanceY;

    public double GestureStartX
    {
        get => _gestureStartX;
        private set
        {
            _gestureStartX = value;
            OnPropertyChanged();
        }
    }

    public double GestureStartY
    {
        get => _gestureStartY;
        private set
        {
            _gestureStartY = value;
            OnPropertyChanged();
        }
    }

    private bool IsSwipe { get; set; }

    public ICommand TappedCommand
    {
        get => (ICommand) GetValue(TappedCommandProperty);
        set => SetValue(TappedCommandProperty, value);
    }
    public ICommand SwipeLeftCommand
    {
        get => (ICommand)GetValue(SwipeLeftCommandProperty);
        set => SetValue(SwipeLeftCommandProperty, value);
    }
    public ICommand SwipeRightCommand
    {
        get => (ICommand)GetValue(SwipeRightCommandProperty);
        set => SetValue(SwipeRightCommandProperty, value);
    }
    public ICommand SwipeUpCommand
    {
        get => (ICommand)GetValue(SwipeUpCommandProperty);
        set => SetValue(SwipeUpCommandProperty, value);
    }
    public ICommand SwipeDownCommand
    {
        get => (ICommand)GetValue(SwipeDownCommandProperty);
        set => SetValue(SwipeDownCommandProperty, value);
    }

    public SwipeGestureGrid()
    {
        var panGesture = new PanGestureRecognizer();
        panGesture.PanUpdated += PanGesture_PanUpdated;

        var tapGesture = new TapGestureRecognizer();
        tapGesture.Tapped += TapGesture_Tapped;

        GestureRecognizers.Add(panGesture);
        GestureRecognizers.Add(tapGesture);
    }

    private void TapGesture_Tapped(object sender, EventArgs e)
    {
        try
        {
            if (!IsSwipe)
                TappedCommand?.Execute(this);

            IsSwipe = false;
        }
        catch (Exception ex)
        {

        }
    }

    private void PanGesture_PanUpdated(object sender, PanUpdatedEventArgs e)
    {
            switch (e.StatusType)
            {
                case GestureStatus.Started:
                    {
                        GestureStartX = e.TotalX;
                        GestureStartY = e.TotalY;
                    }
                    break;
                case GestureStatus.Running:
                    {
                        _gestureDistanceX = e.TotalX;
                        _gestureDistanceY = e.TotalY;
                    }
                    break;
                case GestureStatus.Completed:
                    {
                        IsSwipe = true;

                        if (Math.Abs(_gestureDistanceX) > Math.Abs(_gestureDistanceY))
                        {
                            if (_gestureDistanceX > 0)
                            {
                                SwipeRightCommand?.Execute(this);
                            }
                            else
                            {
                                SwipeLeftCommand?.Execute(null);
                            }
                        }
                        else
                        {
                            if (_gestureDistanceY > 0)
                            {
                                SwipeDownCommand?.Execute(null);
                            }
                            else
                            {
                                SwipeUpCommand?.Execute(null);
                            }
                        }
                    }
                    break;
            }
    }
}

We use it in a DataTemplate for a ScrollableListView:

<DataTemplate x:Key="ThreeRowTemplateTemplate" x:DataType="{x:Type tracking:TrackingItem}">
            <ViewCell>
                <controls:SwipeGestureGrid ColumnSpacing="0" RowSpacing="0" SwipeLeftCommand="{Binding SwipeLeftCommand}">

In this case the bound command has to be on TrackingItem. It seems to work quite well but the detection is sometimes a bit flaky - possibly there's a conflict with the ScrollableListView's gesture handling.

Upvotes: 1

James Lin
James Lin

Reputation: 21

I've do it using cs code,You can try it on xaml.

First, you should build swipe compoment using gesture

SwipeGestureGrid.cs

public class SwipeGestureGrid : Grid
    {
        #region Private Member
        private double _gestureX { get; set; }
        private double _gestureY { get; set; }
        private bool IsSwipe { get; set; }
        #endregion
        #region Public Member
        #region Events
        #region Tapped
        public event EventHandler Tapped;
        protected void OnTapped(EventArgs e)
        {
            if (Tapped != null)
                Tapped(this, e);
        }
        #endregion
        #region SwipeUP
        public event EventHandler SwipeUP;
        protected void OnSwipeUP(EventArgs e)
        {
            if (SwipeUP != null)
                SwipeUP(this, e);
        }
        #endregion
        #region SwipeDown
        public event EventHandler SwipeDown;
        protected void OnSwipeDown(EventArgs e)
        {
            if (SwipeDown != null)
                SwipeDown(this, e);
        }
        #endregion
        #region SwipeRight
        public event EventHandler SwipeRight;
        protected void OnSwipeRight(EventArgs e)
        {
            if (SwipeRight != null)
                SwipeRight(this, e);
        }
        #endregion
        #region SwipeLeft
        public event EventHandler SwipeLeft;
        protected void OnSwipeLeft(EventArgs e)
        {
            if (SwipeLeft != null)
                SwipeLeft(this, e);
        }
        #endregion
        #endregion
        public double Height
        {
            get
            {
                return HeightRequest;
            }
            set
            {
                HeightRequest = value;
            }
        }
        public double Width
        {
            get
            {
                return WidthRequest;
            }
            set
            {
                WidthRequest = value;
            }
        }
        #endregion
        public SwipeGestureGrid()
        {
            PanGestureRecognizer panGesture = new PanGestureRecognizer();
            panGesture.PanUpdated += PanGesture_PanUpdated;

            TapGestureRecognizer tapGesture = new TapGestureRecognizer();
            tapGesture.Tapped += TapGesture_Tapped;

            GestureRecognizers.Add(panGesture);
            GestureRecognizers.Add(tapGesture);
        }

        private void TapGesture_Tapped(object sender, EventArgs e)
        {
            try
            {
                if (!IsSwipe)
                    OnTapped(null);

                IsSwipe = false;
            }
            catch (Exception ex)
            {

            }
        }

        private void PanGesture_PanUpdated(object sender, PanUpdatedEventArgs e)
        {
            try
            {
                switch (e.StatusType)
                {
                    case GestureStatus.Running:
                        {
                            _gestureX = e.TotalX;
                            _gestureY = e.TotalY;
                        }
                        break;
                    case GestureStatus.Completed:
                        {
                            IsSwipe = true;
                            //Debug.WriteLine("{0}  {1}", _gestureX, _gestureY);
                            if (Math.Abs(_gestureX) > Math.Abs(_gestureY))
                            {
                                if (_gestureX > 0)
                                {
                                    OnSwipeRight(null);
                                }
                                else
                                {
                                    OnSwipeLeft(null);
                                }
                            }
                            else
                            {
                                if (_gestureY > 0)
                                {
                                    OnSwipeDown(null);
                                }
                                else
                                {
                                    OnSwipeUP(null);
                                }
                            }
                        }
                        break;
                }
            }
            catch (Exception ex)
            {
            }
        }
    }

Next using datatemplate in listview andattach event for Gesturecompoment

Page.cs

ListView lsvData = new ListView()
        {
            VerticalOptions = LayoutOptions.Fill,
            HorizontalOptions = LayoutOptions.Fill,
            BackgroundColor = Color.White,
            HasUnevenRows = true,
        };
        List<string> lstData = new List<string>();
        public Pages()
        {
            #region DataTemplate
            DataTemplate ListDataTemplate = new DataTemplate(() =>
            {
                #region DataArea of Template
                SwipeGestureGrid gridData = new SwipeGestureGrid()
                {
                    HorizontalOptions = LayoutOptions.FillAndExpand,
                    VerticalOptions = LayoutOptions.FillAndExpand,
                    HeightRequest = 60,
                    RowDefinitions =
                        {
                            new RowDefinition { },
                        },
                    ColumnDefinitions =
                        {
                            new ColumnDefinition { },
                        }
                };
                #endregion
                #region Base of Template
                Grid gridBase = new Grid()
                {
                    HorizontalOptions = LayoutOptions.FillAndExpand,
                    VerticalOptions = LayoutOptions.FillAndExpand,
                    HeightRequest = 60,
                    RowDefinitions =
                    {
                        new RowDefinition { },
                    },
                    ColumnDefinitions =
                    {
                        new ColumnDefinition { },                                                   //Put Cells Data here
                        new ColumnDefinition { Width = new GridLength(0, GridUnitType.Absolute)},   //Button for Cells here
                    },
                };
                #endregion
                Label lblText = new Label
                {
                    HorizontalOptions = LayoutOptions.FillAndExpand,
                    VerticalOptions = LayoutOptions.FillAndExpand,
                    FontAttributes = FontAttributes.Bold,
                    VerticalTextAlignment = TextAlignment.End,
                    TextColor = Color.Black,
                    BackgroundColor = Color.Silver,
                    LineBreakMode = LineBreakMode.TailTruncation,
                    FontSize = 18,
                };
                lblText.SetBinding(Label.TextProperty, ".");

                ImageButton btnCellDelete = new ImageButton() { Source = "Delete" };

                gridData.Children.Add(lblText, 0, 0);

                gridBase.Children.Add(gridData, 0, 0);
                gridBase.Children.Add(btnCellDelete, 1, 0);

                gridData.SwipeLeft += GridTemplate_SwipeLeft;
                gridData.SwipeRight += GridTemplate_SwipeRight; ;
                gridData.Tapped += GridTemplate_Tapped; ;
                btnCellDelete.Clicked += BtnCellDelete_Clicked; ;

                return new ViewCell
                {
                    View = gridBase,
                    Height = 60,
                };
            });

            #endregion
            for (int i = 1; i <= 100; i++)
            {
                lstData.Add(i.ToString());
            }
            lsvData.ItemTemplate = ListDataTemplate;
            lsvData.ItemsSource = lstData;
            Content = lsvData;
        }

event

SwipeLeft to show DeleteButton

private void GridTemplate_SwipeLeft(object sender, EventArgs e)
    {
        try
        {
            if (sender is SwipeGestureGrid)
            {
                var templateGrid = ((SwipeGestureGrid)sender).Parent;
                if (templateGrid != null && templateGrid is Grid)
                {
                    var CellTemplateGrid = (Grid)templateGrid;
                    CellTemplateGrid.ColumnDefinitions[1].Width = new GridLength(60, GridUnitType.Absolute);
                }
            }

        }
        catch (Exception ex)
        {

        }
    }

swiperight to hide delete button

private void GridTemplate_SwipeRight(object sender, EventArgs e)
    {
        try
        {
            if (sender is SwipeGestureGrid)
            {
                var templateGrid = ((SwipeGestureGrid)sender).Parent;
                if (templateGrid != null && templateGrid is Grid)
                {
                    var CellTemplateGrid = (Grid)templateGrid;
                    CellTemplateGrid.ColumnDefinitions[1].Width = new GridLength(0, GridUnitType.Absolute);
                }
            }
        }
        catch (Exception ex)
        {

        }
    }

Delete button click event

private void BtnCellDelete_Clicked(object sender, EventArgs e)
    {
        try
        {
            if (sender is ImageButton)
            {
                var templateGrid = ((ImageButton)sender);
                //templateGrid.Parent = gridBase
                //templateGrid.Parent.Parent = cell
                if (templateGrid.Parent != null && templateGrid.Parent.Parent != null && templateGrid.Parent.Parent.BindingContext != null && templateGrid.Parent.Parent.BindingContext is string)
                {
                    var deletedate = templateGrid.Parent.Parent.BindingContext as string;
                    lstData.RemoveAll(f => f == deletedate);
                    lsvData.ItemsSource = null;
                    lsvData.ItemsSource = lstData;
                }
            }
        }
        catch (Exception ex)
        {

        }
    }

Source can be find in my github https://github.com/act70255/ListViewSwipeGesture

Upvotes: 1

Related Questions