Why this Xamarin CollectionView doesn't update UI when changing element property? - c#

I have a chat application that have a list of chat members which should change property Lida/BgFrame when the last message is seen. Adding new elements to this list works fine, but changing properties of existing elements do nothing.
See my code below(relevant parts only, I have omitted a lot of irrelevant code for this question):
namespace MasterDetailPageNavigation.XAML
{
public static ObservableCollection<ChatMembers> Lista { get; set; } = new ObservableCollection<ChatMembers>();
public static ReaderWriterLockSlim valuesLock = new ReaderWriterLockSlim();
void ObservableCollectionCallback(IEnumerable collection, object context, Action accessMethod, bool writeAccess)
{
var collectionLock = (ReaderWriterLockSlim)context;
Action enterLock = writeAccess ? new Action(collectionLock.EnterWriteLock) : new Action(collectionLock.EnterReadLock);
Action exitLock = writeAccess ? new Action(collectionLock.ExitWriteLock) : new Action(collectionLock.ExitReadLock);
enterLock();
try { accessMethod(); } finally { exitLock(); }
}
public class ChatMembers : INotifyPropertyChanged // THIS IS THE BINDABLE OBJECT
{
public Color WhiteAlpha = new Color(255, 255, 255, 0.7);
public Color GreenAlpha = new Color(160.0f / 255.0f, 255.0f / 255.0f, 160.0f / 255.0f, 0.2);
private Guid _talk { get; set; }
public Guid talk
{
get { return _talk; }
set
{
_talk = value;
NotifyPropertyChanged("talk");
}
}
private string _Username { get; set; }
public string Username
{
get { return _Username; }
set
{
_Username = value;
this.NotifyPropertyChanged("Username");
}
}
private bool _Lida { get; set; }
public bool Lida
{
get { return _Lida; }
set
{
_Lida = Convert.ToBoolean(value);
FrameBg = !_Lida ? (UserFrom == MainPage.MeuID ? WhiteAlpha : GreenAlpha) : WhiteAlpha;
this.NotifyPropertyChanged("Lida");
}
}
private Color _FrameBg { get; set; }
public Color FrameBg
{
get { return _FrameBg; }
set
{
_FrameBg = value;
this.NotifyPropertyChanged("FrameBg");
}
}
private string _Msg { get; set; }
public string Msg
{
get { return _Msg; }
set
{
_Msg = value;
this.NotifyPropertyChanged("Msg");
}
}
public event PropertyChangedEventHandler PropertyChanged;
void NotifyPropertyChanged(string propName)
{
if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
// Code that updates property "Lida":
tasks.Add(Task.Run(() =>
{
Color GreenAlpha = new Color(160.0f / 255.0f, 255.0f / 255.0f, 160.0f / 255.0f, 0.2);
bool itemExists = false;
valuesLock.EnterReadLock();
try
{
itemExists = Lista.Any(i => i.talk == guid);
}
finally { valuesLock.ExitReadLock(); }
if (itemExists)
{
ChatMembers item;
int indexof;
valuesLock.EnterReadLock();
try
{
item = Lista.Single(i => i.talk == guid);
indexof = Lista.IndexOf(item);
}
finally { valuesLock.ExitReadLock(); }
_ = Device.InvokeOnMainThreadAsync(() =>
{
valuesLock.EnterWriteLock();
try
{
Lista[indexof].Lida = true;
Lista[indexof].FrameBg = new Color(255, 255, 255, 0.7); //desperate try but no success
}
finally { valuesLock.ExitWriteLock(); }
});
}
}));
}
VIEW CODE BEHIND(relevant part):
CollectionViewMembers.BindingContext = MainPage.Lista;
VIEW XAML(relevant part):
<CollectionView x:Name="CollectionViewMembers" SelectionMode="None"
HorizontalScrollBarVisibility="Never" ItemsSource="{Binding .}">
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout Padding="6,10,5,1" Margin="0,0,0,0" BackgroundColor="{Binding FrameBg}">
<StackLayout.GestureRecognizers>
<TapGestureRecognizer Command="{Binding Evento}" />
</StackLayout.GestureRecognizers>
<Grid HorizontalOptions="FillAndExpand" VerticalOptions="CenterAndExpand">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="60"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="60"></RowDefinition>
</Grid.RowDefinitions>
<Frame IsClippedToBounds="True" Grid.Row="0" Grid.Column="0" CornerRadius="30" Padding="0" Margin="0">
<ffimageloading:CachedImage DownsampleToViewSize="True" HeightRequest="100" IsOpaque="True" Source="{Binding Imagem}" Aspect="AspectFill" LoadingPlaceholder="resource://MasterDetailPageNavigation.shared.icon_default.png" ErrorPlaceholder="resource://MasterDetailPageNavigation.shared.icon_default.png" />
</Frame>
<Grid Grid.Row="0" Grid.Column="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="15" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="25" />
<RowDefinition Height="20" />
<RowDefinition Height="15" />
</Grid.RowDefinitions>
<Label Grid.Column="0" Grid.Row="0" Text="" TextColor="{Binding ColorOnline}" HorizontalOptions="Center" VerticalOptions="Center" FontSize="15">
<Label.FontFamily>
<OnPlatform x:TypeArguments="x:String" Android="Font-Awesome-Free-Solid.otf#FontAwesome5Free-Solid" iOS="FontAwesome5Free-Solid" />
</Label.FontFamily>
</Label>
<Label Grid.Column="1" Grid.Row="0" Text="{Binding Username}" HorizontalOptions="StartAndExpand" VerticalOptions="Center" FontSize="18" FontAttributes="Bold" LineBreakMode="TailTruncation" />
<Label Grid.ColumnSpan="2" Grid.Column="0" Grid.Row="1" Text="{Binding Msg}" FontSize="15" HorizontalOptions="StartAndExpand" VerticalOptions="Start" LineBreakMode="TailTruncation" />
<Label Grid.ColumnSpan="1" Grid.Column="1" Grid.Row="2" Text="{Binding DataHora}" FontSize="11" TextColor="#CACACA" HorizontalOptions="End" VerticalOptions="Start" />
</Grid>
</Grid>
<BoxView Margin="0,10,0,0" HeightRequest="0.05" WidthRequest="100" BackgroundColor="#CCE5E5E5"/>
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
I also tried to do Lista[indexOf]=item; but no UI update.
Where is my mistake?

Related

Changing Data in CollectionView (Xamarin)

I use CollectionView to show data on screen, but when I change data, UI is not changing, although I am using OnPropertyChanged. Here is the code:
Xaml
<CollectionView ItemsSource="{Binding GridData}" Margin="15">
<CollectionView.ItemTemplate>
<DataTemplate>
<Grid Margin="15" Padding="5">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Label Grid.Row="0"
Grid.Column="0"
HorizontalTextAlignment="Start"
Text="{Binding Title}"
FontSize="Small"/>
<Label Grid.Row="0"
Grid.Column="1"
HorizontalTextAlignment="End"
Text="{Binding Data}"
TextColor="Black"
FontSize="Medium">
<Label.GestureRecognizers>
<TapGestureRecognizer
Command="{Binding Source={x:Reference Page} , Path=BindingContext.TapCommand}"
CommandParameter="{Binding Title}" />
</Label.GestureRecognizers>
</Label>
<BoxView Grid.Row="1"
Grid.Column="0"
Grid.ColumnSpan="2"
BackgroundColor="LightGray"
CornerRadius="2"
HorizontalOptions="FillAndExpand"
HeightRequest="1"></BoxView>
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
ViewModel
private List<CollectionEntity> _gridData;
public List<CollectionEntity> GridData
{
get => _gridData;
set
{
if (_gridData != value)
{
_gridData = value;
OnPropertyChanged(nameof(GridData));
}
}
}
public ICommand TapCommand
{
get
{
return new Command<CollectionView>((commandParameters) =>
{
OpenEditing(commandParameters.ToString());
OnPropertyChanged(nameof(GridData));
});
}
}
Model (is in the same file, as is ViewModel)
public class CollectionEntity: INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public string Title { get; set; }
public string Data { get; set; }
}
So, when I tap on the Label, UI does not react. I tried to write it according to this answer, but cannot understand, what is incorrect.
UPD: new command
public ICommand TapCommand => new Command<object>((commandParameters) =>
{
OpenEditing(commandParameters.ToString()); // changing data
});
Though you had write the code about INotifyPropertyChanged in your model but you didn't implement it on the property Title and Data . Modify the code like following
private string title;
public string Title
{
get => title;
set
{
if (title!= value)
{
title = value;
OnPropertyChanged(nameof(Title));
}
}
}
private string data;
public string Data
{
get => data;
set
{
if (data!= value)
{
data= value;
OnPropertyChanged(nameof(Data));
}
}
}
In addition, the code in TapCommand seems will not change the value of source . You could binding the whole model to the command and set the title or data in command as you want .
CommandParameter="{Binding .}"
public ICommand TapCommand
{
get
{
return new Command<CollectionView>((arg) =>
{
var model = arg as CollectionEntity;
// model.Title = "xxx";
});
}
}

IsVisible not loading correctly in ListView

I've got a ListView with some items inside, each item have some buttons but depends by his own data.
My xaml:
<ListView
x:Name="ListViewRepartos"
Margin="10"
CachingStrategy="RecycleElement"
HasUnevenRows="True">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Frame Margin="10" BackgroundColor="{Binding ColorVisita}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Label
Grid.Row="0"
Grid.Column="0"
Text="Cliente:" />
<Label
Grid.Row="1"
Grid.Column="0"
Text="Direccion:" />
<Label
Grid.Row="3"
Grid.Column="0"
Text="Importe:" />
<Label
Grid.Row="0"
Grid.Column="1"
LineBreakMode="WordWrap"
Text="{Binding Cliente}" />
<Label
Grid.Row="1"
Grid.RowSpan="2"
Grid.Column="1"
LineBreakMode="CharacterWrap"
Text="{Binding Direccion}" />
<Label
Grid.Row="3"
Grid.Column="1"
LineBreakMode="WordWrap"
Text="{Binding Importe}" />
<StackLayout
Grid.Row="5"
Grid.ColumnSpan="2"
Orientation="Horizontal">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="50" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Button
Grid.Row="0"
Grid.Column="0"
Command="{Binding Entregar}"
HorizontalOptions="CenterAndExpand"
IsVisible="{Binding BotonesVisibles}"
Text="Entregar" />
<Button
Grid.Row="0"
Grid.Column="1"
Command="{Binding Llevame}"
HorizontalOptions="CenterAndExpand"
IsVisible="{Binding BotonesVisibles}"
Text="Llevame" />
<Button
Grid.Row="0"
Grid.Column="0"
Command="{Binding Cobrar}"
HorizontalOptions="CenterAndExpand"
IsVisible="{Binding BotonCobrosVisible}"
Text="Cobros" />
<Button
Grid.Row="0"
Grid.Column="1"
Command="{Binding Modificar}"
HorizontalOptions="CenterAndExpand"
IsVisible="{Binding BotonModificarVisible}"
Text="Modificar" />
</Grid>
</StackLayout>
</Grid>
</Frame>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
.cs loading items for ListView:
var resultado = new ObservableCollection<Modelos.Reparto>(Utils.Campos.Repartos.Select(q => ActivarBotones(q)).Where(q => q != null).ToList());
Utils.UIHandler.RunOnUI(() => {
this.ListViewRepartos.ItemsSource = null;
this.ListViewRepartos.ItemsSource = resultado;
ListViewRepartos.EndRefresh();
});
I load the item and activate the buttons with ActivarBotones:
private Modelos.Reparto ActivarBotones(Modelos.Reparto q)
{
try
{
q.SetBotones(this);
return q;
}
catch (Exception ex)
{
Utils.UIHandler.ErrorDebug("ListaRepartos: Warning", ex.Message, Utils.UIHandler.MessageLevel.Warning);
}
return null;
}
Where SetBotones is in my model:
public bool BotonesVisibles
{
get { return Visitado == EstadoVisita.NoVisitado; }
}
public bool BotonCobrosVisible
{
get {
return Visitado == EstadoVisita.Entregado || Visitado == EstadoVisita.EntregadoMod;
}
}
public bool BotonModificarVisible
{
get
{
return Visitado == EstadoVisita.EntregadoMod;
}
}
public void SetBotones(Controladores.Controlador Host)
{
this.Entregar = new Command(() => {
Host.Unico(async () => {
Utils.UIHandler.NavPush(new Vistas.Entregar(this.DatosCabecera, this.DatosCobro, this));
});
});
this.Llevame = new Command(() => {
Host.Unico(async () => {
Utils.UIHandler.RunOnUI(() => {
....
});
});
this.Cobrar = new Command(() => {
Host.Unico(async () => {
Utils.UIHandler.NavPush(new Vistas.DatosPago(this.DatosCabecera));
});
});
this.Info = new Command(() => {
Host.Unico(async () => {
Utils.UIHandler.NavPush(new Vistas.DatosCliente(this.DatosCliente));
});
});
this.Modificar = new Command(() => {
Host.Unico(async () => {
Utils.UIHandler.NavPush(new Vistas.AlbaranFactura(this.DatosCabecera,this.DatosCliente));
});
});
}
But when i see the View in with real data, sometimes it display correctly, and sometimes not, also if go into item tapped and comes back by back button, it loads correctly fitted the buttons.
This image is an example image:
Any idea what i am doing wrong or why is not displaying correctly always the buttons ?
EDIT:
I've change the projecto to mvvm but still getting white spaces because don't display the correct buttons:
xaml:
<StackLayout
Grid.Row="5"
Grid.ColumnSpan="2"
Orientation="Horizontal">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="50" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Button
Grid.Row="0"
Grid.Column="0"
Command="{Binding Entregar}"
HorizontalOptions="CenterAndExpand"
IsVisible="{Binding BotonesVisibles}"
Text="Entregar" />
<Button
Grid.Row="0"
Grid.Column="1"
Command="{Binding Llevame}"
HorizontalOptions="CenterAndExpand"
IsVisible="{Binding BotonLlevarVisible}"
Text="Llevame" />
<Button
Grid.Row="0"
Grid.Column="0"
Command="{Binding Cobrar}"
HorizontalOptions="CenterAndExpand"
IsVisible="{Binding BotonCobrosVisible}"
Text="Cobros" />
<Button
Grid.Row="0"
Grid.Column="1"
Command="{Binding Modificar}"
HorizontalOptions="CenterAndExpand"
IsVisible="{Binding BotonModificarVisible}"
Text="Modificar" />
</Grid>
</StackLayout>
Model:
public EstadoVisita visitado = EstadoVisita.NoVisitado;
public EstadoVisita Visitado
{
get
{
return visitado;
}
set
{
if(visitado == EstadoVisita.NoVisitado) { botonesvisibles = true; botonllevarvisible = true; }
if(visitado == EstadoVisita.Entregado || visitado == EstadoVisita.EntregadoMod) { botoncobrosvisible = true; botonllevarvisible = false; botonesvisibles = false; }
if(visitado == EstadoVisita.EntregadoMod) { botonmodificarvisible = true; botonllevarvisible = false; botonesvisibles = false; }
}
}
public bool botonesvisibles = true;
public bool BotonesVisibles
{
get { return botonesvisibles; }
set { botonesvisibles = value; }
}
public bool botoncobrosvisible;
public bool BotonCobrosVisible
{
get { return botoncobrosvisible; }
set { botoncobrosvisible = value; }
}
public bool botonmodificarvisible;
public bool BotonModificarVisible
{
get { return botonmodificarvisible; }
set { botonmodificarvisible = value; }
}
public bool botonllevarvisible = true;
public bool BotonLlevarVisible
{
get { return botonllevarvisible; }
set { botonllevarvisible = value; }
}
Any other thing to consider?

Alternate row color GroupedListview

From this example:
https://blog.verslu.is/stackoverflow-answers/alternate-row-color-listview/
How can I implement it with a grouped listview? I want to have the alternate row color inside every group of the listview, I already tried to implement it with the grouped Listview, but is always giving me "System.InvalidCastException: Specified cast is not valid." inside the DataTemplateSelector.
Listview Code:
<DataTemplate x:Key="evenTemplate">
<ViewCell>
<customRenders:GridConf Margin="0,0,0,0" HorizontalOptions="FillAndExpand" ColumnSpacing="0" RowSpacing="0" ConfigurationItem ="{Binding .}">
<customRenders:GridConf.ColumnDefinitions>
<ColumnDefinition Width="80"/>
<ColumnDefinition Width="*"/>
</customRenders:GridConf.ColumnDefinitions>
<customRenders:GridConf.RowDefinitions>
<RowDefinition Height="*"/>
</customRenders:GridConf.RowDefinitions>
<BoxView VerticalOptions="CenterAndExpand" HeightRequest="50" Grid.ColumnSpan="1" Margin="-30,0,0,0" Grid.Column="1" HorizontalOptions="FillAndExpand" BackgroundColor="LightGray"/>
<Label VerticalOptions="CenterAndExpand" Margin="10,0,0,0" Grid.Column="1" VerticalTextAlignment="Center" HorizontalOptions="StartAndExpand" Text="tetetetetetet"></Label>
<Image Grid.Column="1" Source="HidePass.png" HeightRequest="30" VerticalOptions="CenterAndExpand" HorizontalOptions="End">
<Image.GestureRecognizers>
<TapGestureRecognizer NumberOfTapsRequired="1" ></TapGestureRecognizer>
</Image.GestureRecognizers>
</Image>
<customRenders:CachedImageItem Grid.Column="0" ConfigurationItem ="{Binding .}" HorizontalOptions="Start" HeightRequest="80" VerticalOptions="Center" x:Name="Image2" Source="{Binding Img}"/>
<customRenders:GridConf.GestureRecognizers>
<TapGestureRecognizer NumberOfTapsRequired="1" ></TapGestureRecognizer>
</customRenders:GridConf.GestureRecognizers>
</customRenders:GridConf>
</ViewCell>
</DataTemplate>
<DataTemplate x:Key="unevenTemplate">
<ViewCell>
<customRenders:GridConf Margin="20,0,0,0" HorizontalOptions="FillAndExpand" ColumnSpacing="0" RowSpacing="0" ConfigurationItem ="{Binding .}">
<customRenders:GridConf.ColumnDefinitions>
<ColumnDefinition Width="80"/>
<ColumnDefinition Width="*"/>
</customRenders:GridConf.ColumnDefinitions>
<customRenders:GridConf.RowDefinitions>
<RowDefinition Height="*"/>
</customRenders:GridConf.RowDefinitions>
<BoxView VerticalOptions="CenterAndExpand" HeightRequest="50" Grid.ColumnSpan="1" Margin="-30,0,0,0" Grid.Column="1" HorizontalOptions="FillAndExpand" BackgroundColor="LightGray"/>
<Label VerticalOptions="CenterAndExpand" Margin="10,0,0,0" Grid.Column="1" VerticalTextAlignment="Center" HorizontalOptions="StartAndExpand" Text="teteteteteette"></Label>
<Image Grid.Column="1" Source="HidePass.png" HeightRequest="30" VerticalOptions="CenterAndExpand" HorizontalOptions="End">
<Image.GestureRecognizers>
<TapGestureRecognizer NumberOfTapsRequired="1" ></TapGestureRecognizer>
</Image.GestureRecognizers>
</Image>
<customRenders:CachedImageItem Grid.Column="0" ConfigurationItem ="{Binding .}" HorizontalOptions="Start" HeightRequest="80" VerticalOptions="Center" x:Name="Image2" Source="{Binding Img}"/>
<customRenders:GridConf.GestureRecognizers>
<TapGestureRecognizer NumberOfTapsRequired="1" ></TapGestureRecognizer>
</customRenders:GridConf.GestureRecognizers>
</customRenders:GridConf>
</ViewCell>
</DataTemplate>
<customRenders1:AlternateColorDataTemplateSelector2 x:Key="alternateColorDataTemplateSelector"
EvenTemplate="{StaticResource evenTemplate}"
UnevenTemplate="{StaticResource unevenTemplate}" />
</ResourceDictionary>
</ContentPage.Resources>
<ListView x:Name="lst" IsGroupingEnabled="True" ItemTemplate="{StaticResource alternateColorDataTemplateSelector}" ItemsSource="{Binding Item}" Margin="5,5,0,0" HasUnevenRows="True" SeparatorVisibility="None">
<ListView.GroupHeaderTemplate>
<DataTemplate>
<customRenders:NativeCell>
<customRenders:NativeCell.View>
<ContentView Padding="10,0,0,0">
<StackLayout>
<Label Text="{Binding Key.Category}" VerticalOptions="Center"/>
</StackLayout>
<ContentView.GestureRecognizers>
<TapGestureRecognizer Command="{Binding Source={x:Reference ProtocolosPage}, Path=BindingContext.HeaderSelectedCommand}" CommandParameter="{Binding .}"/>
</ContentView.GestureRecognizers>
</ContentView>
</customRenders:NativeCell.View>
</customRenders:NativeCell>
</DataTemplate>
</ListView.GroupHeaderTemplate>
</ListView>
AlternateColorDataTemplateSelector:
public DataTemplate EvenTemplate { get; set; }
public DataTemplate UnevenTemplate { get; set; }
protected override DataTemplate OnSelectTemplate(object item, BindableObject container)
{
// TODO: Maybe some more error handling here
return ((List<Product>)((ListView)container).ItemsSource).IndexOf(item as Product) % 2 == 0 ? EvenTemplate : UnevenTemplate;
}
ViewModel
public class ProductsViewModel: BindableBase
{
public class SelectCategoryViewModel
{
public string Category { get; set; }
public bool Selected { get; set; }
}
private ObservableCollection<Grouping<string, Product>> _ProductsGrouped;
public ObservableCollection<Product> Productitems { get; set; }
public ObservableCollection<Grouping<string, Models.Product>> ProductsGrouped
{
get
{
return _ProductsGrouped;
}
set
{
_ProductsGrouped = value;
OnPropertyChanged();
}
}
public ObservableCollection<Grouping<SelectCategoryViewModel, Product>> Item { get; set; }
public DelegateCommand<Grouping<SelectCategoryViewModel, Product>> HeaderSelectedCommand
{
get
{
return new DelegateCommand<Grouping<SelectCategoryViewModel, Product>>(g =>
{
if (g == null) return;
g.Key.Selected = !g.Key.Selected;
if (g.Key.Selected)
{
Productitems.Where(i => (i.Category.Equals(g.Key.Category)))
.ForEach(g.Add);
}
else
{
g.Clear();
}
});
}
}
public ProductsViewModel()
{
Productitems = new ObservableCollection<Product>
{
new Product
{
Img = "dss.png",
Url = "Teste",
Category = "service",
Title = "sdsadsadsdsdsa"
},
new Product
{
Img = "dss.png",
Url = "Teste3",
Category = "service",
Title = "sdsadsadsdsdsatest2"
},
new Product
{
Img = "dss.png",
Url = "Teste2",
Category = "Farmacy",
Title = "sdsadsadsdsdsaes"
},
new Product
{
Img = "dss.png",
Url = "Teste4",
Category = "Farmacy",
Title = "sdsadsadsdsdsaF"
},
new Product
{
Img = "dss.png",
Url = "Teste7",
Category = "Farmacy",
Title = "sdsadsadsdsdsaFarmarcia2"
},
new Product
{
Img = "dss.png",
Url = "Teste9",
Category = "Farmacy",
Title = "sdsadsadsdsdsae"
}
};
Item = new ObservableCollection<Grouping<SelectCategoryViewModel, Product>>();
var selectCategories =
Productitems.Select(x => new SelectCategoryViewModel { Category = x.Category, Selected = false })
.GroupBy(sc => new { sc.Category })
.Select(g => g.First())
.ToList();
selectCategories.ForEach(sc => Item.Add(new Grouping<SelectCategoryViewModel, Product>(sc, new List<Product>())));
}
}
This is not working, since the code in the AlternateColorDataTemplateSelector is casting the ItemSource to a List. When you're using grouping, it cannot be a simple list.
On the other hand, when you do an IndexOf on one group, you will receive the index within that group, which does not need to correspond to the index in the full list.
Find a sample repository here: https://github.com/jfversluis/GroupedListViewAlternateRowColor.
In the adapted DataTemplateSelector I flatten out the whole list and get the index from there. Here is the code:
public class AlternateColorDataTemplateSelector : DataTemplateSelector
{
public DataTemplate EvenTemplate { get; set; }
public DataTemplate UnevenTemplate { get; set; }
private List<string> flatList;
protected override DataTemplate OnSelectTemplate(object item, BindableObject container)
{
if (flatList == null)
{
var groupedList = (ObservableCollection<Grouping<string, string>>)((ListView)container).ItemsSource;
flatList = groupedList.SelectMany(group => group).ToList();
}
return flatList.IndexOf(item as string) % 2 == 0 ? EvenTemplate : UnevenTemplate;
}
}
As an optimization, I just create the flat list once. This could go wrong whenever the list is updated with new items. I didn't test that.
The result looks like this:

Collection was modified; enumeration operation may not execute. Xamarin Forms

I have a list view in Xamarin Froms Project as :
<ListView x:Name="ExerciseList" HasUnevenRows="False" SeparatorVisibility="None" RowHeight="200">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Orientation="Vertical">
<StackLayout Orientation="Horizontal" HorizontalOptions="CenterAndExpand">
<Entry Text="{Binding ExerciseName}" HorizontalTextAlignment="Center" Focused="ExerciseName_Focused" HorizontalOptions="CenterAndExpand">
<Entry.GestureRecognizers>
<TapGestureRecognizer Tapped="ExerciseNameGestureRecognizer_Tapped"/>
</Entry.GestureRecognizers>
</Entry>
<Image IsVisible="{Binding GreenVisible}" Source="smallgreenadd.png"/>
<Image IsVisible="{Binding RedVisible}" Source="smallredremove.png"/>
</StackLayout>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="1*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="1*"/>
<RowDefinition Height="1*"/>
<RowDefinition Height="1*"/>
</Grid.RowDefinitions>
<Label Text="Sets : " Grid.Column="0" Grid.Row="0" HorizontalTextAlignment="Center" HorizontalOptions="CenterAndExpand" />
<Label Text="Weights : " Grid.Column="0" Grid.Row="1" HorizontalTextAlignment="Center" HorizontalOptions="CenterAndExpand" />
<Label Text="Reps: " Grid.Column="0" Grid.Row="2" HorizontalTextAlignment="Center" HorizontalOptions="CenterAndExpand" />
<Entry Text="{Binding Sets}" Grid.Column="1" Grid.Row="0" HorizontalTextAlignment="Center" HorizontalOptions="CenterAndExpand" />
<Entry Text="{Binding Weights}" Grid.Column="1" Grid.Row="1" HorizontalTextAlignment="Center" HorizontalOptions="CenterAndExpand" />
<Entry Text="{Binding Reps}" Grid.Column="1" Grid.Row="2" HorizontalTextAlignment="Center" HorizontalOptions="CenterAndExpand" />
</Grid>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
This is attached to a View Modal called ExerciseViewModal. This is:
public class ExerciseViewModal : BaseViewModal
{
private List<AddExerciseModal> _addExerciseModals;
public List<AddExerciseModal> AddExerciseModals
{
get { return _addExerciseModals; }
set
{
_addExerciseModals = value;
OnPropertyChanged("AddExerciseModals");
}
}
public ExerciseViewModal()
{
_addExerciseModals = new List<AddExerciseModal>();
if (AddExerciseModals.Count == 0)
{
for (int i = 0; i < 7; i++)
{
AddExerciseModal addExerciseModal = new AddExerciseModal
{
ExerciseID = i,
ExerciseName = "Excercise " + i,
GreenVisible = false,
RedVisible = true,
Sets = "2",
Reps = "10",
Weights = "10"
};
AddExerciseModals.Add(addExerciseModal);
}
AddExerciseModals[AddExerciseModals.Count - 1].GreenVisible = true;
AddExerciseModals[AddExerciseModals.Count - 1].RedVisible = false;
}
}
}
AddExerciseModal class :
public class AddExerciseModal
{
public int ExerciseID { get; set; }
public string ExerciseName { get; set; }
public string Weights { get; set; }
public string Reps { get; set; }
public string Sets { get; set; }
public bool GreenVisible { get; set; }
public bool RedVisible { get; set; }
}
Whenever I try to change the sets/reps/Weights property inside the ListView I always get an error saying:
"Collection was modified; enumeration operation may not execute."
How can I solve this?
Can you change your List to ObservableCollection and try it .That will resolve the problem.

Binding Itemsource in ListView inside of CarouselView - Xamarin

I have tried to put a ListView inside a CarouselView, but the element binding does not work.
If I comment out the ListView, the project will work fine.
The error is
Binding: 'Contenido' property not found on
'App.ViewModels.FeedListViewModel', target property:
'Xamarin.Forms.ListView.ItemsSource'
The code is as follows:
VIEWMODEL
public class Feed
{
public int IdFeed { get; set; }
public String Title { get; set; }
public bool HasNewElements { set; get; }
public ObservableCollection<NewsFeedDocument> Contenido { get; set; }
}
public class FeedListViewModel : MenuViewModel
{
public ObservableCollection<Feed> Feeds { get; set; }
public FeedListViewModel()
{
Feeds = new ObservableCollection<Feed>();
for (int i = 0; i <= 12; i++)
{
//Creamos lista de elementos Feed
NewsFeedDocument documento = new NewsFeedDocument();
documento.Titulo = "TITULO dasd dsa dsa dsa dsa";
ObservableCollection<NewsFeedDocument> ElementsX = new ObservableCollection<NewsFeedDocument>();
ElementsX.Add(documento);
Feeds.Add(new Feed
{
IdFeed = i ,Title = "SECTOR" + i,
Contenido = ElementsX
});
}
Position = 0;
}
private int _position = 0;
public int Position
{
get { return _position; }
set { _position = value; RaisePropertyChanged(() => Position); }
}
}
}
XAML
<cv:CarouselView x:Name="FeedsItemsCarousel" Grid.Row="2" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand"
Position="{Binding Position, Mode=TwoWay}"
ItemsSource="{Binding Feeds}">
<cv:CarouselView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<!--<Image Grid.RowSpan="2" Aspect="AspectFill" Source="{Binding ImageUrl}"/>-->
<StackLayout Grid.Row="1" BackgroundColor="#80000000" Padding="12">
<Label TextColor="White" Text="{Binding Title}" FontSize="16" HorizontalOptions="Center" VerticalOptions="CenterAndExpand"/>
</StackLayout>
</Grid>
<StackLayout>
<ListView x:Name="FeedListItem" ItemsSource="{Binding Contenido}" RowHeight="120">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Orientation="Vertical" VerticalOptions="FillAndExpand" HorizontalOptions="Fill" BackgroundColor="White" Padding="10">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="100" />
</Grid.ColumnDefinitions>
<Label Text="{Binding Titulo}" Style="{StaticResource TituloL}" Grid.Row="0" Grid.Column="0" />
</Grid>
<StackLayout Orientation="Horizontal">
</StackLayout>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</DataTemplate>
</cv:CarouselView.ItemTemplate>

Categories