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

MAUI CollectionView替换项时仅Windows端滚动至顶部问题

解决Windows端CollectionView滚动重置问题的两种方案

方案1:使用DataTrigger替代DataTemplateSelector(推荐)

避免替换集合项,通过数据触发自动切换UI状态,不会触发CollectionView的滚动重置:

  1. 修改XAML,移除DataTemplateSelector,改用带DataTrigger的单一DataTemplate:
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:sys="clr-namespace:System;assembly=netstandard"
             xmlns:viewmodel="clr-namespace:YourNamespace.ViewModels"
             x:Class="YourNamespace.Pages.FlatRatePage">

    <ContentPage.Resources>
        <DataTemplate x:Key="FlatRateItemTemplate" x:DataType="viewmodel:FlatRateUI">
            <Grid Padding="10" Margin="0,5,0,0">
                <!-- 通过DataTrigger切换背景色 -->
                <Grid.Style>
                    <Style TargetType="Grid">
                        <Setter Property="BackgroundColor" 
                                Value="{AppThemeBinding Light={StaticResource FlatRateBGNoItemsLight}, Dark={StaticResource FlatRateBGNoItemsDark}}"/>
                        <Style.Triggers>
                            <DataTrigger TargetType="Grid" 
                                         Binding="{Binding Qty}" 
                                         Value="{x:Static sys:Int32.Zero}" 
                                         Comparison="GreaterThan">
                                <Setter Property="BackgroundColor" 
                                        Value="{AppThemeBinding Light={StaticResource FlatRateBGHasItemsLight}, Dark={StaticResource FlatRateBGHasItemsDark}}"/>
                            </DataTrigger>
                        </Style.Triggers>
                    </Style>
                </Grid.Style>

                <!-- 商品名称等公共内容 -->
                <Label Text="{Binding ItemName}" VerticalOptions="Center"/>

                <!-- Add按钮 -->
                <Button Text="Add" 
                        Command="{Binding Source={RelativeSource AncestorType={x:Type viewmodel:YourViewModel}}, Path=AddCommand}" 
                        CommandParameter="{Binding .}"
                        HorizontalOptions="End" VerticalOptions="Center"/>

                <!-- Remove按钮:仅在Qty>0时显示 -->
                <Button Text="Remove" 
                        Command="{Binding Source={RelativeSource AncestorType={x:Type viewmodel:YourViewModel}}, Path=RemoveCommand}" 
                        CommandParameter="{Binding .}"
                        HorizontalOptions="End" VerticalOptions="Center" Margin="0,0,80,0">
                    <Button.Style>
                        <Style TargetType="Button">
                            <Setter Property="IsVisible" Value="False"/>
                            <Style.Triggers>
                                <DataTrigger TargetType="Button" 
                                             Binding="{Binding Qty}" 
                                             Value="{x:Static sys:Int32.Zero}" 
                                             Comparison="GreaterThan">
                                    <Setter Property="IsVisible" Value="True"/>
                                </DataTrigger>
                            </Style.Triggers>
                        </Style>
                    </Button.Style>
                </Button>
            </Grid>
        </DataTemplate>
    </ContentPage.Resources>

    <VerticalStackLayout>
        <HorizontalStackLayout>
            <Button x:Name="BackButton" Text="&lt; Back" VerticalOptions="Center" Clicked="OnBackButtonClicked" HorizontalOptions="Center"/>
            <Button x:Name="CartButton" Text="Cart" VerticalOptions="Center" Clicked="OnCartButtonClicked" HorizontalOptions="Center"/>
            <Button x:Name="SCButton" Text="SC" VerticalOptions="Center" Clicked="OnSCButtonClicked" HorizontalOptions="Center"/>
        </HorizontalStackLayout>
        <CollectionView ItemsSource="{Binding Items}"
                        ItemTemplate="{StaticResource FlatRateItemTemplate}"
                        VerticalOptions="Fill"
                        HeightRequest="680"
                        HorizontalOptions="Fill"/>
    </VerticalStackLayout>
</ContentPage>
  1. 修改FlatRateUI实现INotifyPropertyChanged,确保Qty变化时触发属性通知:
using System.ComponentModel;
using System.Runtime.CompilerServices;

namespace YourNamespace.ViewModels
{
    public class FlatRateUI : INotifyPropertyChanged
    {
        private int _qty;

        public string ItemName { get; set; }

        public int Qty
        {
            get => _qty;
            set
            {
                if (_qty != value)
                {
                    _qty = value;
                    OnPropertyChanged();
                }
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}
  1. 简化Add/Remove命令,无需替换集合项:
[RelayCommand]
async Task Add(FlatRateUI i)
{
    System.Diagnostics.Debug.WriteLine($"{i.ItemName} {i.Qty}");
    i.Qty += 1;
}

[RelayCommand]
async Task Remove(FlatRateUI i)
{
    System.Diagnostics.Debug.WriteLine($"{i.ItemName} {i.Qty}");
    if (i.Qty > 0)
    {
        i.Qty -= 1;
    }
}

方案2:保留DataTemplateSelector,手动保存并恢复滚动位置

如果必须使用原有的DataTemplateSelector方式,需要在替换集合项前后保存滚动位置,替换后恢复:

  1. 在XAML中给CollectionView命名:
<CollectionView x:Name="ItemsCollectionView" 
                ItemsSource="{Binding Items}"
                ItemTemplate="{StaticResource FlatRateItemSelector}"
                VerticalOptions="Fill"
                HeightRequest="680"
                HorizontalOptions="Fill"/>
  1. 修改Add/Remove命令,保存并恢复滚动位置:
[RelayCommand]
async Task Add(FlatRateUI i)
{
    System.Diagnostics.Debug.WriteLine($"{i.ItemName} {i.Qty}");
    i.Qty += 1;
    if (i.Qty == 1)
    {
        // 保存当前滚动位置
        var scrollPosition = ItemsCollectionView.ScrollY;
        
        int idx = Items.IndexOf(i);
        Items[idx] = i;
        
        // 等待UI更新后恢复滚动位置
        await Task.Delay(100);
        ItemsCollectionView.ScrollToAsync(scrollPosition, ScrollToPosition.Start, false);
    }
}

[RelayCommand]
async Task Remove(FlatRateUI i)
{
    System.Diagnostics.Debug.WriteLine($"{i.ItemName} {i.Qty}");
    if (i.Qty > 0)
    {
        i.Qty -= 1;
        if (i.Qty == 0)
        {
            // 保存当前滚动位置
            var scrollPosition = ItemsCollectionView.ScrollY;
            
            int idx = Items.IndexOf(i);
            Items[idx] = i;
            
            // 等待UI更新后恢复滚动位置
            await Task.Delay(100);
            ItemsCollectionView.ScrollToAsync(scrollPosition, ScrollToPosition.Start, false);
        }
    }
}

注意:这种方式依赖Task.Delay,稳定性不如方案1,仅作为兼容原有代码的备选方案。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 05:15:55