Xamarin.Forms Listview: Set the Highlight Color of a Tapped Item

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

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:

Change Background Color of ListView Selected Item in Xamarin

You can bind BackgroundColor for ContentView of ViewCell , then use ViewModel and ItemTapped method of ListView to modify the selected item background color .

For example , the xaml code as follow:

<ListView x:Name="ListViewMenu"
HasUnevenRows="True" ItemTapped="ListViewMenu_ItemTapped">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell >
<Grid Padding="10"
BackgroundColor="{Binding SelectedBackgroundColor}">
<Label Text="{Binding Title}" d:Text="{Binding .}" FontSize="20"/>
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>

Then in HomeMenuItem model add SelectedBackgroundColor property :

public enum MenuItemType
{
Browse,
About
}
public class HomeMenuItem : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public MenuItemType Id { get; set; }

public string Title { get; set; }

private Color selectedBackgroundColor;
public Color SelectedBackgroundColor
{
set
{
if (selectedBackgroundColor != value)
{
selectedBackgroundColor = value;
OnPropertyChanged("SelectedBackgroundColor");
}
}
get
{
return selectedBackgroundColor;
}
}

protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}

}

Then in MenuPage modify ItemSource as follow:

public partial class MenuPage : ContentPage
{
MainPage RootPage { get => Application.Current.MainPage as MainPage; }
List<HomeMenuItem> menuItems;
List<HomeMenuItem> tmpItems; // add a tmp list to remove setted backgroud color
public MenuPage()
{
InitializeComponent();

tmpItems = new List<HomeMenuItem>();

menuItems = new List<HomeMenuItem>
{
new HomeMenuItem {Id = MenuItemType.Browse, Title="Browse" },
new HomeMenuItem {Id = MenuItemType.About, Title="About" }
};

menuItems[0].SelectedBackgroundColor = Color.Red; // default set the first item be selected, you can modify as your wants
tmpItems.Add(menuItems[0]); // add the selected item (default is the first)

ListViewMenu.ItemsSource = menuItems;

ListViewMenu.SelectedItem = menuItems[0];
ListViewMenu.ItemSelected += async (sender, e) =>
{
if (e.SelectedItem == null)
return;

var id = (int)((HomeMenuItem)e.SelectedItem).Id;
await RootPage.NavigateFromMenu(id);
};
}

private void ListViewMenu_ItemTapped(object sender, ItemTappedEventArgs e)
{
menuItems[e.ItemIndex].SelectedBackgroundColor = Color.Red;
tmpItems[0].SelectedBackgroundColor = Color.Transparent;
tmpItems[0] = menuItems[e.ItemIndex];
}
}

The effect :

Sample Image
Sample Image

ListView Highlight Selected Item Color is not staying on when selected other buttons

According to your description, I suggest you can use CollectionView to replace ListView. Then using Xamarin.Forms Visual State Manager to highlight selecteditem backgroundcolor.

This is VisualStateManager in ContentPage.Resource.

 <ContentPage.Resources>

<Style x:Key="stacklayoutStyle" TargetType="StackLayout">
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />

<VisualState x:Name="Selected">
<VisualState.Setters>
<Setter Property="BackgroundColor" Value="LightGray" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
</ContentPage.Resources>

Using this style in CollectionView.The selected state will stay on when you click Button.

 <CollectionView ItemsSource="{Binding items}" SelectionMode="Single">
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout Style="{StaticResource stacklayoutStyle}">
<Grid Margin="4,0,4,0" Padding="0,0,8,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Label
Grid.Column="1"
FontSize="Medium"
Text="{Binding ItemName}" />
<Button Grid.Column="2" Text="decrement" />
<Entry
Grid.Column="3"
Text="{Binding ItemQuantity}"
VerticalTextAlignment="Center" />
<Button Grid.Column="4" Text="Increment" />
</Grid>

</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>

</CollectionView>

How to change background color of listview selected item in Xamarin Forms Cross Platform without a custom renderer

The below code works perfectly for me. However, it iterates the entire ItemsSource for resetting the background of the previously selected item. You can store it and reset it if you wish to optimize it. Hope this helps.

<ListView x:Name="contactList" ItemsSource="{Binding PlatformsList}" ItemTapped="contactList_ItemTapped"
VerticalOptions="CenterAndExpand" HorizontalOptions="FillAndExpand">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Label TextColor="Black" Margin="10,0" Text="{Binding PlatformName}" BackgroundColor="{Binding Background}" VerticalOptions="Center" />
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>

private void contactList_ItemTapped(object sender, ItemTappedEventArgs e)
{
var selectedItem = e.Item as PlatformInfo;
selectedItem.ItemBackground = Color.Aqua;

foreach(var item in this.contactList.ItemsSource)
{
if (item != selectedItem)
(item as PlatformInfo).ItemBackground = Color.Transparent;
}
}

Highlighting of selected item of ListView if background color of stack layout is set

You can use the Grid or Frame that wrap a view with a border(Stacklayout) to do this.

 <ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid Padding="5" BackgroundColor="Transparent">
<StackLayout BackgroundColor="AliceBlue">
<Label
FontSize="Medium"
Text="{Binding FullName}"
TextColor="Orange" />

</StackLayout>
</Grid>

</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>

<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Frame Padding="5" BackgroundColor="Transparent">
<StackLayout BackgroundColor="AliceBlue">
<Label
FontSize="Medium"
Text="{Binding FullName}"
TextColor="Orange" />

</StackLayout>
</Frame>

</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>

Sample Image

Xamarin Forms: Set Background Color of Item Selected in ListView from ViewModel

I did come up with a solution which I am happy with and requires no Renderer. In my custom ViewCell I have added the SelectedBackgroundColor BindableProperty as suggested.

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

I then use a custom Converter. This is actually a generic Converter that is used elsewhere for setting values based on a true/false bound value.

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

In Xaml I define the Converter and set my True/False values to the required background colors:

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

Then assign the Converter to the custom ViewCell. In the custom ViewCell the SelectedBackgroundColor was set using the Converter. As a note The SymbolViewCell already existed to solve a different issue with an image that was part of the item refreshing correctly

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

IsChecked is a property on the Item of the ItemsDataSource. The ListView already used a a collection of Item objects and this object already had an IsChecked property.

Stripping down the Item object to the bare minimum (BindableBase implements the IPropertyChanged interface):

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

This solution would not work if the ItemsDataSource was a collection of string objects because it does require the additional property and you would need a bound SelectedItem property as a place to trigger the change in IsChecked property. But one could make a simple object with a name and IsChecked property to bind. Personally I think this added code is a lot simpler than writing a Renderer to handle things.

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

Highlight the list-view selected item programatically in xamarin.forms

Solution:

You can bind your Cell's background color with the property in your model. When the user clicked on that notification, change the background color of the specific cell in model and then it will change in listview:

    private void ClickNotification(object sender, EventArgs e)
{
var index = _myViewModel.Users.IndexOf(selectedUser);

//change the background color of the specific cell in model
myModel model = _myModelList[index];
model.myBackColor = Color.Blue;
}

In the model:

class myModel : INotifyPropertyChanged
{
Color backColor;

public event PropertyChangedEventHandler PropertyChanged;

public myModel( Color myColor)
{
backColor = myColor;
}

public Color myBackColor
{
set
{
if (backColor != value)
{
backColor = value;

if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("myBackColor"));
}
}
}
get
{
return backColor;
}
}
}

And in the xaml:

<ListView x:Name="myListView">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Orientation="Horizontal" BackgroundColor="{Binding myBackColor}">

</StackLayout>

</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>

Xamarin forms Listview selected Item fore color

You can do this (Without a custom renderer) by adding another property to the object is bound to, and binding TextColor on the label to this new property.

Assuming your bound object looks something like this

public class BoundObject
{
public string AttributeName { get; set; }
public string Description { get; set; }
public string CreditorName { get; set; }

public int id { get; set; }
public Color TextColor { get; set; }
}

XAML

Note the ListView control added, with a name property and an ItemSelected event.

<ListView x:Name="myList" ItemSelected="myListSelected">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout VerticalOptions="StartAndExpand">
<Label Text="{Binding AttributeName}"
FontSize="Small"
FontAttributes="Bold"
TextColor="{Binding TextColor}"
/>

<Label Text="{Binding Description}"
FontSize="Small"
TextColor="{Binding TextColor}"
/>

<Label Text="{Binding CreditorName}"
FontSize="Small"
TextColor="{Binding TextColor}"
/>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>

Code Behind

Most of the magic happens in the code behind. Note that I'm just adding a few items to the list on start here - just for debug purposes. It's important to note that the start color is also given at the time the list needs to be created.

I've also added an ID field to the BoundObject, so we can more easily identify which object we have selected.

    List<BoundObject> listItems = new List<BoundObject>();

public YourPage()
{
InitializeComponent();

for (int i = 0; i < 10; i++)
{
listItems.Add(new BoundObject() { id=i, AttributeName = "Attribute " + i, Description = i + " description", CreditorName = "Creditor: " + i, TextColor = Color.Blue });
}

myList.ItemsSource = listItems;
}

private void myListSelected(object sender, SelectedItemChangedEventArgs e)
{
if (((ListView)sender).SelectedItem == null)
return;

//Get the item we have tapped on in the list. Because our ItemsSource is bound to a list of BoundObject, this is possible.
var selection = (BoundObject)e.SelectedItem;

//Loop through our List<BoundObject> - if the item is our selected item (checking on ID) - change the color. Else - set it back to blue
foreach(var item in listItems)
{
if (item.id == selection.id)
item.TextColor = Color.Red;
else
item.TextColor = Color.Blue;
}

//ItemsSource must be set to null before it is re-assigned, otherwise it will not re-generate with the updated values.
myList.ItemsSource = null;
myList.ItemsSource = listItems;
}

The key points to the code-behind are...

  • New property TextColor on your bound object, of type Color
  • Store your BoundObject in a List<BoundObject>
  • When populating your list for the first time, set the TextColor property in your BoundObject
  • In the ItemSelected event for your list, get the current selection, and update the List<BoundObject> setting the colours as your conditions need
  • Set the list ItemSource to null, and re-assign it to the (now updated) List<BoundObject>

Change background color selecteditem Listview

Edit 2:

Sometime if I have run into strange issues where my ViewCell's BackgroundColor never changes back to the original color so I have started doing this to change the color instead:

cell.Tapped += async (sender, args) => {
cell.View.BackgroundColor = Color.Red;

#pragma warning disable 4014 //These pragma's are only needed if your Tapped is being assigned an async anonymous function and muffles the compiler warning that you did not await Task.Run() which you do not want to fire and forget it

Task.Run(async () => { //Change the background color back after a small delay, no matter what happens
await Task.Delay(300); //Or how ever long to wait

Device.BeginInvokeOnMainThread(() => cell.View.BackgroundColor = Color.Default); //Turn it back to the default color after your event code is done
});

#pragma warning restore 4014

await OnListViewTextCellTapped(cell); //Run your actual `Tapped` event code

};

Edit:

To add the below code to a ListView.DataTemplate, you would want to do something like this:

ListView listView = new ListView {
SeparatorColor = Color.Green,
ItemsSource = ListlvData
};

listView.ItemTemplate = new DataTemplate(() => {
ViewCell cell = new ViewCell();

cell.Tapped += (sender, args) => {
cell.View.BackgroundColor = Color.Red;
OnListViewTextCellTapped(cell); //Run your actual `Tapped` event code
cell.View.BackgroundColor = Color.Default; //Turn it back to the default color after your event code is done
};

cell.View = new Image();

return cell;
});

To change the background color on Tapped you will need to use a ViewCell and an Image control within that since ImageCell does not support BackgroundColors by default.

I put a StackLayout within the ViewCell but then on the Tapped event, I change the ViewCell.View's BackgroundColor, like so:

ViewCell cell = new ViewCell();

cell.Tapped += (sender, args) => {
cell.View.BackgroundColor = Color.Red;
OnListViewTextCellTapped(cell); //Run your actual `Tapped` event code
cell.View.BackgroundColor = Color.Default; //Turn it back to the default color after your event code is done
};


Related Topics



Leave a reply



Submit