Xamarin Forms Data Binding - xamarin

I am just starting out using Xamarin so I decided to make a project so I could get the hang of things. For my project, I have these details pages which based on which page you select will display a chart and some text with data specific to that page. I was able to display the text quite easily with bindingcontext but I am lost on how I would be able to pass the data for the charts. I am using microcharts to generate the charts.
ItemDetailPage.xaml.cs
using System;
using Xamarin.Forms;
using System.Collections.Generic;
using Microcharts;
using Entry = Microcharts.Entry;
using SkiaSharp;
namespace TestApp
{
public partial class ItemDetailPage : ContentPage
{
List<Microcharts.Entry> entries = new List<Microcharts.Entry>
{
new Entry(200)
{
Label = "January",
ValueLabel = "200",
Color = SKColor.Parse("#266489")
},
new Entry(400)
{
Label = "February",
ValueLabel = "400",
Color = SKColor.Parse("#68B9C0")
},
new Entry(-100)
{
Label = "March",
ValueLabel = "-100",
Color = SKColor.Parse("#90D585")
}
};
ItemDetailViewModel viewModel;
// Note - The Xamarin.Forms Previewer requires a default, parameterless constructor to render a page.
public ItemDetailPage()
{
Initialize(null);
}
public ItemDetailPage(ItemDetailViewModel viewModel)
{
Initialize(viewModel);
}
public void Initialize(ItemDetailViewModel viewModel) {
InitializeComponent();
Chart1.Chart = new RadialGaugeChart { Entries = entries };
if (viewModel == null) {
var item = new Item
{
Text = "Item 1",
Description = "This is an item description."
};
viewModel = new ItemDetailViewModel(item);
Console.WriteLine(item.Text);
}
BindingContext = viewModel;
}
}
}
ItemDetailPage.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:forms = "clr-namespace:Microcharts.Forms;assembly=Microcharts.Forms" x:Class="TestApp.ItemDetailPage" Title="{Binding Title}">
<ContentPage.Content>
<StackLayout>
<Label TextColor="#77d065" FontSize = "20" Text="{Binding Item.Text}" />
<Label TextColor="#77d065" FontSize = "20" Text="{Binding Item.Info}" />
<forms:ChartView x:Name="Chart1" HeightRequest = "150"/>
</StackLayout>
</ContentPage.Content>
</ContentPage>
ItemDetailViewModel.cs
using System;
namespace TestApp
{
public class ItemDetailViewModel : BaseViewModel
{
public Item Item { get; set; }
public ItemDetailViewModel(Item item = null)
{
Title = item?.Text;
Item = item;
}
}
}
ItemsPage.xaml.cs
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Xamarin.Forms;
namespace TestApp
{
public partial class ItemsPage : ContentPage
{
ItemsViewModel viewModel;
public ItemsPage()
{
InitializeComponent();
BindingContext = viewModel = new ItemsViewModel();
}
async void OnItemSelected(object sender, SelectedItemChangedEventArgs args)
{
var item = args.SelectedItem as Item;
if (item == null)
return;
await Navigation.PushAsync(new ItemDetailPage(new ItemDetailViewModel(item)));
// Manually deselect item
ItemsListView.SelectedItem = null;
}
async void AddItem_Clicked(object sender, EventArgs e)
{
await Navigation.PushAsync(new NewItemPage());
}
protected override void OnAppearing()
{
base.OnAppearing();
if (viewModel.Items.Count == 0)
viewModel.LoadItemsCommand.Execute(null);
}
}
}
Items List
items = new List<Item>();
var mockItems = new List<Item>
{
new Item { Id = Guid.NewGuid().ToString(), Text = "First item", Description="This is an item description.", Info = "Some More Info", label1 = "value1" },
new Item { Id = Guid.NewGuid().ToString(), Text = "Second item", Description="This is an item description.",label1 = "value2" },
new Item { Id = Guid.NewGuid().ToString(), Text = "Third item", Description="This is an item description.", label1 = "value3" },
new Item { Id = Guid.NewGuid().ToString(), Text = "Fourth item", Description="This is an item description." },
new Item { Id = Guid.NewGuid().ToString(), Text = "Fifth item", Description="This is an item description." },
new Item { Id = Guid.NewGuid().ToString(), Text = "Sixth item", Description="This is an item description." },
};
So like my goal for example would be to have the label for one of the entries in the chart correspond to the label1 value of item.
If anybody could point me in the right direction on how to accomplish this, that would be very nice.

You have got the ItemDetailViewModel in the method Initialize(...), so you can use your item passed from ItemsPage to customize your charts like:
// Do not use the default entries
Chart1.Chart = new RadialGaugeChart(); //{ Entries = entries };
// Modify the entry in entries(for instance the first one)
var singleEntry = entries[0];
Microcharts.Entry entry = new Microcharts.Entry(float.Parse(viewModel.Item.label1))
{
Label = singleEntry.Label,
ValueLabel = viewModel.Item.label1,
Color = singleEntry.Color
};
entries[0] = entry;
// At last initialize the chart's Entries
Chart1.Chart.Entries = entries;

just add this line and set the value the size to say in this example is 20f, but it can be anyone who serves you
Chart1.Chart.LabelTextSize = 30f;

Related

Bind button in DataTemplate C#

I'm trying to bind a button in a DataTemplate that is being used by a collection view.
This is my button inside the DataTemplate, and I want to bind it to a command in "DetailDayPageViewModel".
Button viewComment = new Button()
{
TextColor = Color.DodgerBlue,
HorizontalOptions = LayoutOptions.Start,
VerticalOptions = LayoutOptions.Start,
FontSize = 16
};
// this binding does not work
viewComment.SetBinding(Button.CommandProperty, nameof(DetailDayPageViewModel.ViewComment));
Use RelativeBinding for binding values of Page's BindingContext to property inside DataTemplate.
There are two ways to do this:
1: Binding through the ViewModel. ReltiveBinding of Mode FindAncestorBindingContext
public class ItemView : Grid
{
public ItemView()
{
Button clickButton = new Button() { Text = "Hi there" };
clickButton.SetBinding(
Button.CommandProperty,
new Binding(
"ItemClickCommand",
source: new RelativeBindingSource(
RelativeBindingSourceMode.FindAncestorBindingContext,
typeof(ViewModel))
));
this.Children.Add(clickButton);
}
}
2: Binding through the Parent view BindingContext:
public class ItemView : Grid
{
public ItemView()
{
Button clickButton = new Button() { Text = "Hi there" };
clickButton.SetBinding(
Button.CommandProperty,
new Binding(
"BindingContext.ItemClickCommand",
source: new RelativeBindingSource(
RelativeBindingSourceMode.FindAncestor,
typeof(CollectionView))
));
this.Children.Add(clickButton);
}
}
Please do check and see if it helps!! Comment for any queries.
I think you can take a look to this sample:
TestBinding
It refers to a ListView, but it should be applicated to a CollectionView.
You need to set the the "source":
TapGestureRecognizer tgrUpDown2 = new TapGestureRecognizer();
tgrUpDown2.SetBinding(TapGestureRecognizer.CommandProperty, new Binding("BindingContext.UpDown2Command", source: this));
tgrUpDown2.SetBinding(TapGestureRecognizer.CommandParameterProperty, ".");
then in your Model, you have the "parameter" passend...
this.UpDown2Command = new Command(async (object obj) =>
{
try
{
if (_isTapped)
return;
if (obj != null)
System.Diagnostics.Debug.WriteLine("Obj is not null");
else
System.Diagnostics.Debug.WriteLine("Obj IS null");
_isTapped = true;
int idx = List.IndexOf((Model)obj);
List[idx].Checked1 = !List[idx].Checked1;
_isTapped = false;
}
catch (Exception ex)
{
_isTapped = false;
await Application.Current.MainPage.DisplayAlert("Attention", ex.Message, "Ok");
}
});
This is a useful link I have found some years ago:
listview-in-xamarin-forms-in-mvvm
If you want a "class" to define your ViewCell, you can assign the class in this way:
lv.ItemTemplate = new DataTemplate(() =>
{
return new MyViewCell(lv);
});
Where MyViewCell is something like:
class MyViewCell : ViewCell
{
public MyViewCell(ListView lv)
{
StackLayout slView = new StackLayout();
slView.SetBinding(StackLayout.BackgroundColorProperty, "BackgroundColor");
Label lDesc = new Label();
lDesc.SetBinding(Label.TextProperty, "Description", stringFormat: "DESCRIPTION: {0}");
lDesc.SetBinding(Label.TextColorProperty, "TextColor");
// LABEL QTY
TapGestureRecognizer tgrQty = new TapGestureRecognizer();
tgrQty.SetBinding(TapGestureRecognizer.CommandProperty, new Binding("BindingContext.QtyCommand", source: lv));
tgrQty.SetBinding(TapGestureRecognizer.CommandParameterProperty, ".");
....
....
View = slView;
}
}
You can pass "ListView" in the constructor so you can use it in "source" binding property.

Xamarin Forms: Flowlistview item tapping issue

I am implementing a game in my xamarin forms application, a name match game. It has two lists: one for showing the images and the other one for showing the names. The player taps the image from the top list and then taps the name from the bottom list(or name first then image). If they match the player gets points and tapped image and name will be removed from lists.
I am using flowlistview for showing the image list and name list because I need to show multiple items in a row. When tapping the image and name I have done the matching and remove the image and name if they matched. But I need to highlight the selected image or selected name when tapping and disable selection for other items. I have done highlighting feature using this thread, but it is not working perfectly. Sometimes multiple images are highlighting and sometimes when selecting an image on the top name on the below list is automatically highlighting.
I have created a sample project and uploaded it here. Please help me to complete this game. We have this game on our website, https://www.catholicbrain.com/edu-namematch/39524/1/the-two-great-commandments , please have a look at it for the working of the game. I will give you login details by DM.
Edit 1
#LucasZhang-MSFT I am accepting that, but the current question is different. It has 2 different flowlistviews. On the top an image list and on the bottom a name list. This is a simple game for kids, the player taps the image from the top list and then taps the name from the bottom list(or name first then image). If they match the player gets points and tapped image and name will be removed from lists. When not match I reset the items background colors like below:
foreach (var item1 in ImageItems)
{
item.BGColor = Color.White;
}
foreach (var item2 in NameItems)
{
item.BGColor = Color.White;
}
OnPropertyChanged("NameMatchImagItems");
OnPropertyChanged("NameMatchNameItems");
After this point, multiple images are highlighting and sometimes when selecting an image on the top name on the below list is automatically highlighting.
If you have time, can you please download the sample and have a look? I tried my best, but no luck.
Cause:
You set the ItemsSource of two flowlistview with the same source _allItems !!
Solution:
in xaml
<ContentPage.Content>
<StackLayout Orientation="Vertical">
<!--imageflowlistview-->
<flv:FlowListView
x:Name="NameMatchImageList"
FlowItemTappedCommand="{Binding ImageItemTappedCommand}"
FlowItemsSource="{Binding ImageItems}"
FlowColumnCount="2"
FlowLastTappedItem="{Binding LastImageTappedItem}"
HasUnevenRows="True">
<flv:FlowListView.FlowColumnTemplate>
<DataTemplate>
<StackLayout BackgroundColor="{Binding BGColor}" Orientation="Vertical">
<Frame
Padding="5"
Margin="5"
HasShadow="False"
BorderColor="#a4e6f9"
CornerRadius="15">
<ffimageloading:CachedImage
Source="{Binding imageUrl, Converter={StaticResource urlJoinConverter}}"
HorizontalOptions="FillAndExpand"
VerticalOptions="FillAndExpand"
HeightRequest="100"
Aspect="AspectFill"/>
</Frame>
</StackLayout>
</DataTemplate>
</flv:FlowListView.FlowColumnTemplate>
</flv:FlowListView>
<!--NamesFlowlistview-->
<flv:FlowListView
x:Name="NameMatchNameList"
FlowItemTappedCommand="{Binding NameItemTappedCommand}"
FlowItemsSource="{Binding NameItems}"
FlowColumnCount="2"
FlowLastTappedItem="{Binding LastNameTappedItem}"
HasUnevenRows="True">
<flv:FlowListView.FlowColumnTemplate>
<DataTemplate>
<StackLayout Orientation="Vertical">
<Label
TextColor="Black"
FontSize="Large"
BackgroundColor="{Binding BGColor}"
HorizontalTextAlignment="Center"
VerticalTextAlignment="Center"
Text="{Binding name}"/>
</StackLayout>
</DataTemplate>
</flv:FlowListView.FlowColumnTemplate>
</flv:FlowListView>
</StackLayout>
</ContentPage.Content>
in code behind
namespace FlowListView_Tap
{
class NameMatchViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public ObservableCollection<NameMatchList> imageItems;
public ObservableCollection<NameMatchList> ImageItems { get
{
return imageItems;
}
set {
if(value!=null)
{
imageItems = value;
OnPropertyChanged("ImageItems");
}
}
}
public ObservableCollection<NameMatchList> nameItems;
public ObservableCollection<NameMatchList> NameItems
{
get
{
return nameItems;
}
set
{
if (value != null)
{
nameItems = value;
OnPropertyChanged("NameItems");
}
}
}
public bool isImageSelected = false;
public bool isNameSelected = false;
public ICommand NameItemTappedCommand { get; set; }
public ICommand ImageItemTappedCommand { get; set; }
private NameMatchList lastImageTappedItem;
public NameMatchList LastImageTappedItem
{
get
{
return lastImageTappedItem;
}
set
{
if(value!=null)
{
lastImageTappedItem = value;
OnPropertyChanged("LastImageTappedItem");
}
}
}
private NameMatchList lastNameTappedItem;
public NameMatchList LastNameTappedItem
{
get
{
return lastNameTappedItem;
}
set
{
if (value != null)
{
lastNameTappedItem = value;
OnPropertyChanged("LastNameTappedItem");
}
}
}
public NameMatchViewModel()
{
ImageItemTappedCommand = new Command((obj) => {
try
{
//reset the bg color
foreach (var item in ImageItems)
{
item.BGColor = Color.White;
}
NameMatchList imageList = obj as NameMatchList;
int index = ImageItems.IndexOf(imageList);
imageList.BGColor = Color.Red;
///ImageItems.RemoveAt(index);
//ImageItems.Insert(index, imageList);
//Storing name and imageurl to local db
Application.Current.Properties["NameMatchImageList_Image"] = imageList.imageUrl;
Application.Current.Properties["NameMatchImageList_Name"] = imageList.name;
Application.Current.Properties["ImageItem"] = imageList;
isImageSelected = true;
if (isImageSelected && isNameSelected)
{
//If both image and name selected by player startes checking the matching
StartNameMatchCheck(imageList);
}
}
catch (Exception imagetapEx)
{
Debug.WriteLine("imagetapEx:>>" + imagetapEx);
}
});
NameItemTappedCommand = new Command((obj) => {
try
{
//reset the bg color
foreach (var item in NameItems)
{
item.BGColor = Color.White;
}
NameMatchList nameList = obj as NameMatchList;
int index = NameItems.IndexOf(nameList);
nameList.BGColor = Color.Red;
//NameItems.RemoveAt(index);
//NameItems.Insert(index, nameList);
//Storing name and imageurl to local db
Application.Current.Properties["NameMatchNameList_Image"] = nameList.imageUrl;
Application.Current.Properties["NameMatchNameList_Name"] = nameList.name;
Application.Current.Properties["NameItem"] = nameList;
isNameSelected = true;
if (isImageSelected && isNameSelected)
{
//If both image and name selected by player startes checking the matching
StartNameMatchCheck(nameList);
}
}
catch (Exception nametapEx)
{
Debug.WriteLine("nametapEx:>>" + nametapEx);
}
});
}
public async void StartNameMatchCheck(NameMatchList item)
{
isImageSelected = false;
isNameSelected = false;
//Fetching data from local db
string NameMatchImageListImage = Application.Current.Properties["NameMatchImageList_Image"].ToString();
string NameMatchImageListName = Application.Current.Properties["NameMatchImageList_Name"].ToString();
string NameMatchNameListImage = Application.Current.Properties["NameMatchNameList_Image"].ToString();
string NameMatchNameListName = Application.Current.Properties["NameMatchNameList_Name"].ToString();
//Match check
if ((NameMatchImageListImage == NameMatchNameListImage) && (NameMatchImageListName == NameMatchNameListName))
{
await Application.Current.MainPage.DisplayAlert("Alert", "Success", "Ok");
//Removing the items from list if they match
ImageItems.Remove(LastImageTappedItem);
NameItems.Remove(LastNameTappedItem);
LastImageTappedItem = null;
LastNameTappedItem = null;
}
else
{
await Application.Current.MainPage.DisplayAlert("Alert", "Failed", "Ok");
//resetting the colors
LastImageTappedItem.BGColor = Color.White;
LastNameTappedItem.BGColor = Color.White;
}
}
public async void CallNameMatch()
{
try
{
//HttpClient client = new HttpClient();
//var nameMatchResponse = await client.GetAsync("");
//if (nameMatchResponse.IsSuccessStatusCode)
//{
// var Response = await nameMatchResponse.Content.ReadAsStringAsync();
// var imageResponse = JsonConvert.DeserializeObject<Games>(Response.ToString());
// var namematch = JsonConvert.DeserializeObject<Games>(Response.ToString());
ImageItems = new ObservableCollection<NameMatchList>();
ImageItems.Add(new NameMatchList() { name = "Comfort the Sorrowing", imageUrl = "/cbrain-app/files/doc-lib/2018/02/22/11/46/06/971/head/Comfort the Sorrowing.png" });
ImageItems.Add(new NameMatchList() { name = "Giving Food To The Hungry", imageUrl = "/cbrain-app/files/doc-lib/2018/02/22/11/46/23/784/head/Giving Food To The Hungry.png" });
ImageItems.Add(new NameMatchList() { name = "Pray for the Living and The Dead", imageUrl = "/cbrain-app/files/doc-lib/2018/02/22/11/46/39/707/head/Pray for the Living and The Dead.png" });
ImageItems.Add(new NameMatchList() { name = "To bury the Dead", imageUrl = "/cbrain-app/files/doc-lib/2018/02/22/11/46/54/828/head/To bury the Dead.png" });
//shuffling image list
//Random r1 = new Random();
//int randomIndex1 = 0;
//while (ImageItems.Count > 0)
//{
// randomIndex1 = r1.Next(0, ImageItems.Count);
// ImageItems[randomIndex1].BGColor = Color.White;
// ImageItems.Add(ImageItems[randomIndex1]);
// ImageItems.RemoveAt(randomIndex1);
//}
//NameMatchImagItems = new ObservableCollection<NameMatchList>(ImageItems);
NameItems = new ObservableCollection<NameMatchList>();
NameItems.Add(new NameMatchList() { name = "To bury the Dead", imageUrl = "/cbrain-app/files/doc-lib/2018/02/22/11/46/54/828/head/To bury the Dead.png" });
NameItems.Add(new NameMatchList() { name = "Pray for the Living and The Dead", imageUrl = "/cbrain-app/files/doc-lib/2018/02/22/11/46/39/707/head/Pray for the Living and The Dead.png" });
NameItems.Add(new NameMatchList() { name = "Comfort the Sorrowing", imageUrl = "/cbrain-app/files/doc-lib/2018/02/22/11/46/06/971/head/Comfort the Sorrowing.png" });
NameItems.Add(new NameMatchList() { name = "Giving Food To The Hungry", imageUrl = "/cbrain-app/files/doc-lib/2018/02/22/11/46/23/784/head/Giving Food To The Hungry.png" });
//shuffling name list
//Random r2 = new Random();
//int randomIndex2 = 0;
//while (NameItems.Count > 0)
//{
// randomIndex2 = r2.Next(0, NameItems.Count);
// NameItems[randomIndex2].BGColor = Color.White;
// NameItems.Add(NameItems[randomIndex2]);
// NameItems.RemoveAt(randomIndex2);
//}
// NameMatchNameItems = new ObservableCollection<NameMatchList>(NameItems);
//}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine("NMException:>" + ex);
}
}
protected void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public void ShowAlert(string message)
{
Device.BeginInvokeOnMainThread(async () => {
await Application.Current.MainPage.DisplayAlert("Alert", message, "Ok");
});
}
}
}

Telerik UI for Xamarin: Property not found for RadListView Binding

I am working on an Android app with Xamarin, using Telerik UI.
The following error is raised when trying to bind a property to a Telerik ListViewTextCell in a RadListView:
[0:] Binding: 'Author' property not found on 'Book', target property: 'Telerik.XamarinForms.DataControls.ListView.ListViewTextCell.Detail'
This happens in even the most minimal cases. Below is an example, drawn largely from the ListView documentation itself.
PageTest.cs:
using System.Collections.Generic;
using System.ComponentModel;
using Xamarin.Forms;
using Telerik.XamarinForms.DataControls;
using Telerik.XamarinForms.DataControls.ListView;
namespace MyTelerikApp
{
[DesignTimeVisible(false)]
public partial class PageTest : ContentPage
{
public PageTest()
{
InitializeComponent();
}
protected override void OnAppearing()
{
base.OnAppearing();
var listView = new RadListView
{
ItemsSource = new ViewModel().Source,
ItemTemplate = new DataTemplate(() =>
{
var cell = new ListViewTextCell
{
TextColor = Color.Black,
DetailColor = Color.Gray,
};
cell.SetBinding(ListViewTextCell.TextProperty, new Binding(nameof(Book.Title)));
cell.SetBinding(ListViewTextCell.DetailProperty, new Binding(nameof(Book.Author)));
return cell;
}),
LayoutDefinition = new ListViewLinearLayout { ItemLength = 70 }
};
MainPageContent.Children.Add(listView);
}
}
}
public class Book
{
public string Title { get; set; }
public string Author { get; set; }
}
public class ViewModel
{
public ViewModel()
{
this.Source = new List<Book>{
new Book{ Title = "The Fault in Our Stars ", Author = "John Green"},
new Book{ Title = "Divergent", Author = "Veronica Roth"},
new Book{ Title = "Gone Girl", Author = "Gillian Flynn"},
new Book{ Title = "Clockwork Angel", Author = "Cassandra Clare"},
new Book{ Title = "The Martian", Author = "Andy Weir"},
new Book{ Title = "Ready Player One", Author = "Ernest Cline"},
new Book{ Title = "The Lost Hero", Author = "Rick Riordan"},
new Book{ Title = "All the Light We Cannot See", Author = "Anthony Doerr"},
new Book{ Title = "Cinder", Author = "Marissa Meyer"},
new Book{ Title = "Me Before You", Author = "Jojo Moyes"},
new Book{ Title = "The Night Circus", Author = "Erin Morgenstern"},
};
}
public List<Book> Source { get; set; }
}
PageText.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:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
x:Class="GeoGIS.views.PageTest">
<StackLayout x:Name="MainPageContent">
</StackLayout>
</ContentPage>
After some searching, it seems that a BindingContext is necessary, but I couldn't get that to work either.
I didn't found BindingContext from your code.And I guess you confused the two usages of ContentPage(XAML and C# ).
When we created a contentpage,we have two choices(XAML and C#) as follows:
1.When we choose the ContentPage(c#),in this case, there is no xaml.And we can do like this:
public class TestPage1 : ContentPage
{
public TestPage1 ()
{
var listView = new RadListView
{
BackgroundColor = Color.White,
ItemsSource = new ViewModel().Source,
ItemTemplate = new DataTemplate(() =>
{
var cell = new ListViewTextCell
{
TextColor = Color.Black,
DetailColor = Color.Gray,
};
cell.SetBinding(ListViewTextCell.TextProperty, new Binding(nameof(Book.Title)));
cell.SetBinding(ListViewTextCell.DetailProperty, new Binding(nameof(Book.Author)));
return cell;
}),
LayoutDefinition = new ListViewLinearLayout { ItemLength = 70 },
};
Content = new StackLayout {
Children = {
listView
}
};
}
}
2.When we choose the ContentPage,in this case, code has xaml.We can do like this.
Put the followinging code in your xaml
<StackLayout>
<telerikDataControls:RadListView ItemsSource="{Binding Source}" BackgroundColor="White" x:Name="listView">
<telerikDataControls:RadListView.BindingContext>
<local:ViewModel />
</telerikDataControls:RadListView.BindingContext>
<telerikDataControls:RadListView.ItemTemplate>
<DataTemplate>
<telerikListView:ListViewTextCell Text="{Binding Title}" Detail="{Binding Author}" TextColor="Black" DetailColor="Gray" />
</DataTemplate>
</telerikDataControls:RadListView.ItemTemplate>
<telerikDataControls:RadListView.LayoutDefinition>
<telerikListView:ListViewLinearLayout ItemLength="70" />
</telerikDataControls:RadListView.LayoutDefinition>
</telerikDataControls:RadListView>
</StackLayout>
And remove the method OnAppearing() from your code.
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
//BindingContext = new ViewModel();
}
protected override void OnAppearing()
{
base.OnAppearing();
}
}
From above code,we can found the BindingContext,it is necessary.
<telerikDataControls:RadListView.BindingContext>
<local:ViewModel />
</telerikDataControls:RadListView.BindingContext>
And we can also BindingContext like this(Any one is ok.):
BindingContext = new ViewModel();
The result is the same:

Xamarin Forms Collection view ItemsSource Binding not updating the UI

Xamarin.Forms 4.0 collection view ItemsSource binding is not working as expected if I set the binding in code behind. The items are displayed based on the initial value of the source collection but, the UI is not updating when the source collection is updated. Same is working if I set the binding in xaml.
Code behind:
public MainPage()
{
this.InitializeComponent();
this.BindingContext = this.mainViewModel = new MainViewModel();
CollectionView courseCollectionView = new CollectionView
{
ItemSizingStrategy = ItemSizingStrategy.MeasureFirstItem,
ItemTemplate = new DataTemplate(typeof(ItemView))
};
courseCollectionView.SetBinding(CollectionView.ItemsSourceProperty,
new Binding() { Source = this.mainViewModel.CountryNames, Mode =
BindingMode.TwoWay });
courseCollectionView.ItemsLayout = new GridItemsLayout(4,
ItemsLayoutOrientation.Vertical);
this.CoursesStack.Children.Clear();
this.CoursesStack.Children.Add(courseCollectionView);
}
View Model Property which is using for ItemsSource Binding:
ObservableCollection<Country> countryNames;
public ObservableCollection<Country> CountryNames
{
get => this.countryNames;
set
{
this.countryNames = value;
RaisePropertyChanged("CountryNames");
}
}
Expected: View should be updated as per the changes made to the ObsevableCollection (add/delete items from collection) which is bound to the ItemsSource Property.
Actual: View is not updated with changes to the ObservableCollection.
I believe your binding is wrong. Try:
courseCollectionView.SetBinding(CollectionView.ItemsSourceProperty, nameof(mainViewModel.CountryNames));
You need to specify the Path (mainViewModel.CountryNames) and not the Source
About updating UI when using CollectionView, I do one sample that you can take a look:
public partial class MainPage : ContentPage
{
public mainvidemodel viewmodel { get; set; }
public MainPage()
{
InitializeComponent();
viewmodel = new mainvidemodel();
this.BindingContext = viewmodel;
CollectionView collectionView = new CollectionView();
collectionView.SetBinding(ItemsView.ItemsSourceProperty, "countries");
collectionView.ItemTemplate = new DataTemplate(() =>
{
StackLayout stacklayout = new StackLayout();
Label label1 = new Label();
label1.SetBinding(Label.TextProperty,"Id");
Label label2 = new Label();
label2.SetBinding(Label.TextProperty, "Name");
Label label3 = new Label();
label3.SetBinding(Label.TextProperty, "caption");
stacklayout.Children.Add(label1);
stacklayout.Children.Add(label2);
stacklayout.Children.Add(label3);
return stacklayout;
});
Button btn = new Button() { Text = "btn", WidthRequest = 200, HeightRequest = 50 };
btn.Clicked += Btn_Clicked;
stacklayout1.Children.Add(collectionView);
stacklayout1.Children.Add(btn);
}
private void Btn_Clicked(object sender, EventArgs e)
{
viewmodel.countries.Add(new Country() { Id = 8, Name = "country8", caption = "caption 8" });
}
}
public class mainvidemodel
{
public ObservableCollection<Country> countries { get; set; }
public mainvidemodel()
{
countries = new ObservableCollection<Country>()
{
new Country(){Id=1,Name="country1",caption="caption 1"},
new Country(){Id=2,Name="country2",caption="caption 2"},
new Country(){Id=3,Name="country3",caption="caption 3"},
new Country(){Id=4,Name="country4",caption="caption 4"},
new Country(){Id=5,Name="country5",caption="caption 5"},
new Country(){Id=6,Name="country6",caption="caption 6"},
new Country(){Id=7,Name="country7",caption="caption 7"},
};
}
}
public class Country
{
public int Id { get; set; }
public string Name { get; set; }
public string caption { get; set; }
}
In this case try to extend the MainPage from INotifyPropertyChanged and instead of RaisePropertyChanged("CountryNames") in set property, use OnPropertyChanged()

Data template not updating cell

I'm trying to insert a new row (different cell) between two items on a list. The problem is when the new item is inserted, it does not insert the desired cell.
For example, I want to insert item5(ItemCell2) between item 1 and 2:
item1(ItemCell1)
item2(ItemCell1)
item3(ItemCell1)
item4(ItemCell1)
When I insert item5 the cell takes the form of ItemCell1 not of ItemCell2.
Also, I notice that OnSelectTemplate in DemoTemplate is only called once. Thus if the first item of the list is Visible = true it applies ItemCell2 to all the rows even thought the list contains items with Visible = false
using System;
using System.Collections.ObjectModel;
using MvvmHelpers;
using Xamarin.Forms;
namespace Demo.ViewModels
{
public class ItemViewModel : BaseViewModel
{
public ItemViewModel()
{
var item1 = new Item { ItemName = "Name 1", Visible = false };
var item2 = new Item { ItemName = "Name 2", Visible = true };
var item3 = new Item { ItemName = "Name 3", Visible = false };
var item4 = new Item { ItemName = "Name 4", Visible = false };
var item5 = new Item { ItemName = "Name 5", Visible = false };
List = new ObservableCollection<Item>();
List.Add(item1);
List.Add(item2);
List.Add(item3);
List.Add(item4);
List.Add(item5);
MessagingCenter.Subscribe<Item, Item>(this, "msg", (sender, arg) =>
{
DoSomething(arg);
});
void DoSomething(Item item)
{
var index = List.IndexOf(item);
List.Insert(index, new Item { ItemName = "Name 6", Visible = true} );
}
}
ObservableCollection<Item> list;
public ObservableCollection<Item> List
{
get { return list; }
set { SetProperty(ref list, value); }
}
}
}
Item:
using System;
using System.Windows.Input;
using MvvmHelpers;
using Xamarin.Forms;
namespace Demo.Models
{
public class Item : ObservableObject
{
string itemName;
public string ItemName
{
get { return itemName; }
set { SetProperty(ref itemName, value); }
}
bool visible;
public bool Visible
{
get { return visible; }
set { SetProperty(ref visible, value); }
}
ICommand testCommand;
public ICommand TestCommand => testCommand ??
(
testCommand = new Command(DoSomething)
);
void DoSomething()
{
MessagingCenter.Send(this, "msg", this);
System.Diagnostics.Debug.WriteLine("test");
}
}
}
Data Template:
using System;
using Xamarin.Forms;
namespace Demo.Templates
{
public class DemoTemplate : DataTemplateSelector
{
readonly DataTemplate dataTemplate1;
readonly DataTemplate dataTemplate2;
public DemoTemplate()
{
dataTemplate1 = new DataTemplate(typeof(ItemCell1));
dataTemplate2 = new DataTemplate(typeof(ItemCell2));
}
protected override DataTemplate OnSelectTemplate(object item, BindableObject container)
{
var dataTemplate = dataTemplate1;
var myItem = item as Item;
if(myItem.Visible)
{
dataTemplate = dataTemplate1;
}
else
{
dataTemplate = dataTemplate2;
}
return dataTemplate;
}
}
}
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="Demo.Pages.HomePage">
<ContentPage.Resources>
<ResourceDictionary>
<template:DmoTemplate
x:Key="dataTemplate">
</template:DemoTemplate>
</ResourceDictionary>
</ContentPage.Resources>
<ContentPage.Content>
<ListView
CachingStrategy="RecycleElementAndDataTemplate"
ItemTemplate="{StaticResource dataTemplate}"
ItemsSource="{Binding List}"
HasUnevenRows="true"
SeparatorVisibility="None">
</ListView>
</ContentPage.Content>
</ContentPage>
Changing CachingStrategy to "CachingStrategy="RecycleElement"' fixed the issue.

Resources