Xamarin.Forms ListView:设置点击项目的突出显示颜色


75

使用Xamarin.Forms,如何定义选定/点击的ListView项目的突出显示/背景颜色?

(我的列表具有黑色背景和白色文本颜色,因此iOS上的默认突出显示颜色太亮。相比之下,Android上根本没有突出显示-直到细微的水平灰线为止。)

示例:(左:iOS,右:Android;同时按下“ Barn2”)

Answers:


117

在Android中,只需在Resources \ values下编辑您的styles.xml文件,然后添加以下代码:

<resources>
  <style name="MyTheme" parent="android:style/Theme.Material.Light.DarkActionBar">
   <item name="android:colorPressedHighlight">@color/ListViewSelected</item>
   <item name="android:colorLongPressedHighlight">@color/ListViewHighlighted</item>
   <item name="android:colorFocusedHighlight">@color/ListViewSelected</item>
   <item name="android:colorActivatedHighlight">@color/ListViewSelected</item>
   <item name="android:activatedBackgroundIndicator">@color/ListViewSelected</item>
  </style>
<color name="ListViewSelected">#96BCE3</color>
<color name="ListViewHighlighted">#E39696</color>
</resources>

5
该问题仅在Android上发生,所以这对我来说是最好/最干净的解决方案
Ateik

1
对我来说也是最简单的解决方案。真可惜Xamarin Forms没有为此提供可自定义的属性。
Marin Shalamanov

如果主项目在pcl上,此解决方案将起作用吗?
Pxaml

1
我创建了Style.xml,因为我的Android项目中没有任何内容,但这对我不起作用。我是否需要添加其他内容?也许是一个特殊的主题?我正在使用Xamarin.Forms。
Denis Vitez

68

看来实际上有一种跨平台的方法可以同时在iOS和Android(对于Windows不确定)上运行。它仅使用绑定,不需要自定义渲染器(这似乎很少)。这是很多谷歌搜索的集合,所以要感谢我可能从中借来的任何人...

我假设使用ViewCells,但这也适用于Text或Image单元。我仅在此处包括典型文本,图像等之外的相关代码。

在您的页面上执行以下操作:

MyModel model1 = new MyModel();
MyModel model2 = new MyModel();

ListView list = new ListView
{
    ItemsSource = new List<MyModel> { model1, model2 };
    ItemTemplate = new DataTemplate( typeof(MyCell) )
};

您的自定义模型可能如下所示:

public class MyModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    private Color _backgroundColor;

    public Color BackgroundColor 
    { 
        get { return _backgroundColor; } 
        set 
        { 
            _backgroundColor = value; 

            if ( PropertyChanged != null )
            {
                PropertyChanged( this, new PropertyChangedEventArgs( "BackgroundColor" ) );
            }
        }
    }

    public void SetColors( bool isSelected )
    {
        if ( isSelected )
        {
            BackgroundColor = Color.FromRgb( 0.20, 0.20, 1.0 );
        }
        else
        {
            BackgroundColor = Color.FromRgb( 0.95, 0.95, 0.95 ); 
        }
    }
}

然后,对于您的ItemTemplate,您需要一个类似以下内容的自定义单元格类:

public class MyCell : ViewCell
{
    public MyCell() : base()
    {
        RelativeLayout layout = new RelativeLayout();
        layout.SetBinding( Layout.BackgroundColorProperty, new Binding( "BackgroundColor" ) );

        View = layout;
    }
}

然后在您的ItemSelected事件处理程序中,执行以下操作。请注意,“ selected”是MyModel的一个实例,用于跟踪当前选定的项目。我仅在此处显示背景色,但我也使用此技术来反向突出显示文本和细节文本颜色。

private void ItemSelected( object sender, ItemTappedEventArgs args )
{
    // Deselect previous
    if ( selected != null )
    {
        selected.SetColors( false );
    }

    // Select new
    selected = (list.SelectedItem as MyModel);
    selected.SetColors( true );
}

1
这有效-至少一点点。我发现选择一个项目时可以使用,但是当我选择另一个项目时,第一个可以回到“旧”背景-除非我滚动以隐藏它,然后再次滚动以显示它,在这种情况下重绘中使用“新”颜色。
切片2014年

2
天哪,谢谢!我自己想到了这个,但是当我尝试时却没用。我不确定我做错了什么,但是复制您的代码是可行的。
SpaceMonkey

5
它不起作用,您只需将鼠标悬停在该行上,仍然保留旧颜色,并且仍然可以在底部看到该颜色的1像素行
animekun 2015年

5
@ Greag.Deay-ListView.SeparatorVisibility = SeparatorVisibility.None; ListView.SeparatorColor= Color.Transparent;应该解决您提到的问题。
Rohit Vipin Mathews,2015年

这种方法在我的UWP项目以及Android和iOS上都可以正常工作。踢屁股!
Scuzzlebutt

33

的iOS

解:

在自定义中,ViewCellRenderer您可以设置SelectedBackgroundView。只需创建一个UIView具有您选择的背景色的新产品即可完成设置。

public override UITableViewCell GetCell(Cell item, UITableViewCell reusableCell, UITableView tv)
{
    var cell =  base.GetCell(item, reusableCell, tv);

    cell.SelectedBackgroundView = new UIView {
        BackgroundColor = UIColor.DarkGray,
    };

    return cell;
}

结果:

注意:

使用Xamarin.Forms似乎很重要,这是创建一个新的 UIView而不是仅仅设置当前颜色的背景色。


安卓

解:

我在Android上找到的解决方案要复杂一些:

  1. ViewCellBackground.xmlResources>drawable文件夹中创建一个新的drawable :

    <?xml version="1.0" encoding="UTF-8" ?>
    <selector xmlns:android="http://schemas.android.com/apk/res/android">
        <item android:state_pressed="true" >
            <shape android:shape="rectangle">
                <solid android:color="#333333" />
            </shape>
        </item>
        <item>
            <shape android:shape="rectangle">
                <solid android:color="#000000" />
            </shape>
        </item>
    </selector>
    

    它为UI元素的默认状态和“按下”状态定义了具有不同颜色的实体形状。

  2. View为您的ViewCell,使用继承的类,例如:

    public class TouchableStackLayout: StackLayout
    {
    }
    
  3. 为此类设置背景资源,实现一个自定义渲染器:

    public class ElementRenderer: VisualElementRenderer<Xamarin.Forms.View>
    {
        protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.View> e)
        {
            SetBackgroundResource(Resource.Drawable.ViewCellBackground);
    
            base.OnElementChanged(e);
        }
    }
    

结果:


如果设置背景色不适用于现有的UIView,请尝试在设置其背景色后调用单元格的SetNeedsLayout(或调用该方法的任何方法)。UITableView尝试尽可能不执行布局
Sten Petrov 2014年

@StenPetrov:不,SetNeedsDisplay或者```SetNeedsLayout```不起作用。但这并不重要,因为分配new UIView {...}是一个很短的解决方法。
Falko 2014年

以及如何更改字体颜色?
埃德加

在Android上,所选颜色不会为我保留。我尝试使用android:state_selected .. ::
masterwok

19

要更改selected的颜色ViewCell,有一个简单的过程无需使用自定义渲染器。使Tapped您的事件ViewCell如下

<ListView.ItemTemplate>
    <DataTemplate>
        <ViewCell Tapped="ViewCell_Tapped">            
        <Label Text="{Binding StudentName}" TextColor="Black" />
        </ViewCell>
    </DataTemplate>
</ListView.ItemTemplate>

在您的ContentPage或.cs文件中,实现事件

private void ViewCell_Tapped(object sender, System.EventArgs e)
{
    if(lastCell!=null)
    lastCell.View.BackgroundColor = Color.Transparent;
    var viewCell = (ViewCell)sender;
    if (viewCell.View != null)
    {
        viewCell.View.BackgroundColor = Color.Red;
        lastCell = viewCell;
    }
} 

像这样lastCell在您的顶部声明ContentPageViewCell lastCell;


1
最好的解决方案!谢谢!
a.tarasevich

2
该解决方案应具有最高投票数,并且必须放在首位,因为这是最简单的选择。
user3559462 '20

2
最好的解决方案,最简单,最漂亮。应该在顶部
Tornike Gomareli

1
仅当您从菜单中选择一个项目时,它才起作用。第一次显示菜单时不起作用。
路易斯·勒马

16

仅适用于Android

添加您的自定义主题

<item name="android:colorActivatedHighlight">@android:color/transparent</item>

13

我有一个类似的过程,完全跨平台,但是我自己跟踪选择状态,并且我已经在XAML中做到了。

<ListView x:Name="ListView" ItemsSource="{Binding ListSource}" RowHeight="50">
  <ListView.ItemTemplate>
    <DataTemplate>
      <ViewCell>
        <ViewCell.View>
          <ContentView Padding="10" BackgroundColor="{Binding BackgroundColor}">
            <Label Text="{Binding Name}" HorizontalOptions="Center" TextColor="White" />
          </ContentView>
        </ViewCell.View>
      </ViewCell>
    </DataTemplate>
  </ListView.ItemTemplate>
</ListView>

然后在ItemTapped事件中

ListView.ItemTapped += async (s, e) =>
{
    var list = ListSource;
    var listItem = list.First(c => c.Id == ((ListItem)e.Item).Id);
    listItem.Selected = !listItem.Selected;
    SelectListSource = list;
    ListView.SelectedItem = null;
};

如您所见,我只是将ListView.SelectedItem设置为null来删除任何正在发挥作用的特定于平台的选择样式。

在我的模型中

private Boolean _selected;

public Boolean Selected
{
    get => _selected;
    set
    {
        _selected = value;
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs("BackgroundColor"));
    }
}

public Color BackgroundColor
{
    get => Selected ? Color.Black : Color.Blue;
}

是否也将用于上下文操作菜单?例如长按android并在iOS上向左滑动?
mr5

我无法调用list.First(...)..它说IEnumerable不包含First的定义...
王子

@Prince-您将需要使用System.Linq添加;在顶部。
亚当

如果您使用的是System.Linq,则IEnumerable当然具有扩展名.First()。如果您不能解决此问题,请在StackOverflow上询问另一个问题以调试该特定问题。
亚当

由于所选项目被更改ItemSelected,因此它再次触发事件,因此在我的情况下,其他代码遇到了异常。
莫尔斯

12

我遇到了同样的问题,我也按照Falko的建议通过为iOS创建自定义渲染器来解决了这个问题,但是,我避免了对Android的样式修改,因此我想出了一种也可以为Android使用自定义渲染器的方法。

对于android view cell来说,所选标志始终为false有点奇怪,这就是为什么我必须创建一个新的private属性来对其进行跟踪的原因。但是除此之外,如果您想在两个平台上都使用自定义渲染器,那么我认为这遵循更合适的模式。就我而言,我对TextCell做到了这一点,但我相信它对其他CellView也会采用相同的方式。

Xamarin形式

using Xamarin.Forms;

public class CustomTextCell : TextCell
    {
        /// <summary>
        /// The SelectedBackgroundColor property.
        /// </summary>
        public static readonly BindableProperty SelectedBackgroundColorProperty =
            BindableProperty.Create("SelectedBackgroundColor", typeof(Color), typeof(CustomTextCell), Color.Default);

        /// <summary>
        /// Gets or sets the SelectedBackgroundColor.
        /// </summary>
        public Color SelectedBackgroundColor
        {
            get { return (Color)GetValue(SelectedBackgroundColorProperty); }
            set { SetValue(SelectedBackgroundColorProperty, value); }
        }
    }

的iOS

public class CustomTextCellRenderer : TextCellRenderer
    {
        public override UITableViewCell GetCell(Cell item, UITableViewCell reusableCell, UITableView tv)
        {
            var cell = base.GetCell(item, reusableCell, tv);
            var view = item as CustomTextCell;
            cell.SelectedBackgroundView = new UIView
            {
                BackgroundColor = view.SelectedBackgroundColor.ToUIColor(),
            };

            return cell;
        }
    }

安卓

public class CustomTextCellRenderer : TextCellRenderer
{
    private Android.Views.View cellCore;
    private Drawable unselectedBackground;
    private bool selected;

    protected override Android.Views.View GetCellCore(Cell item, Android.Views.View convertView, ViewGroup parent, Context context)
    {
        cellCore = base.GetCellCore(item, convertView, parent, context);

        // Save original background to rollback to it when not selected,
        // We assume that no cells will be selected on creation.
        selected = false;
        unselectedBackground = cellCore.Background;

        return cellCore;
    }

    protected override void OnCellPropertyChanged(object sender, PropertyChangedEventArgs args)
    {
        base.OnCellPropertyChanged(sender, args);

        if (args.PropertyName == "IsSelected")
        {
            // I had to create a property to track the selection because cellCore.Selected is always false.
            // Toggle selection
            selected = !selected;

            if (selected)
            {
                var customTextCell = sender as CustomTextCell;
                cellCore.SetBackgroundColor(customTextCell.SelectedBackgroundColor.ToAndroid());
            }
            else
            {
                cellCore.SetBackground(unselectedBackground);
            }
        }
    }
}

...然后,在.xaml页面中,您需要将XMLNS引用添加回新的CustomViewCell ...

xmlns:customuicontrols="clr-namespace:MyMobileApp.CustomUIControls"

并且不要忘记在XAML中实际使用新的Custom comtrol。


谢谢一堆,这节省了我几个小时!我可以确认它适用于ViewCells(这是我所需要的)
wislon

2
在设置ListView的SelectedItem的默认值时,有人能使用它吗?
Filipe Silva

这对于使用RetainElement缓存策略的ListView非常有效,但是对于使用RecycleElement或RecycleElementAndDataTemplate策略的ListViews来说,IsSelected属性永远不会更改-但是我们确实更改了'BindingContext'和'Index'属性。我仍在尝试找出是否可以以某种方式使用它们。很近!:)
wislon

1
@Extragorey,您可能错过了ExportRenderer调用:[assembly:ExportRenderer(typeof(ExtendedViewCell),typeof(ExtendedViewCellRenderer))]
thomasgalliker

1
我使用的是这种技术,但我不得不说,就像@FilipeSilva所说的,如果在视图模型中初始化选定的项,则不会起作用。仅与用户互动一起使用。我曾尝试解决,但未找到解决方法。
Nk54

7

这是纯粹的跨平台和整洁的方式:

1)定义触发动作

namespace CustomTriggers {
   public class DeselectListViewItemAction:TriggerAction<ListView> {
       protected override void Invoke(ListView sender) {
                sender.SelectedItem = null;
       }
   }
}

2)将上述类实例作为XAML中的EventTrigger操作应用,如下所示

 <ListView x:Name="YourListView" ItemsSource="{Binding ViewModelItems}">
    <ListView.Triggers>
        <EventTrigger Event="ItemSelected">
            <customTriggers:DeselectListViewItemAction></customTriggers:DeselectListViewItemAction>
        </EventTrigger>
    </ListView.Triggers>
</ListView>

不要忘记添加 xmlns:customTriggers="clr-namespace:CustomTriggers;assembly=ProjectAssembly"

注意:由于所有项目都不处于选定模式,因此选择样式将不会应用到两个平台上。


这如何帮助“设置点击的项目的突出显示/背景颜色”?看来您所做的是抑制任何颜色变化。
ToolmakerSteve

1
它不会以任何方式改变背景颜色。但是在用户希望抑制项目选择时很有用。这在以下情况下通常很有用:当用户要在ListView点击的某个项目但想隐藏所选项目时启动详细视图时。
zafar

2

我有&使用类似于@ adam-pedley的解决方案。没有自定义渲染器,在XAML中我绑定了背景ViewCell属性

                <ListView x:Name="placesListView" Grid.Row="2" Grid.ColumnSpan="3" ItemsSource="{Binding PlacesCollection}" SelectedItem="{Binding PlaceItemSelected}">
                <ListView.ItemTemplate>
                    <DataTemplate>
                        <ViewCell>
                            <Grid BackgroundColor="{Binding IsSelected,Converter={StaticResource boolToColor}}">
                                <Grid.RowDefinitions>
                                    <RowDefinition Height="auto"/>
                                    <RowDefinition Height="auto"/>
                                </Grid.RowDefinitions>
                                <Grid.ColumnDefinitions>
                                    <ColumnDefinition Width="*" />
                                    <ColumnDefinition Width="*" />
                                </Grid.ColumnDefinitions>

                                <Label Grid.Row="1" Grid.ColumnSpan="2" Text="{Binding DisplayName}" Style="{StaticResource blubeLabelBlackItalic}" FontSize="Default" HorizontalOptions="Start" />
                                <Label Grid.Row="2" Grid.ColumnSpan="2" Text="{Binding DisplayDetail}"  Style="{StaticResource blubeLabelGrayItalic}" FontSize="Small" HorizontalOptions="Start"/>
                                <!--
                                <Label Grid.RowSpan="2" Grid.ColumnSpan="2" Text="{Binding KmDistance}"  Style="{StaticResource blubeLabelGrayItalic}" FontSize="Default" HorizontalOptions="End" VerticalOptions="Center"/>
                                -->
                            </Grid>
                        </ViewCell>
                    </DataTemplate>
                </ListView.ItemTemplate>                    
            </ListView>

在代码(MVVM)中,我保存由boolToColor Converter选择的lastitem我更新背景色

    public class BoolToColorConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            return (bool)value ? Color.Yellow : Color.White;
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            return (Color)value == Color.Yellow ? true : false;
        }
    }

    PlaceItem LastItemSelected;

    PlaceItem placeItemSelected;
    public PlaceItem PlaceItemSelected
    {
        get
        {
            return placeItemSelected;
        }

        set
        {
            if (LastItemSelected != null)
                LastItemSelected.IsSelected = false;

            placeItemSelected = value;
            if (placeItemSelected != null)
            {
                placeItemSelected.IsSelected = true;
                LastItemSelected = placeItemSelected;
            }
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(PlaceItemSelected)));
        }
    }

我的示例是通过Xamarin表单地图(相同的内容页面)中的位置的列表视图提取的。我希望这个解决方案对某人有用


1

为了设置突出显示的项目的颜色,您需要cell.SelectionStyle在iOS中设置的颜色。

本示例将点击项目的颜色设置为透明。

如果需要,可以用中的其他颜色进行更改UITableViewCellSelectionStyle。通过在Forms项目中创建一个新的Custom ListView渲染器,可以在iOS的平台项目中编写该代码。

public class CustomListViewRenderer : ListViewRenderer
    {
        protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            base.OnElementPropertyChanged(sender, e);

            if (Control == null)
            {
                return;
            }

            if (e.PropertyName == "ItemsSource")
            {
                foreach (var cell in Control.VisibleCells)
                {
                    cell.SelectionStyle = UITableViewCellSelectionStyle.None;
                }
            }
        }
    }

对于android,您可以在values / styles.xml中添加此样式

<style name="ListViewStyle.Light" parent="android:style/Widget.ListView">
    <item name="android:listSelector">@android:color/transparent</item>
    <item name="android:cacheColorHint">@android:color/transparent</item>
  </style>

1
那如何回答这个问题?您根本没有设置颜色。尽管iOS上的原始突出显示颜色太亮,但我不想完全隐藏突出显示。
法尔科

@Falko-它设置颜色示例,在所示示例中,我将颜色设置为透明,但是您可以设置任何颜色。
Sawan Kumar Bundelkhandi

除了Barry Sohl的解决方案之外,我还必须使用此iOS自定义渲染器,以防止模板化的listview将模板中所有控件的背景色更改为绑定的背景色。但是,我也必须改变e.PropertyName == "ItemsSource",以e.PropertyName == "SelectedItem"得到它才能正常工作。
Scuzzlebutt

正如Falko所提到的那样,这并不能充分控制iOS上的颜色UITableViewCellSelectionStyle只提到了两种内置颜色,蓝色和灰色。
ToolmakerSteve

1

此解决方案可以正常工作,但是如果您将ListView的缓存策略更改为默认值,它将停止工作。如果像这样新建ListView,它会起作用: listView = new ListView() { ... }; 但是,如果这样做,它将不起作用(所选项目的背景保持灰色): listView = new ListView(cachingStrategy:ListViewCachingStrategy.RecycleElement) { ... };

以下是即使使用非标准的cachingStrategy也可以使用的解决方案。与其他解决方案相比,我更喜欢此解决方案,例如在OnItemSelected方法中添加代码,再加上ViewModel对背景色的绑定。

感谢@Lang_tu_bi_dien,他在此处发布了该想法:Listview选定项目的背景色

最终的代码如下所示:

Xamarin.Forms代码:

namespace MyProject
{
    public class ListView2 : ListView
    {
        public ListView2(ListViewCachingStrategy cachingStrategy) : base(cachingStrategy)
        {
        }
    }
}

页面上的XAML:

    <ListView2 x:Name="myListView" ListViewCachingStrategy="RecycleElement" ItemsSource="{Binding ListSource}" RowHeight="50">
        <ListView.ItemTemplate>
          <DataTemplate>
            <ViewCell>
              <ViewCell.View>
                  <Label Text="{Binding Name}" HorizontalOptions="Center" TextColor="White" />
                </ContentView>
              </ViewCell.View>
            </ViewCell>
          </DataTemplate>
        </ListView.ItemTemplate>
    </ListView2>

iOS专用渲染器:

[assembly: ExportRenderer(typeof(ListView2), typeof(ListView2Renderer))]
namespace MyProject.iOS
{
    public partial class ListView2Renderer : ListViewRenderer
    {
        protected override void OnElementChanged(ElementChangedEventArgs<ListView> e)
        {
            base.OnElementChanged(e);
            if (Control != null && e != null)
            {
                //oldDelegate = (UITableViewSource)Control.Delegate;
                Control.Delegate = new ListView2Delegate(e.NewElement);
            }
        }
    }


    class ListView2Delegate : UITableViewDelegate
    {
        private ListView _listView;

        internal ListView2Delegate(ListView listView)
        {
            _listView = listView;
        }

        public override void WillDisplay(UITableView tableView, UITableViewCell cell, Foundation.NSIndexPath indexPath)
        {
            cell.SelectedBackgroundView = new UIView()
            {
                BackgroundColor = Color.Red.ToUIColor()
            };
        }

        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                _listView = null;
            }
            base.Dispose(disposing);
        }
    }
}

注意:由于您要替换默认委托,因此可能会遇到一些问题,有关更多信息,请参见在自定义渲染器中设置控件委托会导致功能丧失。在我的项目中,如果我这样做,一切都会正常进行:

  • 对于使用默认缓存策略ListViewCachingStrategy.RetainElement的ListView,将普通ListView与该线程前面的文章中给出的ListItemViewCellRenderer代码一起使用。

  • 将此ListView2一起用于使用非默认缓存策略(即ListViewCachingStrategy.RecycleElement或ListViewCachingStrategy.RecycleElementAndDataTemplate)的ListView。

我也在向Xamarin提出功能请求,如果您认为应该将其添加到标准ListView中,请对其进行投票:ListView迫切需要SelectedItemBackgroundColor属性


1

在这里使用效果找到了这个可爱的选项。

iOS:

[assembly: ResolutionGroupName("MyEffects")]
[assembly: ExportEffect(typeof(ListViewHighlightEffect), nameof(ListViewHighlightEffect))]
namespace Effects.iOS.Effects
{
    public class ListViewHighlightEffect : PlatformEffect
    {
        protected override void OnAttached()
        {
            var listView = (UIKit.UITableView)Control;

            listView.AllowsSelection = false;
        }

        protected override void OnDetached()
        {
        }
    }
}

Android:

[assembly: ResolutionGroupName("MyEffects")]
[assembly: ExportEffect(typeof(ListViewHighlightEffect), nameof(ListViewHighlightEffect))]
namespace Effects.Droid.Effects
{
    public class ListViewHighlightEffect : PlatformEffect
    {
        protected override void OnAttached()
        {
            var listView = (Android.Widget.ListView)Control;

            listView.ChoiceMode = ChoiceMode.None;
        }

        protected override void OnDetached()
        {
        }
    }
}

形式:

ListView_Demo.Effects.Add(Effect.Resolve($"MyEffects.ListViewHighlightEffect"));

3
我认为这不能回答问题,因为您没有设置被点击项目的颜色。您只是完全避免选择。
法尔科

这不仅不能回答问题,而且使得无法选择行!
ToolmakerSteve

公平点,一定是我在这里找到了一种删除所选行颜色的方法时,已经误解了问题。如果我没记错的话,我仍然可以通过点击手势来选择一行。
保罗·查尔顿

尝试过:启用此效果后无法再选择一行。回滚更改...
thomasgalliker


-1

先前的答案要么建议使用自定义渲染器,要么要求您在数据对象中或其他位置跟踪所选项目。确实不是必需的,有一种方法可以以与ListView平台无关的方式链接到s的功能。然后可以使用它以所需的任何方式更改所选项目。可以修改颜色,根据所选状态显示或隐藏单元格的不同部分。

让我们向中添加一个IsSelected属性ViewCell。无需将其添加到数据对象。列表视图选择单元格,而不是绑定的数据。

public partial class SelectableCell : ViewCell {

  public static readonly BindableProperty IsSelectedProperty = BindableProperty.Create(nameof(IsSelected), typeof(bool), typeof(SelectableCell), false, propertyChanged: OnIsSelectedPropertyChanged);
  public bool IsSelected {
    get => (bool)GetValue(IsSelectedProperty);
    set => SetValue(IsSelectedProperty, value);
  }

  // You can omit this if you only want to use IsSelected via binding in XAML
  private static void OnIsSelectedPropertyChanged(BindableObject bindable, object oldValue, object newValue) {
    var cell = ((SelectableCell)bindable);
    // change color, visibility, whatever depending on (bool)newValue
  }

  // ...
}

为了在列表视图中的单元格和选择之间创建缺失的链接,我们需要一个转换器(最初的想法来自Xamarin论坛):

public class IsSelectedConverter : IValueConverter {
  public object Convert(object value, Type targetType, object parameter, CultureInfo culture) =>
    value != null && value == ((ViewCell)parameter).View.BindingContext;

  public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) =>
    throw new NotImplementedException();
}

我们使用此转换器连接两者:

<ListView x:Name="ListViewName">
  <ListView.ItemTemplate>
    <DataTemplate>
      <local:SelectableCell x:Name="ListViewCell"
        IsSelected="{Binding SelectedItem, Source={x:Reference ListViewName}, Converter={StaticResource IsSelectedConverter}, ConverterParameter={x:Reference ListViewCell}}" />
    </DataTemplate>
  </ListView.ItemTemplate>
</ListView>

这种相对复杂的绑定用于检查当前选择了哪个实际项目。它将SelectedItem列表视图的属性BindingContext与单元格中视图的属性进行比较。该绑定上下文是我们实际绑定到的数据对象。换句话说,它检查所指向的数据对象SelectedItem是否实际上是单元中的数据对象。如果它们相同,则我们具有选定的单元格。我们将此绑定到该IsSelected属性,然后可以在XAML中使用该属性或后面的代码以查看视图单元格是否处于选定状态。

有一个警告:如果要在页面显示时设置默认的选定项目,则需要变得更聪明。不幸的是,Xamarin Forms没有页面Displayed事件,我们只有Appearing事件,这对于设置默认值还为时过早:绑定将不被执行。因此,请稍加延迟:

protected override async void OnAppearing() {
  base.OnAppearing();

  Device.BeginInvokeOnMainThread(async () => {
    await Task.Delay(100);
    ListViewName.SelectedItem = ...;
  });
}

切勿在生产软件中使用此类代码。它可能在您的计算机上运行,​​但是只要您使用“稍稍延迟一下”,这就表明存在问题。在非常慢的诺基亚3 Android手机上,什么是100ms还不够?
thomasgalliker

此外,在ListView的名称和ItemTemplate之间具有直接引用意味着您将永远无法将DataTemplate提取到单独的文件中。如果DataTemplate变得很大,您将得到巨大的xaml文件(就像我们做过很多次一样)。
thomasgalliker

一年多以前,我已经离开Xamarin前往Flutter,再也没有回头。尽管如此,在编写此答案时,这可能是实现它的唯一方法。如果此后情况发生了变化,并且您现在知道了更好的解决方案,那么您可以进行编辑,或者更好地提供新的答案。
加博尔
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.