I have a class that contains different characteristics for a pokemon
public ObservableCollection<Pokemon> GetMesPokemons()
{
ObservableCollection<Pokemon> lstPokemons =
new ObservableCollection<Pokemon>();
lstPokemons.Add(new Pokemon()
{
NamePokemon = "Raichu",
UrlImage = "https://i0.wp.com/pokemon-suisse.ch/wp-content/uploads/2021/06/img_1517.jpg?resize=1536%2C2048&ssl=1",
Abilities = new List<Ability>()
{
new Ability
{
Name = "Thunderbolt",
NbPower = 120
},
new Ability
{
Name = "Spark Ball GX",
NbPower = 200
}
},
PokemonClass = new PokemonType()
{
NameClass = "Electric",
UrlImage = "https://th.bing.com/th/id/OIP.T-kNLsS_VhinWEuPIfZdHAHaHk?w=159&h=180&c=7&r=0&o=5&pid=1.7"
},
Pv = 210
I can get the name of the pokemon and its image and pv, but when I try to get its "Abilities" I get this message on the display: System.Collections.Generic.List'1[MauiApp2.Models.Ability]
This is how I try to get it back
<CollectionView
x:Name="PokemonsAbilities"
ItemsSource="{Binding AbilitiesSource}"
ItemTemplate="{StaticResource AbilitiesTemplate}">
</CollectionView>
<!--Récupération de l'image d'un pokemonRandom-->
<Image Source="{Binding SelectedPokemon.UrlImage}" WidthRequest="150" HeightRequest="150"/>
<!--Récupération du nom d'un pokemonrandom-->
<Label
Text="{Binding SelectedPokemon.NamePokemon}"
VerticalOptions="Center"
HorizontalOptions="Center" />
<Label
Text="{Binding SelectedPokemon.Pv}"
VerticalOptions="Center"
HorizontalOptions="Center" />
<Label
Text="{Binding SelectedPokemon.Abilities}"
VerticalOptions="Center"
HorizontalOptions="Center" />
This is my Pokemon model
public class Pokemon
{
private string _namePokemon;
public string NamePokemon
{
get { return _namePokemon; }
set { _namePokemon = value; }
}
private string _urlImage;
public string UrlImage
{
get { return _urlImage; }
set { _urlImage = value; }
}
private int _pv;
public int Pv
{
get { return _pv; }
set { _pv = value; }
}
public List<Ability> Abilities { get; set; }
This is my Ability model
public class Ability
{
private string _name;
public string Name
{
get { return _name; }
set { _name = value; }
}
private int _nbPower;
public int NbPower
{
get { return _nbPower; }
set { _nbPower = value; }
}
My template
<DataTemplate x:Key="LstPokemons">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.5*"></ColumnDefinition>
<ColumnDefinition Width="0.5*"></ColumnDefinition>
<ColumnDefinition Width="0.5*"></ColumnDefinition>
<ColumnDefinition Width="0.5*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<!--<Button Text="Profil"></Button>-->
<Image Source="{Binding UrlImage}" Grid.Column="2" WidthRequest="150" HeightRequest="150"></Image>
<Label Text="{Binding NamePokemon}" Grid.Column="0"></Label>
</Grid>
</DataTemplate>
<DataTemplate x:Key="Abilities">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.5*"></ColumnDefinition>
<ColumnDefinition Width="0.5*"></ColumnDefinition>
<ColumnDefinition Width="0.5*"></ColumnDefinition>
<ColumnDefinition Width="0.5*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Label Text="{Binding Name}" />
<Label Text="{Binding NbPower}" />
</Grid>
</DataTemplate>
My "PokemonView" page representing my two View collections based on my two DataTemplates one to retrieve the list of pokemons and the other to retrieve the abilities of a pokemon
<ContentPage.BindingContext>
<vm:PokemonViewModel></vm:PokemonViewModel>
</ContentPage.BindingContext>
<VerticalStackLayout>
<Label
Text="{Binding Pseudo}"
VerticalOptions="Center"
HorizontalOptions="Center" />
<Label Text="Voici les pokemons disponible pour un match"></Label>
<!--Collectionview : qui contient mon template pour afficher une grid qui va contenir des pokémons
On peut en sélectionner un à la fois-->
<CollectionView
x:Name="Pokemons"
ItemTemplate="{StaticResource LstPokemons}"
ItemsSource="{Binding ListPokemons}"
SelectionMode="Single"
SelectedItem="{Binding MonSelectedPokemon}">
</CollectionView>
<CollectionView
x:Name="PokemonsAbilities"
ItemTemplate="{StaticResource AbilitiesTemplate}"
ItemsSource="{Binding AbilitiesSource}">
</CollectionView>
<!--Boutton pour passer a la page suivante une fois qu'il a séléctionné un pokémon-->
<Button x:Name="btnValidate" Text="Valider" Command="{Binding ValidateSelected}"></Button>
</VerticalStackLayout>
</ContentPage>
You need a property in your model that will convert your list of Ability objects into a string that you can display in a Label
public string AbilitiesDesc
{
get
{
return string.Join(",", Abilities.Select(a => a.Name));
}
}
Then bind this new property to the Label
<Label
Text="{Binding SelectedPokemon.AbilitiesDesc}"
VerticalOptions="Center"
HorizontalOptions="Center" />
Related
How to bind a Label color in a ListView?
I can't set the color in any way, it shows standard gray. You can set a certain color (for example, red), but I need it to change dynamically, from the user's desire.
<ListView
Style="{StaticResource ListViewStyle}"
ItemsSource="{Binding Stats}"
SelectedItem="{Binding CurrentStatParam}"
HasUnevenRows="true">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid Column="0">
<Label Text="{Binding Name}" **TextColor="{Binding TextColor}"**/>
</Grid>
<Grid Column="1">
<Label Text="{Binding Value}" **TextColor="{Binding TextColor}"**/>
</Grid>
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
public Color TextColor
{
get => _textColor;
set
{
_textColor = value;
OnPropertyChanged(nameof(TextColor));
}
}
<ContentPage.Content>
<Grid>
<StackLayout HorizontalOptions="Center" VerticalOptions="Center">
<Label Text="Back Color" Margin="0,0,0,10" />
<colorPicker:ColorPickerEntry Color="{Binding BackColor}" />
<Label Text="Line color" Margin="0,0,0,10" />
<colorPicker:ColorPickerEntry Color="{Binding LineColor}" />
<Label Text="Text Color" Margin="0,0,0,10" />
<colorPicker:ColorPickerEntry Color="{Binding TextColor}" />
</StackLayout>
<!--<Button Text="Назад" Command="{Binding BackCmd}"></Button>-->
</Grid>
</ContentPage.Content>
The problem is in your ItemsSource. There is no property named "TextColor" here. You can use following code to escape from this situation:
<Label Text="{Binding Name}" TextColor="{Binding Source={x:Reference This}, Path=BindingContext.TextColor}"/>
Do you want to change the color like following gif?
If so, firstly, please achieve INotifyPropertyChanged interface. Here is my Model.
public class MyModel: INotifyPropertyChanged
{
string name;
public string Name
{
set
{
if (name != value)
{
name = value;
OnPropertyChanged("Name");
}
}
get
{
return name;
}
}
string _value;
public string Value
{
set
{
if (_value != value)
{
_value = value;
OnPropertyChanged("Value");
}
}
get
{
return _value;
}
}
private Color _textColor=Color.Green;
public Color TextColor
{
get { return _textColor; }
set
{
_textColor = value;
OnPropertyChanged("TextColor");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
When we change the Color of the Text, I set it by Button's Command in the ViewModel.
public class MyViewModel
{
public ObservableCollection<MyModel> Stats { get; set; }
public ICommand ColorChangeCommand { protected set; get; }
public MyViewModel()
{
Stats = new ObservableCollection<MyModel>();
Stats.Add(new MyModel() { Name="test1", Value="1" });
Stats.Add(new MyModel() { Name = "test2", Value = "2" });
Stats.Add(new MyModel() { Name = "test3", Value = "3" });
ColorChangeCommand = new Command<MyModel>(async (key) =>
{
key.TextColor = Color.Red;
});
}
}
Here is my editted Listview.
<ListView
ItemsSource="{Binding Stats}"
x:Name="mylistview"
HasUnevenRows="true">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid Column="0">
<Label Text="{Binding Name}" TextColor="{Binding TextColor}"/>
</Grid>
<Grid Column="1">
<Label Text="{Binding Value}" TextColor="{Binding TextColor}"/>
</Grid>
<Grid Column="2">
<Button Text="change" Command="{Binding BindingContext.ColorChangeCommand, Source={x:Reference Name=mylistview} }" CommandParameter="{Binding .}"></Button>
</Grid>
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Here is my layout background code.
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
this.BindingContext = new MyViewModel();
}
}
I am doing a Weather app project in Xamarin.Forms. After successfully adding users to the database and validating the existing ones on "log in", now I am struggling with adding a town(with 3 variables) inside SQLite database and then displaying the information in ListView on a content page.
The problem is that when I click the "weather" button there is nothing displayed in the ListView and also the second catch is triggered - Temp = "Unable to get Weather";
Let's begin with my Model for the Town/s:
public class Town
{
[PrimaryKey]
public int ID { get; set; }
public string TownName { get; set; }
public string Temp { get; set; }
public string searchTime { get; set; }
public Town() { }
}
After creating this simple class, I am implementing a controller class for taking care of the Town data:
SQLiteConnection database;
public SearchHistoryDataController()
{
database = DependencyService.Get<ISQLite>().GetConnection();
database.CreateTable<Town>();
}
IEnumerable<Town> orderItemCollection;
public IEnumerable<Town> OrderItemCollection
{
get
{
if (orderItemCollection == null)
orderItemCollection = GetTowns();
return orderItemCollection;
}
}
public IEnumerable<Town> GetTowns()
{
// Changing the database table items as ObservableCollection
var table = (from i in database.Table<Town>() select i);
ObservableCollection<Town> TownList = new ObservableCollection<Town>();
foreach (var town in table)
{
TownList.Add(new Town()
{
ID = town.ID,
TownName = town.TownName,
Temp = town.Temp,
searchTime = town.searchTime
});
}
return TownList;
}
public Town GetTown(int id)
{
return database.Table<Town>().FirstOrDefault(t => t.ID == id);
}
public void DeleteTown(int id)
{
database.Delete<Town>(id);
}
public string AddTown(Town town)
{
var data = database.Table<Town>();
var d1 = data.Where(x => x.TownName == town.TownName && x.searchTime == town.searchTime).FirstOrDefault();
if (d1 == null)
{
database.Insert(town);
return "Successfully Added";
}
else
{
return "Invalid Town id Exist";
}
}
From here the last two things are:
Creating a Town, insert some information, storing the Town in the database and this is done in command whenever the "Show weather" button is clicked. There is the code:
//Get weather information for the Weather view
Temp = $"{weatherRoot?.MainWeather?.Temperature ?? 0}°C";
Condition = $"{weatherRoot?.Weather?[0]?.Description ?? string.Empty}";
Name = $"{weatherRoot.Name}, {weatherRoot.System.Country}";
Humidity = $"{weatherRoot.MainWeather.Humidity}%";
Pressure = $"{weatherRoot.MainWeather.Pressure} hpa";
Clouds = $"{weatherRoot.Clouds.CloudinessPercent}%";
Wind = $"{weatherRoot.Wind.Speed} m/s";
town.TownName = $"{weatherRoot.Name}";
town.Temp = Temp;
town.searchTime = DateTime.Parse(weatherRoot.Date).ToLocalTime().ToString("g");;
//history database push
try
{
SearchHistoryDataController searchHistoryDataController = new SearchHistoryDataController();
var returnvalue = searchHistoryDataController.AddTown(town);
if (returnvalue == "Sucessfully Added")
{
Console.WriteLine("Success");
}
else
{
Console.WriteLine("Fail");
}
}
catch (Exception es)
{
Debug.WriteLine(es.Message);
}
await TextToSpeech.SpeakAsync(Temp + " " + Condition);
IsBusy = false;
}
catch (Exception ex)
{
Temp = "Unable to get Weather";
Debug.WriteLine(ex.Message);
}
finally
{
IsBusy = false;
}
}
The final - XAML file which is structured like this:
<Grid>
<Image HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" Aspect="AspectFill" Source="gradientBack.png"/>
<StackLayout Padding="10" Spacing="10">
<StackLayout Orientation="Horizontal" Spacing="20" HorizontalOptions="Center" VerticalOptions="Start" Margin="5,20">
<Label Text="Search History" FontSize="20" FontAttributes="Bold" VerticalOptions="Center" TextColor="White" HorizontalOptions="Center"/>
</StackLayout>
<ListView ItemsSource="{Binding OrderItemCollection}"
HasUnevenRows="True"
CachingStrategy="RecycleElement"
IsPullToRefreshEnabled="True"
RefreshCommand="{Binding GetWeatherCommand}"
IsRefreshing="{Binding IsBusy, Mode=OneWay}"
RowHeight="66"
x:Name="ListViewTide">
<ListView.SeparatorColor>
<OnPlatform x:TypeArguments="Color" iOS="Transparent"/>
</ListView.SeparatorColor>
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Frame BackgroundColor="White" BorderColor="#F0F0F0" Padding="5" Margin="0,0,0,5" HasShadow="False">
<Grid HeightRequest="50" HorizontalOptions="FillAndExpand" VerticalOptions="Start">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label Text="{Binding DisplayTide}" TextColor="#800080" FontSize="15"
FontAttributes="Bold"
Style="{DynamicResource ListItemTextStyle}"/>
<StackLayout Grid.Column="2" Orientation="Horizontal" Margin="20,0" HorizontalOptions="End" VerticalOptions="Center">
<Label Text="{Binding DisplayTime}" TextColor="Black" FontSize="15" FontAttributes="None" VerticalOptions="Center"/>
</StackLayout>
</Grid>
</Frame>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</Grid>
The idea is to automatically update the History list when the "weather" button is clicked. The history will store the name of the town in the text field, the exact time and also the temperature.
The list won't be used for anything else and it is a tab.
Thank you very much!
I did solve the problem. First issue was with this line: town.searchTime = DateTime.Parse(weatherRoot.Date).ToLocalTime().ToString("g");
It was causing the command to fail because this date variable wasn't accessible. I fixed it with: town.searchTime = DateTime.Now.ToString();
Next - how to display data saved in the SQLite database:
Inserting Town into the database and then polling it out and store it under TownList:
town.TownName = $"{weatherRoot.Name}";
town.Temp = Temp;
town.searchTime = DateTime.Now.ToString();
//history database push
try
{
var returnvalue = searchHistoryDataController.AddTown(town);
if (returnvalue == "Sucessfully Added")
{
Console.WriteLine("Success");
}
else
{
Console.WriteLine("Fail");
}
}
catch (Exception es)
{
Debug.WriteLine(es.Message);
}
TownList = searchHistoryDataController.OrderItemCollection;
Last but not least the XAML and the SearchHistoryController:
<ListView ItemsSource="{Binding TownList}"
x:Name="ListViewTide">
<ListView.SeparatorColor>
<OnPlatform x:TypeArguments="Color" iOS="Transparent"/>
</ListView.SeparatorColor>
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Frame BackgroundColor="White" BorderColor="#F0F0F0" Padding="5" Margin="0,0,0,5" HasShadow="False">
<Grid HeightRequest="50" HorizontalOptions="FillAndExpand" VerticalOptions="Start">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label Text="{Binding TownName}" TextColor="#757575" FontSize="13" VerticalOptions="Center" Margin="20,0"/>
<Label Grid.Column="1" Text="{Binding searchTime}" TextColor="Black" FontSize="10" FontAttributes="None" HorizontalOptions="Center" VerticalOptions="Center"/>
<StackLayout Grid.Column="2" Orientation="Horizontal" Margin="20,0" HorizontalOptions="End" VerticalOptions="Center">
<Label Text="{Binding Temp}" TextColor="Black" FontSize="25" FontAttributes="Bold" VerticalOptions="Center"/>
</StackLayout>
</Grid>
</Frame>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
The method for pulling the info from SQL to List:
IEnumerable<Town> orderItemCollection;
public IEnumerable<Town> OrderItemCollection
{
get
{
if (orderItemCollection == null)
orderItemCollection = GetTowns();
return orderItemCollection;
}
}
public IEnumerable<Town> GetTowns()
{
// Changing the database table items as ObservableCollection
var table = (from i in database.Table<Town>() select i);
ObservableCollection<Town> TownList = new ObservableCollection<Town>();
foreach (var town in table)
{
TownList.Add(new Town()
{
ID = town.ID,
TownName = town.TownName,
Temp = town.Temp,
searchTime = town.searchTime
});
}
return TownList;
}
And just to remind everyone that the Model for your object, if it will be stored in SQL, always include AutoIncrement :
[PrimaryKey, AutoIncrement]
public int ID { get; set; }
Hope this will help someone. :)
i'm trying to bind a command to a button inside a listView, but without success. I follow all other answers posted here, like this one:
Xamarin Forms Button Command binding inside a ListView
The actual result is that nothing happens. A thing that i notice is that visual studio, when i type after x:Reference suggests me just GridWebcam, like if it doesn't see other reference elements. What can i do?
Here my code:
<ContentPage
...
x:Name="WebcamList">
<ContentPage.Resources>
...
<ContentPage.Content>
<ListView ItemsSource="{Binding ListOfWebcam}"
SeparatorVisibility="None"
CachingStrategy="RecycleElement"
RowHeight="250"
VerticalOptions="FillAndExpand"
x:Name="ListWebcam">
<ListView.Header>
<StackLayout x:Name="HeaderStackLayout"
Padding="5,25,0,30"
Orientation="Horizontal"
HorizontalOptions="FillAndExpand">
<Label x:Name="LabelHeader"
Text="Webcam:"
FontSize="Large"
FontAttributes="Bold"
TextColor="{x:Static statics:Palette.PrimaryColor}"
VerticalOptions="Center"
HorizontalOptions="Start" Margin="10,0,0,0"/>
</StackLayout>
</ListView.Header>
<ListView.ItemTemplate>
<DataTemplate>
<controls:ExtendedViewCell IsEnabled="False">
<controls:ExtendedViewCell.View>
<Grid x:Name="GridWebcam">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Frame Grid.Column="1"
Grid.RowSpan="2"
CornerRadius="20"
BackgroundColor="{x:Static statics:Palette.PrimaryColor}"
VerticalOptions="FillAndExpand"
HorizontalOptions="FillAndExpand"
HasShadow="True"
Margin="5,10">
<StackLayout>
<Label Text="{Binding t_str_vid,Converter={StaticResource WebcamNameConverter}}"
FontSize="Medium"
TextColor="White"
FontAttributes="Bold"
HorizontalOptions="FillAndExpand"
VerticalOptions="FillAndExpand">
</Label>
<Label TextColor="White"
FontSize="Medium"
Text="{Binding direzione,Converter={StaticResource DirectionToStringConverter}}"/>
<StackLayout Orientation="Horizontal">
<ffimageloading:CachedImage LoadingPlaceholder="Rolling.gif"
DownsampleToViewSize="False"
VerticalOptions="FillAndExpand"
HorizontalOptions="StartAndExpand"
Source="{Binding image1}"/>
<iconize:IconButton Text="fas-play-circle"
FontSize="50"
HorizontalOptions="EndAndExpand"
VerticalOptions="EndAndExpand"
TextColor="White"
Command="{Binding BindingContext.OpenVideoWebcamCommand, Source={x:Reference WebcamList}}"
CommandParameter="{Binding .}"
BackgroundColor="Transparent"/>
</StackLayout>
</StackLayout>
</Frame>
</Grid>
</controls:ExtendedViewCell.View>
</controls:ExtendedViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ContentPage.Content>
</ContentPage>
```
public class WebcamListViewModel : BaseViewModel
{
public ICommand OpenVideoWebcamCommand { set; get; }
private List<Webcam> _ListOfWebcam { get; set; }
public List<Webcam> ListOfWebcam
{
get { return _ListOfWebcam; }
set
{
_ListOfWebcam = value;
OnPropertyChanged();
}
}
private Task DownloadFramesTask;
CancellationTokenSource tokenSourceDownloadFrames = new CancellationTokenSource();
CancellationToken cancellationTokenDownloadFrames;
public WebcamListViewModel(INavigationService navigationService, IApiAutostradeManagerFactory apiAutostradeManagerFactory) : base(navigationService,apiAutostradeManagerFactory)
{
OpenVideoWebcamCommand = new Command<Webcam>(async (webcam) => {
await navigationService.NavigateAsync(Locator.WebcamVideoPopUpPage);
Messenger.Default.Send(new InfoWebcamVideoMessage(webcam.c_mpr, webcam.c_uuid, webcam.t_str_vid));
});
}
Well it could be related to this mysterious controls:ExtendedViewCell of yours :)
Also did you disable the ListView selection: <ListView ... SelectionMode="None" /> ?
As Roubachof said that I don't know if it is related to controls:ExtendedViewCell,please check if you have binding BindingContext, then you can take a look the following code:
<StackLayout>
<ListView x:Name="listview1" ItemsSource="{Binding persons}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Orientation="Horizontal">
<Label Text="{Binding Id}" />
<Label Text="{Binding name}" />
<Button
Command="{Binding BindingContext.command, Source={x:Reference listview1}}"
CommandParameter="{Binding Id}"
Text="Delete item" />
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
public partial class Page1 : ContentPage
{
public ObservableCollection<person> persons { get; set; }
public RelayCommand1 command { get; set; }
public Page1 ()
{
InitializeComponent ();
persons = new ObservableCollection<person>();
for(int i =0;i<20;i++)
{
person p = new person()
{
Id = i,
name = "cherry" + i
};
persons.Add(p);
command = new RelayCommand1(obj => method1((int)obj));
}
this.BindingContext = this;
}
public void method1(int Id)
{
persons.RemoveAt(Id);
//IEnumerable<person> list = persons.Where(x => x.Id == Id);
//foreach (person m in list)
//{
//}
}
}
public class person
{
public int Id { get; set; }
public string name { get; set; }
}
Personally I created a PARALLAX effect in my application, but within the effect (PARALLAX) I have a LISTVIEW that shows my content! only when the effect ends, the ListView SCROLL does not work and does not load all the content!
My Code
ParallaxControl.cs
public class ParallaxControl : ScrollView
{
public ParallaxControl()
{
Scrolled += (sender, e) => Parallax();
}
public static readonly BindableProperty ParallaxViewProperty =
BindableProperty.Create(nameof(ParallaxControl), typeof(View), typeof(ParallaxControl), null);
public View ParallaxView
{
get { return (View)GetValue(ParallaxViewProperty); }
set { SetValue(ParallaxViewProperty, value); }
}
double height;
public void Parallax()
{
if (ParallaxView == null || Device.RuntimePlatform == "Windows" || Device.RuntimePlatform == "WinPhone")
return;
if (height <= 0)
height = ParallaxView.Height;
ParallaxView.IsEnabled = false;
var y = -(int)((float)ScrollY / 2.5f);
if (y < 0)
{
//Move a imagem no eixo Y em uma fração da posição Y do ScrollView.
ParallaxView.Scale = 1;
ParallaxView.TranslationY = y;
}
else if (Device.RuntimePlatform == "iOS")
{
//Calcula uma escala que iguala a altura x scroll.
double newHeight = height + (ScrollY * -1);
ParallaxView.Scale = newHeight / height;
ParallaxView.TranslationY = -(ScrollY / 2);
}
else
{
ParallaxView.Scale = 1;
ParallaxView.TranslationY = 0;
}
}
}
Menu.XAML
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:DeLivre.Controls"
xmlns:cardView="clr-namespace:DeLivre"
xmlns:local1="clr-namespace:DeLivre.Controls"
xmlns:controls="clr-namespace:ImageCircle.Forms.Plugin.Abstractions;assembly=ImageCircle.Forms.Plugin"
x:Class="DeLivre.Views.Cardapio_Menu">
<ContentPage.ToolbarItems>
<ToolbarItem Name="MenuItem1" Order="Primary" Icon="ic_Carrinho" Clicked="MenuItem1_Clicked" Text="Carrinho" Priority="0" />
</ContentPage.ToolbarItems>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="200" x:Name="Row1Header"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid RowSpacing="0" ColumnSpacing="0" x:Name="HeaderView">
<StackLayout Padding="5,5,5,0" BackgroundColor="White" Orientation="Vertical">
<SearchBar x:Name="CardapioPesquisa" Placeholder="Pesquise por um lanche!" HeightRequest="40" BackgroundColor="White" TextChanged="CardapioPesquisa_TextChanged" />
<cardView:CardView x:Name="CardEstabelecimento" BackgroundColor="White" CardViewHasShadow="True" HeightRequest="220">
<cardView:CardView.CardViewContent>
<StackLayout x:Name="Stack"
Padding="2"
HorizontalOptions="Center"
Spacing="2"
VerticalOptions="Center">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="80"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="80"/>
</Grid.ColumnDefinitions>
<controls:CircleImage x:Name="ImagemEstab"
Grid.Column="1"
BorderColor="#EF5350"
BorderThickness="2"
Margin="1,0,1,0"
WidthRequest="100"
HeightRequest="100"
Aspect="AspectFit"/>
<Label x:Name="lblEntrega"
Grid.Column="0"
Text="ENTREGA"
TextColor="{StaticResource asphaltPrimary}"
FontSize="16" FontAttributes="Bold"/>
<Label x:Name="EstabLocal"
Grid.Column="0"
Margin="0,20"
TextColor="{StaticResource asphaltPrimary}"
FontSize="13"/>
<Label x:Name="EstabEntrega"
Grid.Column="0"
TextColor="{StaticResource asphaltPrimary}"
Margin="-15,35,0,0"
FontSize="13"
HorizontalTextAlignment="End"/>
<Label x:Name="EstabFuncionamento"
TextColor="{StaticResource asphaltPrimary}"
Grid.Column="2"
FontSize="13"/>
<Label x:Name="EstabFrete"
Grid.Column="2"
TextColor="{StaticResource asphaltPrimary}"
Margin="0,15,0,0"
FontSize="13" />
</Grid>
<Label x:Name="Descricao"
Text="Teste"
HorizontalTextAlignment="Center"
TextColor="{StaticResource asphaltPrimary}"
Margin="0,15,0,0"
FontSize="13" />
</StackLayout>
</cardView:CardView.CardViewContent>
</cardView:CardView>
</StackLayout>
</Grid>
<local:ParallaxControl x:Name="MainScroll" Grid.RowSpan="2">
<Grid RowSpacing="0" ColumnSpacing="0" >
<Grid.RowDefinitions>
<RowDefinition Height="200"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<StackLayout Grid.Row="1">
<ListView x:Name="ListaCardapio"
BackgroundColor="White"
SeparatorColor="{StaticResource grayPrimary}"
VerticalOptions="FillAndExpand"
HasUnevenRows="true"
ItemSelected="ListaCardapio_ItemSelected"
ItemsSource="{Binding Cardapios}"
CachingStrategy="RecycleElement">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout >
<StackLayout Orientation="Horizontal">
<controls:CircleImage x:Name="ImagemEstab"
Grid.Column="1"
Source="{Binding Icon_Lanche}"
BorderColor="#EF5350"
BorderThickness="2"
Margin="1,0,1,0"
WidthRequest="100"
HeightRequest="100"
Aspect="AspectFit"/>
<Label x:Name="NomeLanche" Text="{Binding Tipo}"
FontSize="13"
Margin="-5,7"
TextColor="{StaticResource asphaltPrimary}" />
</StackLayout>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</Grid>
</local:ParallaxControl>
</Grid>
Menu.XAML.CS
public Cardapio_Menu()
{
InitializeComponent ();
MainScroll.ParallaxView = HeaderView;
}
The effect works, the problem is that when the effect ends, the Listview scroll does not work and does not show the rest of my data!
Please Help
What you can do is replace the Xamarin.Forms ListView with the RepeaterView
RepeaterView is a control that inherits from stacklayout and works very much like ListView but does not have its own scroll, since you already have a scroll it should work great for you.
public class RepeaterView<T> : StackLayout where T : class
{
public static readonly BindableProperty HeaderTemplateProperty = BindableProperty.Create(nameof(HeaderTemplate), typeof(DataTemplate), typeof(RepeaterView<T>), default(DataTemplate));
public static readonly BindableProperty ItemTemplateProperty = BindableProperty.Create(nameof(ItemTemplate), typeof(DataTemplate), typeof(RepeaterView<T>), default(DataTemplate));
public static readonly BindableProperty ItemsSourceProperty = BindableProperty.Create(nameof(ItemsSource), typeof(IEnumerable<T>), typeof(RepeaterView<T>), null, defaultBindingMode: BindingMode.OneWay, propertyChanged: ItemsChanged);
public RepeaterView()
{
Spacing = 0;
}
public IEnumerable<T> ItemsSource
{
get { return (IEnumerable<T>)GetValue(ItemsSourceProperty); }
set { SetValue(ItemsSourceProperty, value); }
}
public DataTemplate ItemTemplate
{
get { return (DataTemplate)GetValue(ItemTemplateProperty); }
set { SetValue(ItemTemplateProperty, value); }
}
public DataTemplate HeaderTemplate
{
get { return (DataTemplate)GetValue(HeaderTemplateProperty); }
set { SetValue(HeaderTemplateProperty, value); }
}
protected virtual View ViewFor(T item)
{
View view = null;
if (ItemTemplate != null)
{
var content = ItemTemplate.CreateContent();
view = (content is View) ? content as View : ((ViewCell)content).View;
view.BindingContext = item;
}
return view;
}
protected View HeaderView()
{
View view = null;
if (HeaderTemplate != null)
{
var content = HeaderTemplate.CreateContent();
view = (content is View) ? content as View : ((ViewCell)content).View;
view.BindingContext = this.BindingContext;
}
return view;
}
private static void ItemsChanged(BindableObject bindable, object oldValue, object newValue)
{
var control = bindable as RepeaterView<T>;
if (control == null)
return;
control.Children.Clear();
IEnumerable<T> items = (IEnumerable<T>)newValue;
if (items.Any())
{
var header = control.HeaderView();
if (header != null)
control.Children.Add(header);
foreach (var item in items)
control.Children.Add(control.ViewFor(item));
}
}
}
For better understanding of how this control works you can check the guide here
Feel free to revert if you have queries
I have been trying to play around Xamarin Forms for a while and then came across to the Listview Grouping. I am not getting done its showing blank list. Please give me help to find where I am going wrong? Thanks in advance
However, My class domain looks like:
public class LineItemTaxDto
{
public int InvoiceLineItemId { get; set; }
public int InvoiceId { get; set; }
public int TaxId { get; set; }
public decimal TaxRate { get; set; }
public decimal TaxAmount { get; set; }
public string TaxName { get; set; }
}
My View Model property look like
public IEnumerable<KeyValuePair<string, ObservableCollection<LineItemTaxDto>>> ReceiptTaxList { get; set; }
My expecting result look like:
My xaml code below
<ListView x:Name="TaxListView"
Grid.Row="2"
Grid.Column="0"
Grid.ColumnSpan="2"
ItemsSource="{Binding Invoice.ReceiptTaxList}"
IsGroupingEnabled="true">
<ListView.GroupHeaderTemplate>
<DataTemplate>
<ViewCell>
<Label Text="{Binding Key}" />
</ViewCell>
</DataTemplate>
</ListView.GroupHeaderTemplate>
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid RowSpacing="0" ColumnSpacing="0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="150" />
</Grid.ColumnDefinitions>
<Label Text="{Binding TaxName}" Grid.Row="0" Grid.Column="0" FontSize="22" FontFamily="{x:Static resources:Fonts.ArialMTFont}" HorizontalOptions="End" HorizontalTextAlignment="End" Margin="0,5,0,5" />
<Label Grid.Row="0" Grid.Column="1" Text="{Binding TaxAmount, Converter={Helpers:CurrencyAmountConverter}}" FontSize="22" FontFamily="{x:Static resources:Fonts.ArialMTFont}" HorizontalOptions="End" Margin="0,5,0,5" />
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
EDIT
I am set value in view will appear method
Invoice.ReceiptTaxList = Invoice.InvoiceLineItems.Select(x => { return new KeyValuePair<string, ObservableCollection<LineItemTaxDto>>(x.TaxName, x.LineItemTaxes); });
The value was set properly
I have solved this tricky code not using grouping but now its work as expected
My ViewModel Code
var TaxList = Invoice.InvoiceLineItems.Where(x => x.TaxId > 1).GroupBy(y=>y.TaxId > 1).Select(x =>
{
return new KeyValuePair<LineItemTaxDto, ObservableCollection<LineItemTaxDto>>(new LineItemTaxDto()
{
InvoiceLineItemId = x.First().Id,
InvoiceId = x.First().InvoiceId,
TaxId = x.First().TaxId,
TaxRate = x.First().TaxRate,
TaxAmount = x.Sum(a=>a.TaxAmount),
TaxName = taxlabel + " (" + x.First().TaxName + ")"
}, x.First().LineItemTaxes);
});
var taxes = new ObservableCollection<LineItemTaxDto>();
foreach (var tax in TaxList.GroupBy(tax => tax.Key.TaxId).Select(grp => grp.First()))
{
taxes.Add(tax.Key);
foreach (var subtax in tax.Value.Where(x => x.TaxId > 0))
{
taxes.Add(subtax);
}
}
Invoice.ReceiptTaxList = taxes;
My Xaml Code
<ListView
x:Name="TaxListView"
ItemsSource="{Binding Invoice.ReceiptTaxList}"
Grid.Row="2"
Grid.Column="0"
Grid.ColumnSpan="2"
VerticalOptions="FillAndExpand"
HorizontalOptions="FillAndExpand"
SeparatorVisibility="None"
HasUnevenRows="false"
RowHeight="35"
>
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid RowSpacing="0" ColumnSpacing="0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="150" />
</Grid.ColumnDefinitions>
<Label Grid.Row="0" Grid.Column="0" Text="{Binding TaxName}" FontSize="22" FontFamily="{x:Static resources:Fonts.ArialMTFont}" HorizontalOptions="End" HorizontalTextAlignment="End" Margin="0,5,0,5" />
<Label Grid.Row="0" Grid.Column="1" Text="{Binding TaxAmount, Converter={Helpers:CurrencyAmountConverter}}" FontSize="22" FontFamily="{x:Static resources:Fonts.ArialMTFont}" HorizontalOptions="End" Margin="0,5,0,5" />
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
You must use a list of list as items source on listview to get it working. I recommend you to create a class with the properties you want to show at header like this:
public class TaxesObservableCollection<T> : ObservableCollection<T> // Think about to implement INotifyPropertyChanged too, I guess it will be usefull
{
public TaxesObservableCollection(IEnumerable<T> collection, string taxGroupName) : base(collection)
{
TaxGroupName = taxGroupName;
}
private bool taxGroupName;
public bool TaxGroupName
{
get { return taxGroupName; }
set { taxGroupName = value; }
}
}
In your view model:
public IEnumerable<TaxesObservableCollection<LineItemTaxDto>> ReceiptTaxList { get; set; }
Your ListView:
(...) // The rest of it as you did
<ListView.GroupHeaderTemplate>
<DataTemplate>
<ViewCell>
<Label Text="{Binding TaxGroupName}" />
</ViewCell>
</DataTemplate>
</ListView.GroupHeaderTemplate>
(...) // The rest of it as you did
And you fill it this way:
Invoice.ReceiptTaxList = Invoice.InvoiceLineItems.Select(x => { return new TaxesObservableCollection<LineItemTaxDto>(x.LineItemTaxes, x.TaxName); }).ToList();
It should works, please let me know if is that what you wanted.