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

136,484

Solution 1

iOS

Solution:

Within a custom ViewCellRenderer you can set the SelectedBackgroundView. Simply create a new UIView with a background color of your choice and you're set.

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;
}

Result:

Note:

With Xamarin.Forms it seems to be important to create a new UIView rather than just setting the background color of the current one.


Android

Solution:

The solution I found on Android is a bit more complicated:

  1. Create a new drawable ViewCellBackground.xml within the Resources>drawable folder:

    <?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>
    

    It defines solid shapes with different colors for the default state and the "pressed" state of a UI element.

  2. Use a inherited class for the View of your ViewCell, e.g.:

    public class TouchableStackLayout: StackLayout
    {
    }
    
  3. Implement a custom renderer for this class setting the background resource:

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

Result:

Solution 2

In Android simply edit your styles.xml file under Resources\values adding this:

<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>

Solution 3

It looks like there is actually a cross-platform way to do this that works on both iOS and Android (not sure about Windows). It uses only binding and does not require custom renderers (which seems rare). This is a mash-up of lots of googling, so thanks to anyone who I may have borrowed from...

I am assuming ViewCells, but this should work for Text or Image cells as well. I am only including the relevant code here beyond the typical text, image, etc.

On your page do something like this:

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

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

Your custom Model might look something like this:

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 ); 
        }
    }
}

Then for your ItemTemplate you need a custom cell class something like this:

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

        View = layout;
    }
}

Then in your ItemSelected event handler, do the following. Note that 'selected' is an instance of MyModel used to track the currently selected item. I am only showing background color here, but I also use this technique to reverse highlight the text and detail text colors.

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 );
}

Solution 4

To change color of selected ViewCell, there is a simple process without using custom renderer. Make Tapped event of your ViewCell as below

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

In your ContentPage or .cs file, implement the event

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;
    }
} 

Declare lastCell at the top of your ContentPage like this ViewCell lastCell;

Solution 5

Only for Android

Add in your custom theme or your default theme under ProjectName.Android/Resources/values/styles.xml

<item name="android:colorActivatedHighlight">@android:color/transparent</item>
Share:
136,484

Related videos on Youtube

Falko
Author by

Falko

http://www.falkoschindler.de/

Updated on February 21, 2022

Comments

  • Falko
    Falko about 2 years

    Using Xamarin.Forms, how can I define the highlight/background color of a selected/tapped ListView item?

    (My list has a black background and white text color, so the default highlight color on iOS is too bright. In contrast, on Android there is no highlighting at all - up to a subtle horizontal gray line.)

    Example: (left: iOS, right: Android; while pressing "Barn2")

  • Sten Petrov
    Sten Petrov over 9 years
    If setting the background color doesn't work for the existing UIView try calling the cell's SetNeedsLayout (or whatever that method was called) after setting its background color. UITableView tries to not perform layout whenever that's possible
  • Falko
    Falko over 9 years
    @StenPetrov: Nope, SetNeedsDisplay or ``` SetNeedsLayout``` does not work. But it doesn't really matter, since assigning new UIView {...} is a pretty short workaround.
  • Unsliced
    Unsliced over 9 years
    This works - at least a little bit. I'm finding that when an item is selected, this works, but when I then select another, the first goes back to the 'old' background - unless I scroll to hide it, then scroll again to reveal it, in which case the 'new' colour is used in the redraw.
  • SpaceMonkey
    SpaceMonkey about 9 years
    omg, thank you! I thought of that myself, but when I tried it didn't work. I'm not sure what I did wrong, but copying your code worked.
  • animekun
    animekun about 9 years
    it is NOT working, you just draw hover the row, old color is still there, and you can still see 1 pixel row of that color at the bottom
  • Rohit Vipin Mathews
    Rohit Vipin Mathews almost 9 years
    @Greag.Deay - ListView.SeparatorVisibility = SeparatorVisibility.None; ListView.SeparatorColor= Color.Transparent; should solve the problem you mentioned.
  • Falko
    Falko about 8 years
    How does that answer the question? You're not setting a color at all. Although the original highlight color on iOS is too bright, I didn't want to hide the highlighting completely.
  • Sawan Kumar Bundelkhandi
    Sawan Kumar Bundelkhandi about 8 years
    @Falko - Its setting the colour example, In the example shown I have set to transparent but you can set any colour of your choice.
  • Edgar
    Edgar about 8 years
    And how to change the font color?
  • Scuzzlebutt
    Scuzzlebutt about 7 years
    This method did work fine on my UWP project along with Android and iOS. Kick ass!
  • Ateik
    Ateik about 7 years
    the issue is only happening on Android, so this is the best/cleanest solution for me
  • Scuzzlebutt
    Scuzzlebutt about 7 years
    I had to use this iOS custom renderer in addition to Barry Sohl's solution to prevent my templated listview from changing the background color of all controls in the template to the binded background color. However, I did have to change e.PropertyName == "ItemsSource" to e.PropertyName == "SelectedItem" to get it to work properly.
  • wislon
    wislon about 7 years
    Thanks a bunch, this saved me hours! I can confirm it works for ViewCells (which is what I needed)
  • masterwok
    masterwok about 7 years
    On Android the selected color isn't retained for me. I've tried using android:state_selected.. :[
  • Filipe Silva
    Filipe Silva about 7 years
    Did anyone get this to work while setting a default value for the ListView's SelectedItem?
  • Marin Shalamanov
    Marin Shalamanov almost 7 years
    Easiest solution for me too. It's such a pity Xamarin Forms don't have a customizable property for that.
  • mr5
    mr5 almost 7 years
    Will this also for for contextual action menus? Such as long click on android and swipe left on iOS?
  • ToolmakerSteve
    ToolmakerSteve over 6 years
    As Falko mentioned, this doesn't give enough control over color on iOS. UITableViewCellSelectionStyle only mentions two built-in colors, blue and gray.
  • ToolmakerSteve
    ToolmakerSteve over 6 years
    How does this help "set the highlight/background color of a tapped item"? Looks like what you've done is suppress any color change.
  • zafar
    zafar over 6 years
    It does not change the background color by any means. But helpful when user wants to suppress the item selection. This is usually helpful in the scenarios when user wants to launch the detail view when an item in the ListView is tapped but wants to suppress the selected item.
  • Pxaml
    Pxaml over 6 years
    will this solution work ,if the main project is on pcl ?
  • Falko
    Falko over 6 years
    I think this does not answer the question, since you're not setting the color of the tapped item. You're just avoiding the selection altogether.
  • Marek
    Marek about 6 years
    If you are following MVVM pattern this is actually not a best approach. Color is not really related with the model itself - it's a strictly UI matter and framework dependent (Forms in this case). Setting i.e. IsSelected flag in model with converter or style trigger in XAML would be more portable solution.
  • wislon
    wislon about 6 years
    this works great for ListViews using the RetainElement caching strategy, but for those using the RecycleElement or RecycleElementAndDataTemplate strategy, the IsSelected property never changes - But we do get 'BindingContext' and 'Index' property changes. I'm still trying to figure out if I can use those somehow instead. So close! :)
  • Prince
    Prince about 6 years
    I am unable to call list.First(...) .. It says IEnumerable does not contain a definition for First...
  • Adam
    Adam about 6 years
    @Prince - you will need to add using System.Linq; at the top.
  • Adam
    Adam about 6 years
    If you have using System.Linq, then an IEnumerable certainly has the extension .First(). If you can't resolve this, ask another question on StackOverflow to debug that particular issue.
  • ToolmakerSteve
    ToolmakerSteve about 6 years
    Not only does this not answer the question, this makes it impossible to select a row!
  • Paul Charlton
    Paul Charlton about 6 years
    Fair point, must have misread the question as I found my way here looking for a way to remove the selected row colour. I do still manage to select a row, through tap gestures if I remember correctly.
  • Tiago_nes
    Tiago_nes almost 6 years
    does not work 100% because at the first selection i still see the default blue for half a second, but it is good enough.
  • Morse
    Morse over 5 years
    Since the selected item gets changed it triggers ItemSelected event again, so in my case the further code ran into exception.
  • user875234
    user875234 over 5 years
    That's a great solution. I'm not criticizing your solution. But that is too much code to add to change the color of a list item. If you have 10 little fixes like this your code just becomes a mess.
  • Denis Vitez
    Denis Vitez over 5 years
    I created the Style.xml since there was none in my Android project, but this is not working for me. Do I need to add something else? Maybe a special theme? I'm using Xamarin.Forms.
  • Inrego
    Inrego about 5 years
    Anyone has an idea as to why the SelectedBackgroundColor is never set, when using it in an Implicit style?
  • Extragorey
    Extragorey about 5 years
    How do you tell CustomTextCell to use CustomTextCellRenderer? Is there some attribute I'm missing?
  • zafar
    zafar over 4 years
    Also this approach will not be able to address the color change for long press, in the scenarios where you have Context Actions defined on the ListView's ViewCell
  • thomasgalliker
    thomasgalliker over 4 years
    Tried it: Can no longer select a row when this effect is active. Rollback changes...
  • thomasgalliker
    thomasgalliker over 4 years
    I would definitetly not handle the BackgroundColor in the viewmodel (MyModel in your case) as this is against the idea of MVVM. The viewmodel is here to prepare data which is then visualized by the view. If we start to mix these concepts,we will be soon back to Winforms times... happy coding then.
  • thomasgalliker
    thomasgalliker over 4 years
    @Extragorey, you're probably missing the ExportRenderer call: [assembly: ExportRenderer(typeof(ExtendedViewCell), typeof(ExtendedViewCellRenderer))]
  • a.tarasevich
    a.tarasevich over 4 years
    Best solution! Thanks!
  • Nk54
    Nk54 about 4 years
    I was using this technique but i have to say that like @FilipeSilva said, it doesn't work if the selected item is initialized in view model. Only work with user interaction. I've tried working around but didn't find a fix.
  • Vikas Lalwani
    Vikas Lalwani about 4 years
    This solution should have maximum votes and must be on top, as this is the easiest one.
  • Tornike Gomareli
    Tornike Gomareli almost 4 years
    The best solution, easiest and most beautiful. Should be on the top
  • Luis Lema
    Luis Lema almost 4 years
    It works only when you select an item from the menu. It doesn' work the first time the menu is shown.
  • thomasgalliker
    thomasgalliker almost 4 years
    Never use such code in production software. It may works on your machine, but whenever you use "some delay to wait a bit" this is an indicator for problems. What is 100ms is not enough on a very slow Nokia 3 Android phone?
  • thomasgalliker
    thomasgalliker almost 4 years
    Furthermore, having a direct reference between the ListView's Name and the ItemTemplate means that you'll never be able to extract the DataTemplate to a separate file. If the DataTemplate grows large, you gonna end up with huge xaml files (as we did many times).
  • Gábor
    Gábor almost 4 years
    I've left Xamarin for Flutter more than a year ago and never looked back. Still, at the time this answer was written, this probably was the only way to accomplish it. If things have changed since and you know a better solution now, then you could edit, or better yet, provide a new answer.
  • tuke307
    tuke307 over 3 years
    look here for more information
  • cineam mispelt
    cineam mispelt about 3 years
    Sadly not working for me. The color in the question is when the item is "tapped", which i believe is different to after the tap when it then goes to "selected". This solution works for me on the selected item (after the tap) but "during" the tap i get the same faded pink as in the image in the question.
  • Softlion
    Softlion about 3 years
    SelectedBackgroundView is for selection, not highlightning. Question is about highlightning