Xamarin.Forms CollectionView加点击手势改背景色多item异常变色问题
问题原因及解决方案
问题原因
这个问题是Xamarin.Forms中CollectionView的UI虚拟化(视图复用)机制导致的:
- CollectionView不会为所有数据项单独创建对应的视图容器,只会创建足以填满可视区域的少量视图,当你滚动列表时,会把滚出屏幕的视图回收,重新绑定新的数据后复用给新滚入屏幕的项。
- 你直接在点击事件里修改
Frame的背景色,相当于修改了可复用视图的属性,这个视图被复用到其他数据项时,修改后的背景色不会自动重置,就会出现未点击的项也跟着变色的问题。
解决方案
不要直接修改视图的属性,把选中状态下沉到绑定的数据源模型中,通过数据绑定自动控制背景色,步骤如下:
- 给你的数据模型添加
IsSelected属性,并且实现INotifyPropertyChanged接口:
public class YourItemModel : INotifyPropertyChanged { private bool _isSelected; public bool IsSelected { get => _isSelected; set { _isSelected = value; OnPropertyChanged(); } } private string _title; public string Title { get => _title; set { _title = value; OnPropertyChanged(); } } public event PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged([CallerMemberName] string propertyName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } }
- 实现bool到颜色的值转换器:
public class SelectedToColorConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { if (value is bool isSelected && isSelected) { return Color.FromHex("#60CED3"); } return Color.FromHex("#2C2E31"); } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { throw new NotImplementedException(); } }
- 修改自定义ContentView的XAML,添加转换器引用,绑定Frame的背景色:
首先在ContentView的头部添加转换器资源:
<ContentView.Resources> <local:SelectedToColorConverter x:Key="SelectedToColor"/> </ContentView.Resources>
然后修改Frame的属性:
<Frame BackgroundColor="{Binding IsSelected, Converter={StaticResource SelectedToColor}}" IsClippedToBounds="False" Padding="15,15,15,15" CornerRadius="12">
- 修改点击事件逻辑,只修改模型的IsSelected属性,如果需要单选效果,还要遍历数据源把其他项的IsSelected设为false:
void TapGestureRecognizer_Tapped(System.Object sender, System.EventArgs e) { var item = sender as Xamarin.Forms.Frame; if (item.BindingContext is YourItemModel model) { // 如果需要单选,先把所有项的选中状态取消,yourCollectionViewItemsSource替换为你绑定给CollectionView的数据源 foreach (var m in yourCollectionViewItemsSource) { m.IsSelected = false; } // 再设置当前项为选中 model.IsSelected = true; } }
内容的提问来源于stack exchange,提问作者innom
相关产品推荐
相关产品推荐

