【问题标题】:Xamarin Forms CollectionView with SwipeView on items - tapping ImageButton selects cellXamarin Forms CollectionView 与项目上的 SwipeView - 点击 ImageButton 选择单元格
【发布时间】:2022-01-25 03:06:27
【问题描述】:

我的CollectionView 的每个单元格中都有一个ImageButton。当我点击ImageButton 时,我希望它能够捕获并处理触摸事件,但它也会将触摸事件传递给单元格并在CollectionView 中选择该单元格。

点击呼叫会更改SelectedItem 并打开该联系人的详细信息页面。点击ImageButton 会开始通话,但会立即切换到详细信息页面。

以下是页面截图:

CollectionView 定义为:

<CollectionView
    x:Name="contactsList"
    ItemsSource="{Binding Contacts}"
    SelectionMode="Single"
    SelectedItem="{Binding SelectedContact, Mode=TwoWay}"
    ItemSizingStrategy="MeasureAllItems"
    IsGrouped="True"
    EmptyView="No Contacts">
    <CollectionView.ItemsLayout>
        <LinearItemsLayout Orientation="Vertical"/>
    </CollectionView.ItemsLayout>
    <CollectionView.GroupHeaderTemplate>
        ...
    </CollectionView.GroupHeaderTemplate>
    <CollectionView.ItemTemplate>
        <DataTemplate>
            <SwipeView
                x:DataType="models:Contact">
                ...
                <StackLayout
                    BackgroundColor="{StaticResource BackgroundColor}">
                    <Grid
                        Padding="0,15,0,10"
                        ColumnDefinitions="80,*,80"
                        RowDefinitions="*,*"
                        BackgroundColor="{StaticResource BackgroundColor}">
                        <Ellipse
                            Grid.Column="0"
                            Grid.Row="0"
                            Grid.RowSpan="2"
                            Fill="{Binding Colour, Converter={StaticResource intToBrushColor}}"
                            .../>
                        <Label
                            Grid.Column="0"
                            Grid.Row="0"
                            Grid.RowSpan="2"
                            Text="{Binding Initials}"
                            .../>
                        <Label
                            Grid.Column="1"
                            Grid.Row="0"
                            Text="{Binding FullName}"
                            .../>
                        <StackLayout
                            Grid.Column="1"
                            Grid.Row="1"
                            Orientation="Horizontal">
                            <Image
                                HeightRequest="15"
                                Source="{Binding WasOutgoing, Converter={StaticResource callDirectionToIcon}}"/>
                            <Label
                                Grid.Column="1"
                                Grid.Row="1"
                                Text="{Binding TimeStamp}"
                                .../>
                        </StackLayout>
                        <ImageButton
                            Grid.Column="2"
                            Grid.Row="0"
                            Grid.RowSpan="2"
                            Margin="0,0,15,0"
                            Padding="10"
                            BackgroundColor="Transparent"
                            Source="{StaticResource IconCalls}"
                            Command="{Binding BindingContext.CallCommand, Source={x:Reference contactsPage}}"
                            CommandParameter="{Binding .}"/>
                    </Grid>
                    <BoxView
                        Style="{StaticResource Seperator}"/>
                </StackLayout>
            </SwipeView>
        </DataTemplate>
    </CollectionView.ItemTemplate>
</CollectionView>

如何使ImageButton 保持触摸事件并在点击ImageButton 时阻止单元格被选中?

以下是我考虑过的一些肮脏的解决方法,但并不理想:

  • 将单元格分成两个Grids 并有两个TapGestureRecognizers。
  • 跟踪ImageButton 是否被点击并忽略下一次选择更改。

这些都不理想,会花费更多并打破 MVVM 模式。此问题的根本原因是 ImageButton 未保留触摸事件或将其标记为已处理。

有人知道这个问题的更清洁的解决方案吗?

【问题讨论】:

    标签: xamarin.forms imagebutton collectionview


    【解决方案1】:

    我已将您的问题缩小到在ItemTemplate 中使用SwipeView。这似乎强制选择该项目。

    没有它,按预期工作。

    我推断 SwipeView 会更改触摸事件,以强制行选择,以执行其操作。

    请参阅下面的WORKAROUND,了解黑客修复。


    xaml:

    <ContentPage.Content>
        <StackLayout>
            <CollectionView
                x:Name="contactsList"
                ItemsSource="{Binding Contacts}"
                SelectionMode="Single"
                SelectedItem="{Binding SelectedContact, Mode=TwoWay}"
                ItemSizingStrategy="MeasureAllItems" >
                <CollectionView.ItemTemplate>
                    <DataTemplate>
                        <!--<SwipeView>-->
                            <StackLayout>
                                <Grid
                                Padding="0,15,0,10"
                                ColumnDefinitions="*,80">
                                    <Label Grid.Column="0" Text="abcdef" />
                                    <Button
                                    Grid.Column="1"
                                    Padding="4"
                                    Text="Press Me"
                                    Clicked="Button_Clicked"
                                    />
                                </Grid>
                            </StackLayout>
                        <!--</SwipeView>-->
                    </DataTemplate>
                </CollectionView.ItemTemplate>
            </CollectionView>
        </StackLayout>
    </ContentPage.Content>
    

    xaml.cs:

    public partial class CollectionViewWithCellButtonPage : ContentPage
    {
        private Model selectedContact;
    
        public CollectionViewWithCellButtonPage()
        {
            InitializeComponent();
            BindingContext = this;
        }
    
        private void Button_Clicked(object sender, EventArgs e)
        {
    
        }
    
        public ObservableCollection<Model> Contacts { get; set; } = new ObservableCollection<Model> {
            new Model(),
            new Model(),
            new Model(),
        };
    
        public Model SelectedContact {
            get => selectedContact;
            set => selectedContact = value;
        }
    }
    

    SelectedContact setter 和Button_Clicked 上有断点,单击按钮不会影响SelectedContact。单击行上的其他位置。这是期望的行为。

    然后取消注释&lt;SwipeView&gt;&lt;/SwipeView&gt;

    现在,SelectedContact setter 被调用。之前Button_Clicked.
    因为调用是 BEFORE,所以我没有看到任何简单的解决方法。

    修复这个“正确”可能需要为SwipeView 自定义渲染器(每个平台)。


    解决方法

    让它工作。但这是一个 hack。

    1. SelectContact 时采取的延迟操作。这让我们有时间查明 Button 是否被按下。 (第 2 步将显示 _suppressSelection 已设置。)
        private Model _selectedContact;
        private bool _suppressSelection;
    
        public Model SelectedContact
        {
            get => _selectedContact;
            set
            {
                Device.BeginInvokeOnMainThread(async () =>
                {
                    await DelayedSetSelectedContact(value);
                });
    
            }
        }
    
        private async Task DelayedSetSelectedContact(Model value)
        {
            await Task.Delay(100);
    
            if (_suppressSelection)
            {
                // Button was pressed. DO NOTHING - DON'T select the item.
    
                // Clear state for next time.
                _suppressSelection = false;
            }
            else
            {
                _selectedContact = value;
                // ... Do your other work here ...
            }
        }
    
    1. 按钮单击设置_suppressSelection。确保 _suppressSelection 不会“卡住”。
        private System.Timers.Timer _buttonTimer;
    
        protected override void OnAppearing()
        {
            base.OnAppearing();
    
            // Make sure _suppressSelection can't get "stuck on".
            _buttonTimer = new System.Timers.Timer { Interval = 500, AutoReset = false };
            _buttonTimer.Elapsed += Timer_Elapsed;
        }
    
        private void Button_Clicked(object sender, EventArgs e)
        {
            // FIRST LINE in method - do this as early as possible.
            _suppressSelection = true;
    
            //... your main logic here ...
    
            // Make sure _suppressSelection can't get "stuck on".
            _buttonTimer.Start();
        }
    
        private void Timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        {
            // "if" line can be commented out. I just have it so breakpoint on following line is only hit if
            // timer is needed to do its job. Some sequences of item selection and button presses do hit that breakpoint.
            if (_suppressSelection)
                _suppressSelection = false;
        }
    
    1. 离开页面时清理。
        protected override void OnDisappearing()
        {
            base.OnDisappearing();
    
            // Stop timer. Release reference.
            if (_buttonTimer != null)
            {
                _buttonTimer.Stop();
                _buttonTimer = null;
            }
            // Clean up state, in case navigate back to page.
            _suppressSelection = false;
        }
    

    CollectionViewWithCellButtonPageToolmakerSteve - repo XFormsSOAnswers 中的完整代码。

    【讨论】:

      猜你喜欢
      • 2020-10-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-02-27
      相关资源
      最近更新 更多