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:
Create a new drawable
ViewCellBackground.xml
within theResources
>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.
Use a inherited class for the
View
of yourViewCell
, e.g.:public class TouchableStackLayout: StackLayout
{
}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 :
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>
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 typeColor
- Store your
BoundObject
in aList<BoundObject>
- When populating your list for the first time, set the
TextColor
property in yourBoundObject
- In the
ItemSelected
event for your list, get the current selection, and update theList<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 BackgroundColor
s 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
How to Retrieve the Android Sdk Version
Extending Application to Share Variables Globally
Bitmap Recycle with Largeheap Enabled
Updating Progress Dialog in Activity from Asynctask
Android - Out of Memory Exception When Creating Bitmap
Marking Sms Messages as Read/Unread or Deleting Messages Not Working in Kitkat
How to Install Android Sdk Build Tools on the Command Line
How to Make Sticky Headers in Recyclerview? (Without External Lib)
Background Listview Becomes Black When Scrolling
Scale Image to Fill Imageview Width and Keep Aspect Ratio
How to Create a Http Server in Android
Android Failed to Load Js Bundle
Android: How to Create a Dialog Without a Title
Recyclerview - How to Smooth Scroll to Top of Item on a Certain Position
Android Fragment Lifecycle Over Orientation Changes
Read the Package Name of an Android APK
Error: "Adb Connection Error:An Existing Connection Was Forcibly Closed by the Remote Host"