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

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>

Related

Xamarin Forms Bind label text to value of dictionary

I have a problem. I am trying to bind my label to the value of my dictionary, so I would get a label foreach value in the dictionary. Now here is my code:
<ScrollView x:Name="categoryScrollView" HeightRequest="40" Orientation="Horizontal"
VerticalScrollBarVisibility="Never" HorizontalScrollBarVisibility="Never" HorizontalOptions="FillAndExpand">
<Frame CornerRadius="20" BackgroundColor="Black" BorderColor="DarkGray" HeightRequest="40">
<Label Text="{Binding categoryCollection[Value]}" FontSize="18" HorizontalTextAlignment="Center"
VerticalOptions="CenterAndExpand" HorizontalOptions="CenterAndExpand" TextColor="White" x:Name="txtCategory" />
</Frame>
</ScrollView>
And as you can see categoryCollection is my dictionary.
Here is the ViewModel:
private Dictionary<int, string> _categoryCollection;
public Dictionary<int, string> categoryCollection
{
get
{
return _categoryCollection;
}
set
{
if (_categoryCollection != value)
{
_categoryCollection = value;
OnPropertyChanged();
}
}
}
But after running the app, no text gets shown!?
What am I doing wrong?
so I would get a label foreach value in the dictionary.
Maybe you can use code to get each value from dictionary as follow :
Dictionary<int, string> dict = new Dictionary<int, string>(){{1,"One"},{2, "Two"},{3,"Three"}};
for (int i = 0; i < dict.Count; i++)
{
Console.WriteLine("Key: {0}, Value: {1}", dict.Keys.ElementAt(i), dict[ dict.Keys.ElementAt(i)]);
}
However this can not be displayed in Label or ScrollView dicrectly.
I suggest that using ListView to display loop list , ObservableCollection<Model> can be used as ItemSource for ListView .Then it will be easy displyed in each cell of listview .
You can create a Model class :
public class Employee
{
public int DisplayID {get; set;}
public string DisplayName {get; set;}
}
Then in ViewModel can set sample data :
ObservableCollection<Employee> employees = new ObservableCollection<Employee>();
public ObservableCollection<Employee> Employees { get { return employees; }}
public ViewModel()
{
// is set and the UI will react to changes
employees.Add(new Employee{ DisplayID = 1 , DisplayName="Rob Finnerty"});
employees.Add(new Employee{ DisplayID = 2 , DisplayName="Bill Wrestler"});
employees.Add(new Employee{ DisplayID = 3 , DisplayName="Dr. Geri-Beth Hooper"});
employees.Add(new Employee{ DisplayID = 4 , DisplayName="Dr. Keith Joyce-Purdy"});
employees.Add(new Employee{ DisplayID = 5 , DisplayName="Sheri Spruce"});
employees.Add(new Employee{ DisplayID = 6 , DisplayName="Burt Indybrick"});
}
Now in Xaml , add ListView in ContentPage :
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:constants="clr-namespace:XamarinFormsSample;assembly=XamarinFormsXamlSample"
x:Class="XamarinFormsXamlSample.Views.EmployeeListPage"
Title="Employee List">
<ListView x:Name="EmployeeView"
ItemsSource="{Binding Employees}">
<ListView.ItemTemplate>
<DataTemplate>
<TextCell Text="{Binding DisplayName}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ContentPage>
Not forget to bind ItemSource in ContenPage.cs :
public MainPage()
{
ViewModel viewmodel = new ViewModel();
EmployeeView.ItemsSource = viewmodel.employees ;
}
Here you can consider ViewModel as the Dictionary , employees contains the Key-Value data in each cell .In addition , you can add more property in Employee, then your cell will show much more styles.About custom data in listview , you can have a look at this doc to know more about it .
================================Update==================================
If need a horizonal listview , you can use CollectionView to implement it as follow :
<CollectionView ItemsSource="{Binding Monkeys}">
<CollectionView.ItemsLayout>
<LinearItemsLayout Orientation="Horizontal" />
</CollectionView.ItemsLayout>
<CollectionView.ItemTemplate>
<DataTemplate>
...
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
There is a sample for reference .

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

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.

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.

How to get name of a List<string> from List<List<string>> to bind it dynamically? [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 6 years ago.
Improve this question
I have a dynamic DataGrid in Which 1 column contains ComboBox template. Now I'll get 'N' number of comboboxes. Each ComboBox should have different ItemsSource. how can it be achieved?
My dynamic datagrid has the property ItemsSourceBinding. Now I need to provide a DataContext.BindingName to this property at runtime. How can it be achieved?
column.ItemsSourceBinding = new Binding()
{
Path = new System.Windows.PropertyPath("DataContext." + bindStreamList1),
RelativeSource = new RelativeSource(RelativeSourceMode.FindAncestor, typeof(DataGrid), 1)
};
In place of bindStreamList1 I need a name of List<string>. it may be from List<List<string>> or from Dictionary<string,List<string>>
I would recommend you to get familiar with MVVM pattern. There are tons of tutorials on the web.If you want to have two way binding you should implement the INotifyPropertyChanged interface. You can find yourself also very good tutorials on that. I also would recommend to do your bindings in XAML and not in code behind when ever possible.
Here is an example of what I guess you want:
XAML:
<Window x:Class="WpfApplication4.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" >
<Grid>
<DataGrid ItemsSource="{Binding }"
AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding Path=AvailableNames}"
SelectedItem="{Binding Path=Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Binding="{Binding Path=Name, Mode=OneWay}"
IsReadOnly="True" />
</DataGrid.Columns>
</DataGrid>
</Grid>
</Window>
Code behind:
public MainWindow()
{
InitializeComponent();
List<MyViewModel1> Items = new List<MyViewModel1>();
Items.Add(new MyViewModel1() { Name = "Leonardo" , AvailableNames = new List<string>() { "Leonardo", "Michael Angelo" } });
Items.Add(new MyViewModel1() { Name = "Michael Angelo", AvailableNames = new List<string>() { "Michael Angelo"} }); // can't make a leonardo out of this item
Items.Add(new MyViewModel1() { Name = "Master Splinter", AvailableNames = new List<string>() { "Master Splinter", "Jon Skeet" } }); // master stays master PERIOD ..... or become Jon Skeet
DataContext = Items;
}
And the MyViewModel1
public class MyViewModel1 : INotifyPropertyChanged
{
private List<string> _availableNames;
private string _name;
public event PropertyChangedEventHandler PropertyChanged;
public string Name
{
get
{
return _name;
}
set
{
_name = value;
OnPropertyChanged();
}
}
public List<string> AvailableNames
{
get
{
return _availableNames;
}
set
{
_availableNames = value;
OnPropertyChanged();
}
}
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}

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