I need to get all the viewcells of my listview programmatically so I could change the background color of a specific child layout within the viewcell. There's no problem with updating the color for a viewcell when it's tapped, but I need to change the color of all viewcells to the default color, whenever a different viewcell is tapped.
While searching for a solution I often found answers where the viewcells were accessed by the runtime properties of the listview (see code below or the second answer in here: Xamarin.Forms: Get all cells/items of a listview), while testing the code I realized that this doesn't work for listviews where grouping is enabled.
IEnumerable<PropertyInfo> pInfos = (connectionsListView as ItemsView<Cell>).GetType().GetRuntimeProperties();
var templatedItems = pInfos.FirstOrDefault(info => info.Name == "TemplatedItems");
if (templatedItems != null)
{
var cells = templatedItems.GetValue(connectionsListView);
foreach (ViewCell cell in cells as Xamarin.Forms.ITemplatedItemsList<Xamarin.Forms.Cell>)
{
if (cell.BindingContext != null && cell.BindingContext is MyModelClass)
{
// Change background color of viewcell
}
}
}
When grouping is enabled this code only returns the grouped header viewcells. I couldn't find any answer to change this code so the actual "body" viewcells are returned instead of only the headings. Is there any possible approach to change this code so I get my expected result or do I have to use a custom renderer for this?
Update - Listview xaml code
Here you can see the listview I'm currently using in my XAML. I try to work out a solution where I can bind the background color of each viewcell to the model (to each "document" in my case) but at the moment I couldn't work out how to change the color for each specific viewcell when one is tapped. (I need to only have the background color of the currently selected viewcell to be changed, so all other viewcells have the default background color.)
<ListView x:Name="DocumentListView"
ItemsSource="{Binding GroupedDocuments}"
BackgroundColor="WhiteSmoke"
HasUnevenRows="True"
RefreshCommand="{Binding LoadDocumentsCommand}"
IsPullToRefreshEnabled="True"
Refreshing="DocumentListView_OnRefreshing"
IsRefreshing="{Binding IsBusy, Mode=OneWay}"
CachingStrategy="RecycleElement"
IsGroupingEnabled="True"
GroupDisplayBinding="{Binding Key}"
GroupShortNameBinding="{Binding Key}"
VerticalOptions="StartAndExpand"
HorizontalOptions="StartAndExpand"
Margin="0, -20, 0, 0">
<ListView.GroupHeaderTemplate>
<DataTemplate>
<ViewCell Height="25">
<Label x:Name="DocumentDate"
FontSize="Medium"
TextColor="#2E588C"
VerticalOptions="Center"
HorizontalTextAlignment="Center"
Text="{Binding Key}"/>
</ViewCell>
</DataTemplate>
</ListView.GroupHeaderTemplate>
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell Height="155" Tapped="DocumentViewCell_OnTapped">
<StackLayout Padding="10, 5, 10, 5">
<Frame Padding="0" HorizontalOptions="FillAndExpand" HasShadow="True">
<StackLayout Padding="10" Orientation="Horizontal" HorizontalOptions="FillAndExpand" VerticalOptions="StartAndExpand">
<StackLayout HorizontalOptions="StartAndExpand">
<StackLayout Orientation="Horizontal" Spacing="15" Margin="10, 10, 10, 0" HorizontalOptions="StartAndExpand">
<Label Text="{Binding Name}"
LineBreakMode="NoWrap"
FontSize="20"
TextColor="CornflowerBlue"
FontAttributes="Bold"/>
</StackLayout>
<StackLayout Orientation="Horizontal" Spacing="5" Margin="12, 0, 0, 0" HorizontalOptions="StartAndExpand">
<Label Text="{Binding DocumentType.Name, StringFormat='Typ: {0}'}"
LineBreakMode="NoWrap"
FontSize="16"
TextColor="Black"/>
</StackLayout>
<StackLayout Orientation="Horizontal" Spacing="5" Margin="12, 3, 0, 0" HorizontalOptions="StartAndExpand">
<Label Text="{Binding TotalValue, StringFormat='Gesamtwert: {0:F2} €'}"
LineBreakMode="NoWrap"
FontSize="16"
TextColor="Black"/>
</StackLayout>
<StackLayout Spacing="5" Margin="12, 3, 0, 0" Orientation="Horizontal" HorizontalOptions="StartAndExpand" VerticalOptions="Start">
<Label Text="{Binding TagCollectionString, StringFormat='Tags: {0}'}"
LineBreakMode="WordWrap"
FontSize="14"
TextColor="Black"
VerticalOptions="CenterAndExpand"/>
</StackLayout>
</StackLayout>
<StackLayout HorizontalOptions="EndAndExpand" VerticalOptions="Start" Margin="0, 25, 25, 0">
<ImageButton HeightRequest="85" MinimumWidthRequest="85" x:Name="ButtonEditDocument" Source="baseline_more_vert_black_48.png" Clicked="ButtonEditDocument_OnClicked" Margin="0, 0, 15, 0" BackgroundColor="Transparent" WidthRequest="25" />
</StackLayout>
</StackLayout>
</Frame>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Update 2 - Usage of Bindings
I worked out how I could use bindings to store the currently selected background of each viewcell in my model. At the moment I'm facing the issue that the UI doesn't update properly when the bound values have changed. Here is the code I have written so far and how the page is updated when a different viewcell is tapped:
Document model class:
public class Document
{
public bool HasDefaultColor { get; set; }
public string CurrentlySelectedColorFromHex
{
get => ColorConverter.GetHexString(CurrentlySelectedColor);
}
[NotMapped]
public Color CurrentlySelectedColor => HasDefaultColor ? DefaultColor : ActivatedColor;
private static readonly Color DefaultColor = Color.WhiteSmoke;
private static readonly Color ActivatedColor = Color.FromHex("#2E588C");
}
OnTapped function in code-behind:
private void DocumentViewCell_OnTapped(object sender, EventArgs e)
{
var documents = documentRepository.GetAll();
foreach (var document in documents)
document.HasDefaultColor = true;
selectedDocument.HasDefaultColor = false;
unitOfWork.Complete();
UpdatePage();
}
In UpdatePage() I want to refresh the listview properly after the bound collection has changed:
viewModel.LoadDocuments();
DocumentListView.BeginRefresh();
Sorry if this is a beginner question but I couldn't find an answer to this yet or couldn't figure out how to properly update the UI so that the background color of each viewcell is properly updated. At least the bounded values are stored correctly at each OnTapped() call.
Update 3 - Added Converter
Hey guys, I have tried a few things and get stuck with updating the bound property of the model. I have tried data triggers as well but couldn't change those data triggers correctly so they didn't worked as I expected them to do.
Until now I have added a custom bool to Color converter to convert the bound property:
public class BoolToColorConverter : IValueConverter
{
private static readonly Color DefaultColor = Color.WhiteSmoke;
private static readonly Color ActivatedColor = Color.FromHex("#2E588C");
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is bool activated)
return activated ? ActivatedColor : DefaultColor;
return DefaultColor;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is Color color)
{
if (color == DefaultColor)
return true;
}
return false;
}
The Color converter returns the correct values, but I can't figure out how to update the model property at runtime in the OnTapped() method of each viewcell.
Currently this is my OnTapped() method:
private void DocumentViewCell_OnTapped(object sender, EventArgs e)
{
// Determine which document was selected
if (sender.GetType() == typeof(ViewCell))
{
ViewCell selectedViewCell = (ViewCell)sender;
if (selectedViewCell.BindingContext != null && selectedViewCell.BindingContext.GetType() == typeof(Document))
{
Document document = (Document)selectedViewCell.BindingContext;
// Update backing field selectedDocument for correct bindings and to show correct detail page
if (document != null)
selectedDocument = document;
}
}
Thanks for any help in advance and thanks to anyone who commented so far.