Change Background Color of ListView Selected Item in Xamarin - asp.net

I created a Master-Detail type project in Xamarin. When I selected an item from the Master page the background color is orange by default. How can I change this to a color of my choosing?

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 :

This problem is specific to Android. In the Android project add this line to Resources\values\styles.xml inside the <style> tag:
<item name="android:colorActivatedHighlight">#00FFFFFF</item>

Related

How to access the BindingContext of custom control in Xamarin.Forms

I have a CollectionView with ItemsSource set to ObservableCollection of type Employee.
The ItemTemplate of the CollectionView is a CustomControl that has 1 BindableProperty of Type Employee
MainPage.xaml:
<CollectionView ItemsSource="{Binding Employees}"
SelectedItem="{Binding SelectedEmployee}">
<CollectionView.ItemTemplate>
<DataTemplate>
<controls:CustomControl Employee="{Binding .}" />
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
The CustomControl has an image (checked image to indicate selection).
CustomControl.xaml:
<Frame HasShadow="True"
BackgroundColor="Blue">
<StackLayout Orientation="Horizontal">
<Label Text="{Binding Name}" />
<Image Source="check.png" />
</StackLayout>
</Frame>
CustomControl.xaml.cs:
public partial class CustomControl : ContentView
{
public CustomControl()
{
InitializeComponent();
}
public static BindableProperty EmployeeProperty = BindableProperty.Create(
propertyName: nameof(Employee),
returnType: typeof(Employee),
declaringType: typeof(CustomControl),
defaultValue: default(Employee),
defaultBindingMode: BindingMode.OneWay);
public Employee Employee
{
get
{
return (Employee)GetValue(EmployeeProperty);
}
set
{
SetValue(EmployeeProperty, value);
}
}
}
Model (Employee):
public class Employee: INotifyPropertyChanged
{
private int name;
public int Name
{
get
{
return name;
}
set
{
name = value;
OnPropertyChanged(nameof(Name));
}
}
private int isSelected;
public int IsSelected
{
get
{
return isSelected;
}
set
{
isSelected = value;
OnPropertyChanged(nameof(IsSelected));
}
}
#region PropertyChanged
public void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
#endregion
}
I am trying to create simple animation (FadeIn/FadeOut) for the checked image in the CustomControl so when an item is selected the image will fade in, and when unselected it will fade out. I could use IsVisible and set it to true/false but that's ugly.
My idea was to listen to PropertyChanged event of the Employee (which supposed to be the context of my CustomControl), and when the property IsSelected is modified, I will start the animation to show/hide the image. something like this
public CustomControl()
{
InitializeComponent();
(this.BindingContext as Employee).PropertyChanged += CustomControl_PropertyChanged;
}
private void CustomControl_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(Employee.IsSelected))
{
//do animation to show/hide image
}
}
But couldn't access the Context of my CustomControl!
When I declare the binding in MainPage.xaml I am passing a single Emplyee objet as BindingContext (that dot, right?):
<controls:CustomControl Employee="{Binding .}" />
but after the CustomControl is initializd, the BindingContext is still null!
public CustomControl()
{
InitializeComponent();
var context = this.BindingContext; //this is null
}
How can I observe the changes on the IsSelected property of the Employee object from my CustomControl?
In your custom control override the OnBindingContextChanged method, inside of that method you should be able to access the binding context that is set for your view.
Ex:
protected override void OnBindingContextChanged()
{
base.OnBindingContextChanged();
var context = this.BindingContext as Employee
}

Problem changing background color in Xamarin form from View model

Is there something different about changing the text value of an Entry, and the background color of an entry?
Using the code here...https://github.com/jamesmontemagno/app-compass. I can add an entry
<Entry Placeholder="Degree to follow:"
Text="{Binding Degree}" x:Name="Degree"
Keyboard="Numeric"
Grid.Row="0" BackgroundColor="Green"/>
In the ViewModel define a property
public string _degree;
public string Degree
{
get => _degree;
set => SetProperty(ref _degree, value);
}
I can change the Entry from the View Model..."_degree = "3333";"
I can get it's value..."HeadingDisplay = $"Heading: {Heading.ToString()}" + " Degree: " + _degree;"
Why then, can I not change the background color of one of the entry, label, or page the same way?
I added the binding here...
<Label Grid.Row="2"
Text="{Binding HeadingDisplay}"
x:Name="LabelInfo"
TextColor="{Binding BGColor, Mode=OneWay}"
VerticalOptions="Center"
HorizontalOptions="Center" />
And then tried to change it in the ViewModel, I have tried a few different things...
public Color BGColor { get; set; }
//public Color BgColor
//{
// get => _bgcolor;
// set => SetProperty(ref _bgcolor, value);
//}
//_bgcolor = "#00FF00;
//_bgcolor = Xamarin.Forms.Color.Red;
BGColor = Color.Red;
The background color never changes. Do I need a converter, does the page need to be re-displayed, or what?
As per comments below, I made some changes, I am now binding the label background, instead of the text background
<Label Grid.Row="2"
Text="{Binding HeadingDisplay}"
x:Name="LabelInfo" BackgroundColor="{Binding=BGCOLOR Mode=OneWay}"
VerticalOptions="Center"
HorizontalOptions="Center" />
In the view model, this is the code now
public Color _bgcolor;
public Color BGCOLOR
{
get => _bgcolor;
set => SetProperty(ref _bgcolor, value);
}
//set the color here
_bgcolor = Color.Red;
Still no color change. I am unclear on the comment about two color types. Can you point me to link, or give example? I have also tried sending FromHex.
Here is a sample how to modify Color and Text of Control , you can refer to check where problem is .
Create a ViewModel class :
public class ViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private Color color { set; get; }
public Color Color
{
set
{
if (color != value)
{
color = value;
OnPropertyChanged("Color");
}
}
get
{
return color;
}
}
private string textValue { set; get; }
public string TextValue
{
set
{
if (textValue != value)
{
textValue = value;
OnPropertyChanged("TextValue");
}
}
get { return textValue; }
}
public ViewModel()
{
color = Color.Red;
textValue = "Default Text";
}
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
The Xaml code :
<StackLayout>
<!-- Place new controls here -->
<Label Text="{Binding TextValue}" TextColor="{Binding Color}"
HorizontalOptions="Center"/>
<Entry Text="{Binding TextValue}" TextColor="{Binding Color}"/>
<Button Text="Change" Clicked="Button_Clicked"/>
</StackLayout>
In ContenPage can modify the value as follow :
public partial class MainPage : ContentPage
{
ViewModel viewModel;
public MainPage()
{
InitializeComponent();
viewModel = new ViewModel();
BindingContext = viewModel;
}
private void Button_Clicked(object sender, EventArgs e)
{
viewModel.TextValue = "New Value";
viewModel.Color = Color.Green;
}
}
The effect :

How to implement checkbox in listview xamarin form

I am trying to implement checkbox in listview and retrieve the data from the selected rows. I tried searching online, but i am not finding any proper resource and some plugins are already not working anymore. Any reference and approach from anyone will be very kind.
You can use MVVM to achieve it. Here is running GIF.
First of all, you can create a Model. Add your need binding properties.
public class MyModel:BaseViewModel
{
public bool IsChecked { get; set; }
public string Title { get; set; }
}
public class BaseViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
var changed = PropertyChanged;
if (changed != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Then create a Viewmodel to pop data.
public class MyViewModel
{
public ObservableCollection<MyModel> myModels { get; set; }
public MyViewModel()
{
myModels =new ObservableCollection<MyModel>();
myModels.Add(new MyModel() { IsChecked = false, Title = "test1" });
myModels.Add(new MyModel() { IsChecked = false, Title = "test2" });
myModels.Add(new MyModel() { IsChecked = false, Title = "test3" });
myModels.Add(new MyModel() { IsChecked = false, Title = "test4" });
myModels.Add(new MyModel() { IsChecked = false, Title = "test5" });
}
}
Then Create a layout to display the data.
<ListView x:Name="listView" ItemsSource="{Binding myModels}" ItemSelected="listView_ItemSelected">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout BackgroundColor="#eee"
Orientation="Vertical">
<StackLayout Orientation="Horizontal">
<CheckBox IsChecked="{Binding IsChecked}"></CheckBox>
<Label Text="{Binding Title}" TextColor="#f35e20" />
</StackLayout>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Here is background code for the layout, then retrieve the data from the selected rows and make a bindingContext.
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
this.BindingContext = new MyViewModel();
}
private void listView_ItemSelected(object sender, SelectedItemChangedEventArgs e)
{
var myModel=e.SelectedItem as MyModel;
DisplayAlert("title",myModel.Title+" "+myModel.IsChecked,"OK");
}
}

Display Datagrid based on Binding item count in Xamarin Forms

I am currently using Syncfusion Sf Datagrid. I want to display the Datagrids based on count. For example I have button A and button B, if user click on A, it will display 1 datagrid. If user click on B, it will display 2 datagrid. FYI, the datagrid will display on the same page.
What my code does now is displaying all datagrid. I would like to display the datagrid only based on condition (in this case which is A or B). I am not sure how to achieve this because the datagrid is in xaml, how do I make sure the correct count of datagrid I want to appear? Let's say if I want to have more category that has different items, how do I display the correct number of datagrid according to the items inside a category?
MainPage.xaml
<ContentPage.Content>
<StackLayout>
<Button Text="A"/>
<Button Text="B"/>
<sfgrid:SfDataGrid ItemsSource="{Binding fruitA}"/>
<sfgrid:SfDataGrid ItemsSource="{Binding vegeB1}"/>
<sfgrid:SfDataGrid ItemsSource="{Binding vegeB2}"/>
</StackLayout>
</ContentPage.Content>
MainPage.cs
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
this.BindingContext = new ViewModel();
}
}
ViewModel
public class ViewModel
{
public ObservableCollection<Test> fruitA { get; set; }
public ObservableCollection<Test> vegeB1 { get; set; }
public ObservableCollection<Test> vegeB2 { get; set; }
public ViewModel()
{
fruitA = new ObservableCollection<ChildPart>();
fruitA.Add(new Test() { Title = "Orange", Value = "1.20" });
fruitA.Add(new Test() { Title = "Banana", Value = "1.40" });
fruitA.Add(new Test() { Title = "Apple", Value = "1.30" });
vegeB1 = new ObservableCollection<ChildPart>();
vegeB1.Add(new Test() { Title = "Spinach", Value = "1.20" });
vegeB1.Add(new Test() { Title = "Cabbage", Value = "1.40" });
vegeB2 = new ObservableCollection<ChildPart>();
vegeB2.Add(new Test() { Title = "Lettuce", Value = "1.20" });
vegeB2.Add(new Test() { Title = "Broccoli", Value = "1.40" });
vegeB2.Add(new Test() { Title = "Celery", Value = "1.30" });
}
}
Test
public class Test
{
public string Title { get; set; }
public string Value { get; set; }
}
There is a way to use Bindable layouts to achieve that , you can check whether is your want .
Modify MainPage.xaml as follow (Add ScrollView if count of items is too much):
<StackLayout>
<Button Text="A" Clicked="Button_Clicked_A"/>
<Button Text="B" Clicked="Button_Clicked_B"/>
<Button Text="C" Clicked="Button_Clicked_C"/>
<ScrollView>
<StackLayout x:Name="MyStackLayout" Orientation="Vertical">
<BindableLayout.ItemTemplate>
<DataTemplate>
<sfgrid:SfDataGrid ItemsSource="{Binding }" />
</DataTemplate>
</BindableLayout.ItemTemplate>
</StackLayout>
</ScrollView>
</StackLayout>
Modiy MainPage.cs asd follow :
public partial class MainPage : ContentPage
{
ViewModel viewModel;
public MainPage()
{
InitializeComponent();
viewModel = new ViewModel();
}
private void Button_Clicked_A(object sender, EventArgs e)
{
List<object> list = new List<object>();
list.Add(viewModel.fruitA);
BindableLayout.SetItemsSource(MyStackLayout, list);
}
private void Button_Clicked_B(object sender, EventArgs e)
{
List<object> list = new List<object>();
list.Add(viewModel.vegeB1);
list.Add(viewModel.vegeB2);
BindableLayout.SetItemsSource(MyStackLayout, list);
}
private void Button_Clicked_C(object sender, EventArgs e)
{
List<object> list = new List<object>();
list.Add(viewModel.fruitA);
list.Add(viewModel.vegeB1);
list.Add(viewModel.vegeB2);
BindableLayout.SetItemsSource(MyStackLayout, list);
}
}
After running , the effect as follow :
You can refer the following updated code snippet to achieve your requirement “Based on the button click I have to show respective datagrid”.
Code snippet:
<StackLayout>
<Button x:Name="A" Text="A" Clicked="A_Clicked" />
<Button x:Name="B1" Text="B1" Clicked="B1_Clicked" />
<Button x:Name="B2" Text="B2" Clicked="B2_Clicked" />
<sfgrid:SfDataGrid x:Name="fruitA" IsVisible="False" ItemsSource="{Binding fruitA}"/>
<sfgrid:SfDataGrid x:Name="vegeB1" IsVisible="False" ItemsSource="{Binding vegeB1}"/>
<sfgrid:SfDataGrid x:Name="vegeB2" IsVisible="False" ItemsSource="{Binding vegeB2}"/>
</StackLayout>
private void A_Clicked(object sender, EventArgs e)
{
//// Displays only fruit datagrid.
fruitA.IsVisible = true;
vegeB1.IsVisible = false;
vegeB2.IsVisible = false;
}
private void B1_Clicked(object sender, EventArgs e)
{
//// Displays only vegeB1 datagrid.
fruitA.IsVisible = false;
vegeB1.IsVisible = true;
vegeB2.IsVisible = false;
}
private void B2_Clicked(object sender, EventArgs e)
{
//// Displays only vegeB2 datagrid.
fruitA.IsVisible = false;
vegeB1.IsVisible = false;
vegeB2.IsVisible = true;
}
Sample link
Regards,
Pradeep Kumar

Xamarin.Forms Change the Orange ListView Backgroud-Color on tappping cell to white or transparent

I cant find solution about this orange color? Do i need to write a renderer or can change from resources in Android and IOS?
Yes,if you want to change ListView selecteditem background color, need to use custom render to do this in Xamarin.Forms.
In the PCL, create a class name is ExtendedViewCell which should inherit any ViewCell.
public class ExtendedViewCell : ViewCell
{
public static readonly BindableProperty SelectedBackgroundColorProperty =
BindableProperty.Create("SelectedBackgroundColor",
typeof(Color),
typeof(ExtendedViewCell),
Color.Default);
public Color SelectedBackgroundColor
{
get { return (Color)GetValue(SelectedBackgroundColorProperty); }
set { SetValue(SelectedBackgroundColorProperty, value); }
}
}
In Android project, create a class name as ExtendedViewCellRenderer and make sure to add renderer registration for our ExtendedViewCell class above the namespace.
[assembly: ExportRenderer(typeof(ExtendedViewCell), typeof(ExtendedViewCellRenderer))]
namespace demo3.Droid
{
public class ExtendedViewCellRenderer : ViewCellRenderer
{
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);
_selected = false;
_unselectedBackground = _cellCore.Background;
return _cellCore;
}
protected override void OnCellPropertyChanged(object sender, PropertyChangedEventArgs args)
{
base.OnCellPropertyChanged(sender, args);
if (args.PropertyName == "IsSelected")
{
_selected = !_selected;
if (_selected)
{
var extendedViewCell = sender as ExtendedViewCell;
_cellCore.SetBackgroundColor(extendedViewCell.SelectedBackgroundColor.ToAndroid());
}
else
{
_cellCore.SetBackground(_unselectedBackground);
}
}
}
}
}
Then you can set color for listview SelectedBackgroundColor.
<ListView ItemsSource="{Binding students}">
<ListView.ItemTemplate>
<DataTemplate>
<local:ExtendedViewCell SelectedBackgroundColor="White">
<StackLayout Orientation="Horizontal">
<Label Text="{Binding Username}" TextColor="Yellow" />
<Label Text="{Binding Age}" TextColor="Blue" />
</StackLayout>
</local:ExtendedViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
More detail info and steps in ios platform, you can take a look:
https://blog.wislon.io/posts/2017/04/11/xamforms-listview-selected-colour
Update:
In ios project, create a class name as ExtendedViewCellRenderer and make sure to add renderer registration for our ExtendedViewCell class above the namespace.
[assembly: ExportRenderer(typeof(ExtendedViewCell), typeof(ExtendedViewCellRenderer))]
namespace xamformsdemo.iOS
{
public class ExtendedViewCellRenderer : ViewCellRenderer
{
public override UITableViewCell GetCell(Cell item, UITableViewCell reusableCell, UITableView tv)
{
var cell = base.GetCell(item, reusableCell, tv);
var view = item as ExtendedViewCell;
cell.SelectedBackgroundView = new UIView
{
BackgroundColor = view.SelectedBackgroundColor.ToUIColor(),
};
return cell;
}
}
}

Resources