【问题标题】:Xamarin Forms: Set Background Color of Item Selected in ListView from ViewModelXamarin Forms:从 ViewModel 设置 ListView 中选择的项目的背景颜色
【发布时间】:2020-04-27 12:07:57
【问题描述】:

当在 ViewModel 的代码中完成选择时,我正在尝试更改列表视图中所选项目的背景颜色。我发现了许多与通过用户操作(即点击)选择项目时更改背景颜色相关的帖子

Xamarin.Forms ListView: Set the highlight color of a tapped item

此解决方案 - Using Item Taped 适用于用户点击列表中的项目,但在某些情况下,用户在表单的其他位置输入数据选择了 ListView 中的项目。

我尝试在 ListView 上为 ItemSelected 添加处理程序

    private void ListView_OnItemSelected(object sender, SelectedItemChangedEventArgs e)
    {
        if ( e.SelectedItem == null ) return;  //The selection is set to null in some cases
        if ( !(sender is ListView listView) ) return;
        if ( !(listView.TemplatedItems[0] is ViewCell cell) ) return;

        SetCellColor(cell);
    }

    private void SetCellColor(ViewCell cell)
    {
        if ( _selectedCell != null )
        {
            _selectedCell.View.BackgroundColor = Color.Transparent;
        }

        cell.View.BackgroundColor = Color.LightGray;
        _selectedCell = cell;
    }

这似乎应该可以工作,但 TemplatedItems 集合是一个内部 Forms 对象,不能像我预期的那样工作。我确实返回了一个 ViewCell,但它不会改变背景颜色。

考虑使用 CustomViewCell 的 OnAppearing 事件做一些事情,但不知道在这种情况下如何知道选择了什么。我也尝试过一些使用渲染器的方法,但这些方法似乎也无法让您知道某个项目已被选中。

提前致谢 -乔

【问题讨论】:

    标签: xamarin.forms


    【解决方案1】:

    这在 Xamarin Forms 中是不行的,你需要使用 Native Method 来做。

    你可以自定义一个ViewCellRenderer来实现它。关于Customizing a ViewCell,你可以参考这个文档。

    创建一个 NativeCell 从 Xamarin Forms 中的 ViewCell 继承。也可以在 ViewCell 中添加其他属性。

    public class NativeCell : ViewCell
    {
        public static readonly BindableProperty NameProperty =
            BindableProperty.Create("Name", typeof(string), typeof(NativeCell), "");
    
        public string Name
        {
            get { return (string)GetValue(NameProperty); }
            set { SetValue(NameProperty, value); }
        }
    }
    

    Xaml 中使用:

    <ListView x:Name="listview"
                            MinimumHeightRequest="96"
                            Margin="16,0">
        <ListView.ItemTemplate>
            <DataTemplate>
                <app19:NativeCell Name="{Binding Name}"/>
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>
    

    ListView 添加数据源

    listview.ItemsSource = DataSource.GetList();
    

    我不会展示DataSource的代码,只包含Name属性及其默认数据。

    最后,在 iOS 中,您需要添加继承自 ViewCellRendererNativeViewCellRenderer

    public class NativeViewCellRenderer : ViewCellRenderer
    {
        public override UITableViewCell GetCell(Cell item, UITableViewCell reusableCell, UITableView tv)
        {
            var nativeCell = (NativeCell)item;
    
            UITableViewCell cell = new UITableViewCell();
    
            cell.TextLabel.Text = nativeCell.Name;
            cell.SelectedBackgroundView = new UIView
            {
                BackgroundColor = UIColor.Blue, //Set the selected color is Blue
            };
            return cell;
        }
    }
    

    现在你会看到效果:

    Android 中与 iOS 相同,您需要添加从 ViewCellRenderer 继承的 NativeViewCellRenderer

    class NativeViewCellRenderer : ViewCellRenderer
    {
        private Android.Views.View _cellCore;
        private Drawable _unselectedBackground;
        private bool _selected = false;
        protected override Android.Views.View GetCellCore(Cell item, Android.Views.View convertView, ViewGroup parent, Context context)
        {
            _cellCore = (context as Activity).LayoutInflater.Inflate(Resource.Layout.NativeAndroidCell, null);
            TextView nameTextView = _cellCore.FindViewById<TextView>(Resource.Id.NameText);
    
            var nativeCell = (NativeCell)item;
            nameTextView.Text = nativeCell.Name;
    
            _selected = false;
            _unselectedBackground = _cellCore.Background;
            return _cellCore;
        }
    
        protected override void OnCellPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            base.OnCellPropertyChanged(sender, e);
            if (e.PropertyName == "IsSelected")
            {
                _selected = !_selected;
                if (_selected)
                {
                    //var extendedViewCell = sender as CustomViewCell;
                    _cellCore.SetBackgroundColor(Android.Graphics.Color.Red);
                }
                else
                {
                    _cellCore.SetBackground(_unselectedBackground);
                }
            }
        }
    }
    

    效果如下:

    【讨论】:

    • 我尝试了与此类似的解决方案,也许我做了一些不正确的事情,但它为每个单元格生成了背景,而不仅仅是选定的单元格。当一个 ListView 项目被选中时,它的行为并没有什么不同。我目前只使用 iOS
    • @JoeH 很高兴您找到了解决方案!如果我的回答有帮助,记得投赞成票:)
    • @JoeH 对了,还有时间记得标记答案。
    【解决方案2】:

    我确实提出了一个我很满意并且不需要渲染器的解决方案。在我的自定义 ViewCell 中,我按照建议添加了 SelectedBackgroundColor BindableProperty。

            /// <summary>
        /// The SelectedBackgroundColor property.
        /// </summary>
        public static readonly BindableProperty SelectedBackgroundColorProperty =
            BindableProperty.Create("SelectedBackgroundColor", typeof(Color), typeof(SymbolViewCell), Color.Transparent, propertyChanged:SelectionColorChanged);
    
        public Color SelectedBackgroundColor
        {
            get => (Color)GetValue(SelectedBackgroundColorProperty);
            set => SetValue(SelectedBackgroundColorProperty, value);
        }
    
        private static void SelectionColorChanged(BindableObject bindable, object oldvalue, object newvalue)
        {
            if ( !(bindable is SymbolViewCell viewCell) ) return;
    
            var color = (Color) newvalue;
    
            viewCell.View.BackgroundColor = color;
        }
    

    然后我使用自定义转换器。这实际上是一个通用转换器,用于在其他地方根据真/假界限值设置值。

    public class ConfigurableBoolConverter<T> : IValueConverter
    {
        public ConfigurableBoolConverter() { }
    
        public ConfigurableBoolConverter(T trueResult, T falseResult)
        {
            TrueResult = trueResult;
            FalseResult = falseResult;
        }
    
        public T TrueResult { get; set; }
        public T FalseResult { get; set; }
    
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (TrueResult == null || FalseResult == null) return !(bool)value;
    
    
            return value is bool b && b ? TrueResult : FalseResult;
        }
    
        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (TrueResult == null || FalseResult == null) return !(bool)value;
    
            return value is T variable && EqualityComparer<T>.Default.Equals(variable, TrueResult);
        }
    }
    

    在 Xaml 中,我定义了转换器并将 True/False 值设置为所需的背景颜色:

    <converters:ConfigurableBoolConverter x:Key="BackgroundColorConverter"
                                      x:TypeArguments="x:String"
                                      TrueResult="Color.LightGray"
                                      FalseResult="Color.Transparent"/>
    

    然后将 Converter 分配给自定义 ViewCell。在自定义 ViewCell 中,SelectedBackgroundColor 是使用转换器设置的。作为说明,SymbolViewCell 已经存在,用于解决作为项目一部分的图像正确刷新的不同问题

    <DataTemplate>
         <views:SymbolViewCell 
              SelectedBackgroundColor="{Binding IsChecked, Converter={StaticResource 
                    BackgroundColorConverter}}"/>
    </DataTemplate>
    

    IsChecked 是 ItemsDataSource 的 Item 的一个属性。 ListView 已经使用了 Item 对象的集合,并且该对象已经具有 IsChecked 属性。

    将 Item 对象剥离到最低限度(BindableBase 实现 IPropertyChanged 接口):

    public class SymbolItem : BindableBase
    {
        private bool? _isChecked;
    
    
        public SymbolItem(LegendInfo legendInfo, FeatureTemplate featureTemplate, ArcGISFeatureTable featureTable, IEnumerable<string> requiredFields)
        {
            IsChecked = false;
        }
    
    
    
        public bool? IsChecked
        {
            get => _isChecked;
            set => SetProperty(ref _isChecked, value);
        }
    }
    

    如果 ItemsDataSource 是字符串对象的集合,则此解决方案将不起作用,因为它确实需要附加属性,并且您需要绑定 SelectedItem 属性作为触发 IsChecked 属性更改的位置。但是可以创建一个简单的对象,该对象具有名称和 IsChecked 属性以进行绑定。我个人认为这个添加的代码比编写渲染器来处理事情要简单得多。

    public SymbolItem SelectedSymbolItem
    {
        get => _selectedSymbolItem;
        set
        {
            if ( _selectedSymbolItem != null ) _selectedSymbolItem.IsChecked = false;
            SetProperty(ref _selectedSymbolItem, value);
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-03-26
      • 2010-12-12
      • 2019-06-20
      • 1970-01-01
      • 2011-08-28
      • 2015-04-19
      相关资源
      最近更新 更多