How to fix the issue with binding generic list to listview in xamarin forms? - c#

I'm new to Xamarin forms. I tried to make a card view using ListView in Xamarin forms. Problem is that I am having issue with binding data from generic list.
The number of rows shown in listview are the same as the number of rows in the list but the property values don't bind to the XAML tags
I've tried it using both approaches by binding the data from code behind and directly to the item source in XAML
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MedicalCenter.Pages.MenuPage">
<ListView x:Name="MyListView"
ItemsSource="{Binding Items}"
ItemTapped="Handle_ItemTapped"
CachingStrategy="RecycleElement">
<ListView.ItemTemplate>
<DataTemplate>
<TextCell Text="{Binding lstHomeMenu}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ContentPage>
public partial class HomePage : ContentPage
{
public HomePage ()
{
InitializeComponent ();
this.BindingContext = new HomeMenuViewModel();
}
}
public class HomeMenuViewModel
{
public IList<HomeMenu> lstHomeMenu { get; set; }
public HomeMenuViewModel()
{
lstHomeMenu = new List<HomeMenu>();
GenerateCardModel();
}
private void GenerateCardModel()
{
lstHomeMenu.Add(new HomeMenu()
{
Title = "Request Appointment",
Icon = "icon_appointment.png",
BackgroundColor = "#479589"
});
lstHomeMenu.Add(new HomeMenu()
{
Title = "Order Prescription",
Icon = "icon_prescription.png",
BackgroundColor ="#4383D9"
});
}
}
public class HomeMenu
{
public string Title { get; set; }
public string Icon { get; set; }
public string BackgroundColor { get; set; }
}
}
When I bind a List to the ListView the data binds properly.
public class HomeMenuViewModel
{
public IList<string> lstHomeMenu { get; set; }
public HomeMenuViewModel()
{
lstHomeMenu = new List<string>();
GenerateCardModel();
}
private void GenerateCardModel()
{
lstHomeMenu = new ObservableCollection<string>
{
"1",
"2",
"3",
"4",
"5",
"6"
};
}
}
}
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MedicalCenter.Pages.MenuPage">
<ListView x:Name="MyListView"
ItemsSource="{Binding Items}"
ItemTapped="Handle_ItemTapped"
CachingStrategy="RecycleElement">
<ListView.ItemTemplate>
<DataTemplate>
<TextCell Text="{Binding .}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ContentPage>
but when I bind the List the binding doesn't work but I get the same number of rows in the ListView as the number of rows in the List.
Edited Question:
List View Result
This is the View I am getting. Seems like the list is binding but the properties inside the object are not binding

UPD:
I still think that your XAML is a problem.
I've recreated a project from the provided code. Please look at the lines 10 and 16 here.
==========
Original answer:
I think that problem is here:
ItemsSource="{Binding Items}"
Your HomeMenuViewModel contains public IList<string> lstHomeMenu { get; set; }, not Items.

Ok. I found the problem. During building the release version when I select
"Sdk and User Assemblies" for "Linking" menu in Android Options properties. after that the list view doesn't bind the data. When I select "Sdk assemblies only" it works fine. Don't know why this is happening.

Related

Simplest way to bind a ListViews ItemSource property to an ObservableCollection in the CodeBehind Class

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 1 hour ago.
Improve this question
I'm trying to find the simplest and most elegant way to do a simple data-binding of a ListView to an ObservableCollection in .NET MAUI. Maybe I'm a little spoiled by web-development (Angular), where data-binding is just so easy and performant.
The best solutions I have found are the following:
XAML only approach.
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="..."
x:Name="page"
xmlns:services="..."
Title="...">
<ListView BindingContext="{x:Reference page}" ItemsSource="{Binding observableCollection}">
</ListView>
</ContentPage>
I really like this approach, because I don't have to interact with the Markup Elements in the code-behind file. This is what I'd do in the other approach I have found.
XAML:
<ListView x:Name="listView" ItemsSource="{Binding observableCollection}"></ListView>
Code-Behind File:
public partial class ContentPage
{
// Edit: no need for a setter here
public ObservableCollection<type> observableCollection { get; }
public ContentPage()
{
InitializeComponent();
listView.BindingContext = this;
...
}
}
I'm wondering if there is a simpler or cleaner solution? Like where I can directly bind the ListViews ItemSource property to the ObservableCollection property of the code-behind class (like I'm used to do in web-development)?
We generally recommend using Model-View-ViewModel (MVVM).
You can refer to the following code:
create a ViewModelc class (e.g. MyViewModel.cs)
public class MyViewModel {
public ObservableCollection<Item> Items { get; set; }
public MyViewModel() {
Items = new ObservableCollection<Item>();
Items.Add(new Item { NumType = "S", LocationCode = "0001" });
Items.Add(new Item { NumType = "M", LocationCode = "0002" });
Items.Add(new Item { NumType = "L", LocationCode = "0003" });
Items.Add(new Item { NumType = "S", LocationCode = "0001" });
Items.Add(new Item { NumType = "M", LocationCode = "0002" });
Items.Add(new Item { NumType = "L", LocationCode = "0003" });
}
}
public class Item {
public string NumType { get; set; }
public string LocationCode { get; set; }
}
set the BindingContext for current page(MainPage.xaml) and set ItemsSource for the ListView.
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:MauiListViewApp"
x:Class="MauiListViewApp.MainPage">
<ContentPage.BindingContext>
<local:MyViewModel></local:MyViewModel>
</ContentPage.BindingContext>
<VerticalStackLayout
Spacing="25"
Padding="30,0"
VerticalOptions="Center">
<ListView ItemsSource="{Binding Items}" >
<ListView.ItemTemplate>
<DataTemplate>
<TextCell Text="{Binding NumType}"
Detail="{Binding LocationCode}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</VerticalStackLayout>
</ContentPage>

CarouselView.Content is empty

I try study is new componet CarouselView and с# in total.
But I have problem.
This code display is empty content in CarouselView. Why?
I send is fill code my colutions.
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:carousel="clr-namespace:CarouselView.FormsPlugin.Abstractions;assembly=CarouselView.FormsPlugin.Abstractions"
xmlns:local="clr-namespace:App6"
x:Class="App6.MainPage">
<StackLayout>
<Image Source="home.png"/>
<carousel:CarouselViewControl x:Name="MyCV" BackgroundColor="Black">
<carousel:CarouselViewControl.ItemTemplate>
<DataTemplate>
<StackLayout>
<Label Text="{Binding TestLine}"></Label>
</StackLayout>
</DataTemplate>
</carousel:CarouselViewControl.ItemTemplate>
</carousel:CarouselViewControl>
</StackLayout>
</ContentPage>
namespace App6
{
public partial class MainPage : ContentPage
{
class CustomCell
{
public string TestLine { get; set; }
}
public MainPage()
{
InitializeComponent();
List<CustomCell> myCarousel = new List<CustomCell>();
myCarousel.Add(new CustomCell { TestLine = "Line 1" });
myCarousel.Add(new CustomCell { TestLine = "Line 2" });
MyCV.ItemsSource = myCarousel;
}
}
}
Your code is correct, so your problem is in the visual layer. Try to define the height and width wherever possible and it should resolve the problem. CarouselView isn't the official control and isn't up to standards of Xamarin's built-in controls so it has some somewhat unexpected behaviors like this.

Xamarin Cross Platform Picker not binding correctly

I am new to Xamarin Cross Platform apps
I am trying to bind a Picker ItemSource to a List and display one property, with out success! My reference has been from here
Please can some one advise where my error is, my View or Xmal please (or probably both)
The List is a List of StdGrades defined as
namespace FitRestults_Dev1
{
class StdGrade
{
public string Gradelbl
{ get; set; }
public string Grade
{ get; set; }
public static List<StdGrade> Grades()
{
List<StdGrade> GradesList = new List<StdGrade>(){
new StdGrade(){ Gradelbl="10th Gup (White belt)", Grade="G10"},
new StdGrade(){ Gradelbl="9th Gup (Organge belt)", Grade="G9"},
new StdGrade(){ Gradelbl="8th Gup (Organge belt 1 tag)", Grade="G8"},
... };
return GradesList;
}
public List<StdGrade> GradesList => Grades();
public static string GetGrade(string Input)
{
List<StdGrade> GradesList = Grades();
var result = (from r in GradesList where r.Gradelbl == Input select r).First();
return result.Grade;
}
}
For the Content page I have defined a simple view as
namespace FitRestults_Dev1
{
class AddStudentView
{
List<StdGrade> _GradeList;
public List<StdGrade> GradeList
{ get => _GradeList;
set
{
_GradeList = StdGrade.Grades();
}
}
}
}
My content page xmal is
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="FitRestults_Dev1.AddStudent"
xmlns:src="clr-namespace:FitRestults_Dev1"
>
<ContentPage.BindingContext>
<src:AddStudentView/>
</ContentPage.BindingContext>
<ContentPage.Content>
<StackLayout Padding="10" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand">
<Grid>
…
<Picker x:Name="GradePicker" Title="Select a Grade" Grid.Row="2" Grid.Column="1" MinimumWidthRequest="100" FontSize="12"
ItemsSource="GradeList" SelectedIndex="0" ItemDisplayBinding="{Binding Gradelbl}">
</Picker>
</StackLayout>
</ContentPage.Content>
</ContentPage>
You are providing the itemsSource the incorrect way it should be a binding
ItemsSource={Binding GradeList}
Also Stop using Generic.List for binding, MVVM with Xamarin Forms should have ObservableCollections as it inherits from INotifyPropertyChanged and INotifyCollectionChanged

Xamarin Forms Refresh Layout after Observable Collection changes

I'm having a though time trying to "refresh" one of the views where I'm using a WrapLayout. Even though I change the items inside the ObservableCollection the page does not show the changes made.
Code below (some obfuscation needed due to confidentiality issues but I think the most important part is all there). Any help would be greatly appreciated.
Thanks.
ItemCardsViewModel.cs
// INotifyPropertyChanged implemented on BaseViewModel
public class ItemCardsViewModel : BaseViewModel
{
public ObservableCollection<ItemViewModel> Items { get; set; }
public ICommand RefreshCardsCommand { get; private set; }
public Action OnItemsChanged { get; internal set; }
public ItemCardsViewModel()
{
(...)
this.RefreshCardsCommand = new Command(RefreshCards);
}
private void RefreshCards(object x)
{
this.Items = new ObservableCollection<ItemViewModel>(
this.Items.Select(x =>
{
x.IsVisible = false;
return x;
}));
OnPropertyChanged(nameof(this.Items));
if (this.OnItemsChanged != null)
OnItemsChanged();
}
(...)
}
ItemCards.xaml.cs
public partial class ItemCards : ContentPage
{
ItemCardsViewModel ViewModel => ((ItemCardsViewModel)this.BindingContext);
public ItemCards()
{
InitializeComponent();
foreach (var item in ViewModel.Items)
{
var cell = new ItemView { BindingContext = item };
CardsLayout.Children.Add(cell);
}
ViewModel.OnItemsChanged += CardsLayout.ForceLayout;
}
}
ItemCards.xaml
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:prism="clr-namespace:Prism.Mvvm;assembly=Prism.Forms"
prism:ViewModelLocator.AutowireViewModel="True" (...)>
<ContentPage.Content>
<Grid>
(...)
<ScrollView Orientation="Vertical" Padding="0,5,0,5" Grid.Column="0" Grid.Row="2">
<ScrollView.Content>
<local:WrapLayout x:Name="CardsLayout" Spacing="5" HorizontalOptions="Start" VerticalOptions="Start" />
</ScrollView.Content>
</ScrollView>
</Grid>
</ContentPage.Content>
</ContentPage>
EDIT: Forgot to mention but I'm using Prism so the ViewModel is automatically wired up to the view.
EDIT 2: Just a quick update on this one... The issue persists even if I don't create a new Instance of the ObservableCollection on the RefreshCards method but rather loop through the records and set the IsVisible property one by one. Also tried to add a new ItemViewModel to the collection. Always the same result, no changes are shown on the page.

Load an image in a listview from URI xamarin forms

ok, so I have this listView which is supposed to show a bunch of items, each of which containing at least one photo.
The idea is to show the main photo in the listCell, and when an item is selected, its details are shown in a different Forms Page, and there its supposed to be able to access all its photos.
When the item doesn't have a photo, it will show a placeholder one from resources.
Problem: can't load the image from URI either binding the image source to a list property (from the viewModel) that contains the specific URI obj, or by binding it to the same property containing now strings, or by means of
<Image.Source>
<UriImageSource Uri="{Binding MainPhotoSource}" />
</Image.Source>
no matter. none of these seems to work.
already asked the Xamarin Team for help, and their answer was to come here, or go to the forums (which I already did, been waiting for almost two months, now, and the work needs to be delivered)...
any help, please?
EDIT:
Here's a piece of the ViewModel code.
In this first method, for each item I receive from the WCF, I add an equivalence in the format of this ItemDto obj to this ObservableCollection List.
// Sets this List observable collection to a new ItemDto obj,
// with certain properties from the Item.Find result[].
ObservableCollection<ItemDto> SetList(Item.Find[] result)
{
ObservableCollection<ItemDto> collection = new ObservableCollection<ItemDto>();
foreach (Item.Find item in result)
{
collection.Add(GetDto(item));
}
return collection;
}
// Gets a new ItemDto obj with the required fields from Item.Find obj.
ItemDto GetDto(Item.Find item)
{
return new ItemDto()
{
ID = item.ID,
UserID = item.User_ID,
MainPhotoSource = new Uri(_serverInbox + item.MediaItems[0].RelativeUrl),
Title = item.Title,
Description = item.Description,
Category = item.Category_Name,
DateCreated = GetFormatedDateCreated(item.DateCreated)
};
}
Uri property of UriImageSource requires an Uri rather than a string. But you can use a URI Bindable property in your View Model and bind to it:
Check this code
View Model
public string ProductNo
{
get { return _productNo}
set
{
if (_productNo != value)
{
_productNo = value;
RaisePropertyChanged();
RaisePropertyChanged(() => ThumbnailImageUri);
}
}
}
public Uri ThumbnailImageUri
{
get
{
if (_thumbnailImageUri == null)
{
_thumbnailImageUri = new Uri(String.Format("http://www.YOURWEBSITE.com/{0}.jpg", _productNo));
}
return _thumbnailImageUri;
}
}
View
<StackLayout BindingContext="{Binding SelectedProduct}">
<StackLayout Orientation="Horizontal">
<Image HorizontalOptions="EndAndExpand"
VerticalOptions="Center">
<Image.Source>
<UriImageSource Uri="{Binding ThumbnailImageUri}"/>
</Image.Source>
</Image>
<Label Text="{Binding ProductNo}"
Font="Bold, Large"
HorizontalOptions="StartAndExpand"
VerticalOptions="Center"/>
</StackLayout>
</StackLayout>
Here is, what works for me - hope this helps you
First my BindingContext:
public class ItemContainer
{
public ItemContainer()
{
Collection = SetList(new[] { "1", "2", "3", "4" });
}
ObservableCollection<ItemDto> SetList(string[] result)
{
ObservableCollection<ItemDto> collection = new ObservableCollection<ItemDto>();
foreach (string item in result)
{
collection.Add(GetDto(item));
}
return collection;
}
public ObservableCollection<ItemDto> Collection { get; set; }
ItemDto GetDto(string item)
{
return new ItemDto() { MainPhotoSource = new Uri(_serverInbox + item) };
}
}
My Page1.xaml looks like this:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="App1gcm.Page1">
<ListView ItemsSource="{Binding Collection}" VerticalOptions="Center" HorizontalOptions="Center" >
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Image Source="{Binding MainPhotoSource}" />
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ContentPage>
And I merge them on creating as MainPage in App.cs:
public App()
{
// The root page of your application
MainPage = new Page1
{
BindingContext = new ItemContainer()
};
}

Categories