You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET MAUI中SwipeView滑动触发CollectionView选中项的问题

问题解决:CollectionView嵌套SwipeView时滑动误触发选中跳转的问题

核心原因

Android平台上,CollectionView的SelectedItem/SelectionChangedCommand会将滑动动作误识别为选中操作,导致用户还没选择SwipeView的操作项就触发页面跳转。


解决方案步骤

1. 替换CollectionView选中逻辑为ItemTapCommand

ItemTapCommand仅响应明确的轻触动作,不会被滑动事件误触发,彻底替代原有的选中绑定逻辑。

2. 拦截SwipeView滑动事件

通过ViewModel的状态标记,确保滑动SwipeView时不执行跳转逻辑。

3. 优化触摸事件优先级

确保SwipeView优先捕获触摸事件,避免事件冒泡到CollectionView。


完整修改代码

XAML代码修改
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit" 
             x:Class="GastoMisto.Pages.MateriaisGestaoLista"
             Title="Materiais">
    <ContentPage.ToolbarItems>
        <ToolbarItem Command="{Binding AdicionarMaterialCommand}" IconImageSource="Resources/Images/plus.svg" />
    </ContentPage.ToolbarItems>
    <ContentPage.Behaviors>
        <toolkit:EventToCommandBehavior EventName="Appearing" Command="{Binding LoadDataCommand}" />
    </ContentPage.Behaviors>
    <VerticalStackLayout Padding="5">
        <!-- 移除SelectedItem、SelectionChangedCommand、SelectionMode,改用ItemTapCommand -->
        <CollectionView ItemsSource="{Binding Materiais}"
                        ItemTapCommand="{Binding MaterialTappedCommand}"
                        x:Name="cMateriaisView">
            <CollectionView.ItemTemplate>
                <DataTemplate>
                    <!-- 添加滑动开始/结束事件绑定 -->
                    <SwipeView SwipeStarted="{Binding Source={x:Reference cMateriaisView}, Path=BindingContext.OnSwipeStarted}"
                               SwipeEnded="{Binding Source={x:Reference cMateriaisView}, Path=BindingContext.OnSwipeEnded}">
                        <SwipeView.LeftItems>
                            <SwipeItems Mode="Reveal">
                                <SwipeItem Text="Desativar" Command="{Binding DesativarCommand, Source={x:Reference cMateriaisView}}" CommandParameter="{Binding .}" />
                                <SwipeItem Text="Apagar" Command="{Binding ApagarCommand, Source={x:Reference cMateriaisView}}" CommandParameter="{Binding .}" />
                                <SwipeItem Text="Consultar" Command="{Binding ConsultarCommand, Source={x:Reference cMateriaisView}}" CommandParameter="{Binding .}" />
                            </SwipeItems>
                        </SwipeView.LeftItems>

                        <!-- 设置InputTransparent确保触摸事件优先被SwipeView捕获 -->
                        <Grid RowDefinitions="Auto,Auto" ColumnDefinitions="Auto,Auto" ColumnSpacing="5"
                              InputTransparent="False">
                            <Label Text="{Binding Codigo}" FontAttributes="Bold" Grid.Row="0" Grid.Column="0" />
                            <Label Text="{Binding Designacao}" Grid.Row="0" Grid.Column="1" />
                            <Label Text="{Binding Notas}" Grid.Row="1" Grid.ColumnSpan="2" />
                        </Grid>
                    </SwipeView>
                </DataTemplate>
            </CollectionView.ItemTemplate>
        </CollectionView>
    </VerticalStackLayout>
</ContentPage>
ViewModel代码修改
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;

namespace GastoMisto.ViewModels
{
    public class MateriaisGestaoListaViewModel : INotifyPropertyChanged
    {
        #region INotifyPropertyChanged

        public event PropertyChangedEventHandler PropertyChanged;

        void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }

        #endregion

        private ObservableCollection<Model.Material> _materiais;
        public ObservableCollection<Model.Material> Materiais
        {
            get => _materiais;
            set
            {
                _materiais = value;
                OnPropertyChanged(); // 修复原代码缺失的属性变更通知
            }
        }

        // 标记当前是否处于滑动状态
        private bool _isSwiping;

        // 给操作命令添加参数,直接获取当前Material对象
        public ICommand DesativarCommand => new Command<Model.Material>(async (material) => await Task.FromException<NotImplementedException>(new NotImplementedException()));
        public ICommand ApagarCommand => new Command<Model.Material>(async (material) => await Task.FromException<NotImplementedException>(new NotImplementedException()));
        public ICommand ConsultarCommand => new Command<Model.Material>(async (material) => await Task.FromException<NotImplementedException>(new NotImplementedException()));

        public ICommand LoadDataCommand => new Command(async () => await fillData());

        // 替换原MaterialSelectedCommand为ItemTapCommand,接收点击的Material对象
        public ICommand MaterialTappedCommand => new Command<Model.Material>(async (material) => await SelecionaMaterial(material));

        // 滑动开始时标记状态
        public ICommand OnSwipeStarted => new Command(() => _isSwiping = true);
        // 滑动结束时重置状态
        public ICommand OnSwipeEnded => new Command(() => _isSwiping = false);

        public ICommand AdicionarMaterialCommand => new Command(async () => await Shell.Current.GoToAsync("Materiais/Ficha"));

        public MateriaisGestaoListaViewModel()
        {
            _materiais = new ObservableCollection<Model.Material>();
            MessagingCenter.Subscribe<MateriaisGestaoFichaViewModel>(this, Messages.Refresh, async (sender) => await fillData());
        }

        private async Task fillData()
        {
            Materiais.Clear();
            var materiaisEmBd = await App.DbSingleton.Materiais.GetAll();

            foreach (var esteMaterial in materiaisEmBd)
            {
                Materiais.Add(esteMaterial);
            }
        }

        private async Task SelecionaMaterial(Model.Material material)
        {
            // 若处于滑动状态,直接返回不执行跳转
            if (material == null || _isSwiping)
                return;

            var navigationParameter = new Dictionary<string, object>()
            {
                { "MatEmQuestao", material }
            };

            await Shell.Current.GoToAsync("Materiais/Ficha", navigationParameter);
        }
    }
}

内容的提问来源于stack exchange,提问作者Morv

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 20:24:53