How to get data from ViewModel in Xamarin - xamarin

I have:
public class ViewCustomerViewModel
{
public Customer CustomerInfo { get; set; }
public static string baseUrl = "https://xxxx/Customers/";
public ViewCustomerViewModel()
{
CheckUserinfo();
}
public async void CheckUserinfo()
{
.....
var url = baseUrl;
HttpClient client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", xxx);
string jsonStr = await client.GetStringAsync(url);
var res = JsonConvert.DeserializeObject<Customer>(jsonStr);
CustomerInfo = res;
}
}
CustomerInfo returns oke
I have PageOne.xaml
<ContentPage ...>
<ContentPage.BindingContext>
<locals:ViewCustomerViewModel/>
</ContentPage.BindingContext>
<ContentPage.Content>
<Label Text={Binding xxxx} />
</ContentPage.Content>
</ContentPage>
PageOne.xaml.cs
This is how I get the data to Binding:
public Customer CustomerInfo { get; set; }
public PageOne()
{
InitializeComponent();
BindingContext = new ViewCustomerViewModel();
}
However: in PageOne.xaml page when I <Label Text={Binding xxxx} /> ---> Binding xxxx, I get no value of Customer class.
I was doing something wrong. Please help me how can I display CustomerInfo results. Thank you
Update
PageOne.xaml
<ContentPage ...>
<ContentPage.Content>
<Label Text={Binding xxxx} />
</ContentPage.Content>
</ContentPage>

The problem is that when the constructor completes, the instance is still being asynchronously initialized, and there isn’t an obvious way to determine when the asynchronous initialization has completed.
Instead of calling from the constructor, do it from the page's OnAppearing method, which you can make async.
Or you could change your viewmodel,make a Factory Pattern for it.From the link,there are several methods you could refer to.

Related

How to initialize data and make data binding work properly in xamarin.forms?

I learnt from this tutorial and tried to build my own time tracker app using master detail page instead. But I met an issue. Here is a similar quick demo for the issue:
For MasterDetailPageDetail.xaml:
<?xml version="1.0" encoding="utf-8" ?>
<TabbedPage 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"
xmlns:local="clr-namespace:QuickDemo"
x:Class="QuickDemo.MasterDetailPageDetail"
Title="Detail">
<TabbedPage.Children>
<NavigationPage Title="Tab One">
<x:Arguments>
<local:FirstPage BindingContext="{Binding FirstPageModel}" />
</x:Arguments>
</NavigationPage>
<NavigationPage Title="Tab Two">
</NavigationPage>
</TabbedPage.Children>
</TabbedPage>
For FirstPage.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"
NavigationPage.HasNavigationBar="False"
xmlns:buttons="clr-namespace:QuickDemo.Views.Buttons"
x:Class="QuickDemo.FirstPage">
<ContentPage.Content>
<StackLayout>
<StackLayout Padding="20,20,20,5">
<Label Text="{Binding CurrentStartDate, StringFormat='{0:h:mm tt}'}"
FontSize="20"
TextColor="Black"/>
<Label Text="{Binding RunningTotal, StringFormat='{}{0:h\\:mm\\:ss}'}"
FontSize="50" HorizontalTextAlignment="Center"
TextColor="Black"/>
</StackLayout>
</StackLayout>
</ContentPage.Content>
</ContentPage>
For DetailPageModel.cs:
public class DetailPageModel : PageModelBase
{
private FirstPageModel _firstPageModel;
public FirstPageModel FirstPageModel
{
get => _firstPageModel;
set => SetProperty(ref _firstPageModel, value);
}
public DetailPageModel(
FirstPageModel firstPageModel)
{
FirstPageModel = firstPageModel;
}
public override Task InitializeAsync(object navigationData = null)
{
return Task.WhenAny(base.InitializeAsync(navigationData),
FirstPageModel.InitializeAsync(null));
}
}
For FirstPageModel.cs:
public class FirstPageModel : PageModelBase
{
private DateTime _currentStartDate;
public DateTime CurrentStartDate
{
get => _currentStartDate;
set => SetProperty(ref _currentStartDate, value);
}
private TimeSpan _runningTotal;
public TimeSpan RunningTotal
{
get => _runningTotal;
set => SetProperty(ref _runningTotal, value);
}
private Timer _timer;
public FirstPageModel()
{
this.InitializeTimer();
}
private void InitializeTimer()
{
_timer = new Timer();
_timer.Interval = 1000;
_timer.Enabled = false;
_timer.Elapsed += TimerElapsed;
}
private void TimerElapsed(object sender, ElapsedEventArgs e)
{
RunningTotal += TimeSpan.FromSeconds(1);
}
public override async Task InitializeAsync(object navigationData)
{
CurrentStartDate = DateTime.Now;
RunningTotal = new TimeSpan();
await base.InitializeAsync(navigationData);
}
}
And I registered the page and pagemodel in the PageModelLocator.cs:
public class PageModelLocator
{
static TinyIoCContainer _container;
static Dictionary<Type, Type> _lookupTable;
static PageModelLocator()
{
_container = new TinyIoCContainer();
_lookupTable = new Dictionary<Type, Type>();
// Register pages and page models
Register<DetailPageModel, MasterDetailPageDetail>();
Register<FirstPageModel, FirstPage>();
// Register services (services are registered as singletons default)
}
public static T Resolve<T>() where T : class
{
return _container.Resolve<T>();
}
public static Page CreatePageFor(Type pageModelType)
{
var pageType = _lookupTable[pageModelType];
var page = (Page)Activator.CreateInstance(pageType);
var pageModel = _container.Resolve(pageModelType);
page.BindingContext = pageModel;
return page;
}
static void Register<TPageModel, TPage>() where TPageModel : PageModelBase where TPage : Page
{
_lookupTable.Add(typeof(TPageModel), typeof(TPage));
_container.Register<TPageModel>();
}
}
PageModelBase.cs and ExtendedBindableObject.cs are same as the tutorial. When I ran the emulator, I got this result:
I thought there would be a DateTime string and a zero time span. I feel like the data in FirstPageModel isn't initialized at all. I also tried to set CurrentStartTime in the constructor. Got the same result.
Did I miss something to display CurrentStartDate and RunningTotal on FirstPage? Any help or hints would be appreciated. Thanks in advance.
I believe your MasterDetailPageDetail is not being initialized (or BindingContext set for that matter). The architecture in the tutorial sets the BindingContext during a navigation event. Since you are setting the Master and Detail explicitly in the XAML of your MasterDetailPage, the Binding Context is not explicitly set and the InitializeAsync Method is not being called.
Add initialization in the Navigation Service
update NavigationService's NavigateToAsync method with the following, right after if (setRoot) and before if (page is TabbedPage tabbedPage):
if (page is MasterDetailPage mdp)
{
App.Current.MainPage = mdp;
// We need to initialize both Master's BindingContext as
// well as Detail's BindingContext if they are PageModelBases
if (mdp.Master.BindingContext is PageModelBase masterPM)
{
await masterPM.InitializeAsync(null);
}
if (mdp.Detail.BindingContext is PageModelBase detailPM)
{
await detailPM.InitializeAsync(null);
}
}
else if (page is TabbedPage tabbedPage)
// .... existing code here
Make sure you're setting the Binding Context for the Pages (Master and Detail). You can do this in the XAML or by resolving in the MasterDetailPage and PageModel like you did for the Tabbed Page
I can make a video to cover this if needed, and add it to the Series. Hope this helps!
Cheers,
Patrick

Xamarin - Asynchronous data binding

I have following code:
Page with lots of images, that are loaded dynamically with the databinding:
base.OnAppearing();
if (!loaded)
{
loaded = true;
BindingContext = new GalleryViewModel(pCode, gCode, gUrl);
}
viewmodel:
namespace GalShare.ViewModel
{
class GalleryViewModel
{
public string pCode { get; set; }
public string gCode { get; set; }
public string gUrl { get; set; }
public ObservableCollection<picdata> Galleries { get; set; }
public GalleryViewModel(string pCode, string gCode, string gUrl)
{
this.pCode = pCode;
this.gCode = gCode;
this.gUrl = gUrl;
Galleries = new GalleryService().GetImageList(pCode,gCode,gUrl);
}
}
}
galleryservice.cs
class GalleryService
{
public ObservableCollection<picdata> Images { get; set; }
public ObservableCollection<picdata> GetImageList(string pCode, string gCode, string gUrl)
{
WebClient client = new WebClient();
Images = new ObservableCollection<picdata>();
string downloadString = client.DownloadString(gUrl);
var deserialized = JsonConvert.DeserializeObject<JsonTxt>(downloadString);
foreach (File img in deserialized.Files)
{
Images.Add(new picdata()
{
ImageName = img.file,
BaseUrl = deserialized.Settings.Path.ToString(),
ThumbUrl = deserialized.Settings.Path.ToString() + "/thumbs" + img.file
});
}
return Images;
}
}
XAML of the page:
<?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:vm="clr-namespace:GalShare.ViewModel"
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:ffimageloading="clr-namespace:FFImageLoading.Forms;assembly=FFImageLoading.Forms"
mc:Ignorable="d"
x:Class="GalShare.Views.Gallery">
<StackLayout>
<CollectionView ItemsSource="{Binding Galleries}" x:Name="myCollection" SelectionMode="Single" SelectionChanged="CollectionView_SelectionChanged">
<CollectionView.ItemsLayout>
<GridItemsLayout Orientation="Vertical"
Span="2" />
</CollectionView.ItemsLayout>
<CollectionView.ItemTemplate>
<DataTemplate>
<ffimageloading:CachedImage Source="{Binding ThumbUrl}" CacheDuration="1" HorizontalOptions="Fill" VerticalOptions="Fill" DownsampleToViewSize="False"></ffimageloading:CachedImage>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</StackLayout>
</ContentPage>
The code works, but given that the images are loaded from the web, while I am loading the data the App is locked. How can this be done asynchronously? I'd like to load the destination page, and then load the content while I'm there.. Currently the app freezes on the page that loads this one until all is loaded.
I have tried with tasks/await but with no success. I think i have to move some things around to run the code asynchronously but cannot figure out how.
You've tagged async-await and writting asynchronous in your title. However, all of your code is running on the main thread and not asynchronously.
Instead of loading your data in the constructor of the ViewModel. I highly suggest you use a lifecycle event such as OnAppearing on your Page and fire a ICommand to load your data asynchronously.
Additionally I would switch to using HttpClient and its nice async APIs. So something like:
public class GalleryService
{
private HttpClient _httpClient;
public GalleryService()
{
_httpClient = new HttpClient();
}
public async Task<IEnumerable<picdata>> GetImageList(string pCode, string gCode, string gUrl)
{
var response = await _httpClient.GetAsync(gUrl).ConfigureAwait(false);
if (response.IsSuccessStatusCode)
{
var json = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
var deserialized = JsonConvert.DeserializeObject<JsonTxt>(json);
var images = new List<picdata>();
foreach(var img in deserialized.Files)
{
images.Add(new picdata()
{
ImageName = img.file,
BaseUrl = deserialized.Settings.Path.ToString(),
ThumbUrl = deserialized.Settings.Path.ToString() + "/thumbs" + img.file
});
}
return images;
}
return new picdata[0]; // return empty set
}
}
and ViewModel:
public class GalleryViewModel
{
private GalleryService _galleryService;
public ObservableCollection<picdata> Galleries { get; } = new ObservableCollection<picdata>();
public ICommand GetImagesCommand { get; }
public GalleryViewModel(string pCode, string gCode, string gUrl)
{
_galleryService = new GalleryService();
GetImagesCommand = new Command(async () => DoGetImagesCommand(pCode, gCode, gUrl));
}
private async Task DoGetImagesCommand(string pCode, string gCode, string gUrl)
{
var images = await _galleryService.GetImageList(pCode, gCode, gUrl);
foreach(var image in images)
Galleries.Add(image);
}
}
Then in your OnAppearing() override on your page you can call something like: (BindingContext as GalleryViewModel).GetImagesCommand.Execute(null);
Make sure you set your BindingContext before trying to call the command. This can be done in the Page constructor with:
BindingContext = new GalleryViewModel();
This way you are not blocking your entire UI until it is done downloading the images. Alternatively you could fire off a Task with Task.Run in the constructor of the ViewModel. However, then you will have to marshal the population of the ObservableCollection to the UI thread.

Xamarin Forms Basic BindingContext to ViewModel

I'm trying to do some basic data binding from my XAML view to my view model in my Xamarin Forms (4.2) app. I navigate to the PhotoUploadPage from a different page.
PhotoUploadPage.xaml
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage
x:Class="AgentConnectMobile.Views.PhotoUploadPage"
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"
Title="Upload Photo"
mc:Ignorable="d">
<ContentPage.Content>
<!--Upload.Id below isn't showing -->
<Label Text="{Binding Upload.Id}" />
</ContentPage.Content>
</ContentPage>
PhotoUploadPage.xaml.cs
public partial class PhotoUploadPage : ContentPage
{
PhotoUploadViewModel viewModel;
public PhotoUploadPage(PhotoUploadViewModel viewModel)
{
this.viewModel = viewModel;
BindingContext = this.viewModel;
InitializeComponent();
}
}
PhotoUploadViewModel.cs
public class PhotoUploadViewModel : BaseViewModel
{
public Upload Upload { get; set; }
public PhotoUploadViewModel(Upload upload = null)
{
Upload = upload;
}
}
Upload.cs
public class Upload
{
public string Id;
}
Navigate to PhotoUploadPage
var upload = new Upload
{
Id = "abc123",
};
await Navigation.PushAsync(new PhotoUploadPage(new PhotoUploadViewModel(upload)));
I put breakpoints in PhotoUploadPage.xaml.cs and the BindingContext is getting set and I can see Upload on it with my Id value, but the value never appears in the Label text. I have also switched the order of InitializeComponent() and setting the BindingContext but that didn't solve anything either.
What am I doing wrong? I believe I'm doing this other places and it is working just fine...

How can I pass a command to a template and have it execute in my back end code and pass the parameter?

I have this template:
<?xml version="1.0" encoding="utf-8"?>
<Grid Padding="20,0" xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:Japanese;assembly=Japanese"
x:Class="Japanese.Templates.DataGridTemplate"
x:Name="this" HeightRequest="49" Margin="0">
<Grid.GestureRecognizers>
<TapGestureRecognizer
Command="{Binding TapCommand, Source={x:Reference this}}"
CommandParameter="1"
NumberOfTapsRequired="1" />
</Grid.GestureRecognizers>
<Label Grid.Column="0" Text="{Binding Test" />
</Grid>
Behind this I have:
public partial class DataGridTemplate : Grid
{
public DataGridTemplate()
{
InitializeComponent();
}
public static readonly BindableProperty TapCommandProperty =
BindableProperty.Create(
"Command",
typeof(ICommand),
typeof(DataGridTemplate),
null);
public ICommand TapCommand
{
get { return (ICommand)GetValue(TapCommandProperty); }
set { SetValue(TapCommandProperty, value); }
}
}
and I am trying to call the template like this in file: Settings.xaml.cs
<template:DataGridTemplate TapCommand="openCFSPage" />
hoping that it will call my method here in file: Settings.cs
void openCFSPage(object sender, EventArgs e)
{
Navigation.PushAsync(new CFSPage());
}
The code compiles but when I click on the grid it doesn't call the openCFSPage method.
1) Does anyone have an idea what might be wrong?
2) Also is there a way that I can add a parameter to the template and then have that parameter passed to my method in the CS back end code?
Note that I would like to avoid adding a view model if possible. The application is small and I'd like to just have the code I need in the CS code of the page that calls the template.
Please note that the simplest way to implement this would be through MVVM (i.e. a view-model), but if you want to side-step this option (as you mentioned in the question) then you can use one of the following options
Option1 : Wrap delegate into command object
If you look at it from the perspective of a XAML parser, you are technically trying to assign a delegate to a property of type ICommand. One way to avoid the type mismatch would be to wrap the delegate inside a command-property in the page's code-behind.
Code-behind [Settings.xaml.cs]
ICommand _openCFSPageCmd;
public ICommand OpenCFSPageCommand {
get {
return _openCFSPageCmd ?? (_openCFSPageCmd = new Command(OpenCFSPage));
}
}
void OpenCFSPage(object param)
{
Console.WriteLine($"Control was tapped with parameter: {param}");
}
XAML [Settings.xaml]
<!-- assuming that you have added x:Name="_parent" in root tag -->
<local:DataGridView TapCommand="{Binding OpenCFSPageCommand, Source={x:Reference _parent}}" />
Option2 : Custom markup-extension
Another option (a bit less mainstream) is to create a markup-extension that wraps the delegate into a command object.
[ContentProperty("Handler")]
public class ToCommandExtension : IMarkupExtension
{
public string Handler { get; set; }
public object Source { get; set; }
public object ProvideValue(IServiceProvider serviceProvider)
{
if (serviceProvider == null)
throw new ArgumentNullException(nameof(serviceProvider));
var lineInfo = (serviceProvider?.GetService(typeof(IXmlLineInfoProvider)) as IXmlLineInfoProvider)?.XmlLineInfo ?? new XmlLineInfo();
object rootObj = Source;
if (rootObj == null)
{
var rootProvider = serviceProvider.GetService<IRootObjectProvider>();
if (rootProvider != null)
rootObj = rootProvider.RootObject;
}
if(rootObj == null)
{
var valueProvider = serviceProvider.GetService<IProvideValueTarget>();
if (valueProvider == null)
throw new ArgumentException("serviceProvider does not provide an IProvideValueTarget");
//we assume valueProvider also implements IProvideParentValues
var propInfo = valueProvider.GetType()
.GetProperty("Xamarin.Forms.Xaml.IProvideParentValues.ParentObjects",
BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
if(propInfo == null)
throw new ArgumentException("valueProvider does not provide an ParentObjects");
var parentObjects = propInfo.GetValue(valueProvider) as IEnumerable<object>;
rootObj = parentObjects?.LastOrDefault();
}
if(rootObj != null)
{
var delegateInfo = rootObj.GetType().GetMethod(Handler,
BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
if(delegateInfo != null)
{
var handler = Delegate.CreateDelegate(typeof(Action<object>), rootObj, delegateInfo) as Action<object>;
return new Command((param) => handler(param));
}
}
throw new XamlParseException($"Can not find the delegate referenced by `{Handler}` on `{Source?.GetType()}`", lineInfo);
}
}
Sample usage
<local:DataGridView TapCommand="{local:ToCommand OpenCFSPage}" />
You have 2 options depending on the the use case :
FYI, there's no way to call another method directly from the view (its a bad design pattern to do so)
Using Event Aggregator :
Create interface
public interface IEventAggregator
{
TEventType GetEvent<TEventType>() where TEventType : EventBase, new();
}
All you have to do is call it from you TapCommand
_eventAggregator.GetEvent<ItemSelectedEvent>().Publish(_selectedItem);
Then in your Settings.cs you can Create a method that can receive the data
this.DataContext = new ListViewModel(ApplicationService.Instance.EventAggregator);
Inheritance and Polymorphism / Making openCFSPage a service :
Creating a interface / service that links both models
public interface IOpenCFSPage
{
Task OpenPage();
}
and a method :
public class OpenCFSPage : IOpenCFSPage
{
private INavigationService _navigationService;
public OpenCFSPage(INavigationService navigationService){
_navigationService = navigationService;
}
public async Task OpenPage()
{
await _navigationService.NavigateAsync(new CFSPage());
}
}
Settings.xaml:
<template:DataGridTemplate TapCommand="{Binding OpenCFSPage}" />
<!-- Uncomment below and corresponding parameter property code in DataGridTemplate.xaml.cs to pass parameter from Settings.xaml -->
<!--<template:DataGridTemplate TapCommand="{Binding OpenCFSPage}" CommandParameter="A" />-->
Settings.xaml.cs:
public Settings()
{
InitializeComponent();
OpenCFSPage = new Command(p => OpenCFSPageExecute(p));
BindingContext = this;
}
public ICommand OpenCFSPage { get; private set; }
void OpenCFSPageExecute(object p)
{
var s = p as string;
Debug.WriteLine($"OpenCFSPage:{s}:");
}
DataGridTemplate.xaml:
<?xml version="1.0" encoding="UTF-8"?>
<Grid xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:Japanese;assembly=Japanese"
Padding="0,20"
HeightRequest="49" Margin="0"
x:Class="Japanese.DataGridTemplate">
<Grid.GestureRecognizers>
<TapGestureRecognizer
Command="{Binding TapCommand}"
CommandParameter="1"
NumberOfTapsRequired="1" />
</Grid.GestureRecognizers>
<Label Grid.Column="0" Text="Test" />
</Grid>
DataGridTemplate.xaml.cs:
public partial class DataGridTemplate : Grid
{
public DataGridTemplate()
{
InitializeComponent();
}
public static readonly BindableProperty TapCommandProperty =
BindableProperty.Create(
nameof(TapCommand), typeof(ICommand), typeof(DataGridTemplate), null,
propertyChanged: OnCommandPropertyChanged);
public ICommand TapCommand
{
get { return (ICommand)GetValue(TapCommandProperty); }
set { SetValue(TapCommandProperty, value); }
}
//public static readonly BindableProperty CommandParameterProperty = BindableProperty.Create(
// nameof(CommandParameter), typeof(string), typeof(DataGridTemplate), null);
//public string CommandParameter
//{
// get { return (string)GetValue(CommandParameterProperty); }
// set { SetValue(CommandParameterProperty, value); }
//}
static TapGestureRecognizer GetTapGestureRecognizer(DataGridTemplate view)
{
var enumerator = view.GestureRecognizers.GetEnumerator();
while (enumerator.MoveNext())
{
var item = enumerator.Current;
if (item is TapGestureRecognizer) return item as TapGestureRecognizer;
}
return null;
}
static void OnCommandPropertyChanged(BindableObject bindable, object oldValue, object newValue)
{
if (bindable is DataGridTemplate view)
{
var tapGestureRecognizer = GetTapGestureRecognizer(view);
if (tapGestureRecognizer != null)
{
tapGestureRecognizer.Command = (ICommand)view.GetValue(TapCommandProperty);
//tapGestureRecognizer.CommandParameter = (string)view.GetValue(CommandParameterProperty);
}
}
}
}
Check this code you help you. Here you have to pass a reference of list view and also you need to bind a command with BindingContext.
<ListView ItemsSource="{Binding Sites}" x:Name="lstSale">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Orientation="Vertical">
<Label Text="{Binding FriendlyName}" />
<Button Text="{Binding Name}"
HorizontalOptions="Center"
VerticalOptions="Center"
Command="{Binding
Path=BindingContext.RoomClickCommand,
Source={x:Reference lstSale}}"
CommandParameter="{Binding .}" />
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>

Xamarin rest api

Binding does not work in my code. what's wrong in this code?
HttpClient client = new HttpClient();
var response = await client.GetAsync(string.Format("uri link"));
string jsonstring = await response.Content.ReadAsStringAsync();
RootObject item = JsonConvert.DeserializeObject<RootObject>(jsonstring);
titles.ItemsSource =item.ToString();
XAML code
<ListView x:Name="titles" HasUnevenRows="False" >
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<ViewCell.View>
<StackLayout Orientation="Horizontal">
<Label Text="{Binding note}"/>
</StackLayout>
</ViewCell.View>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
class object:
public class Result
{
public string note { get; set; }
}
public class Response
{
public List<Result> results { get; set; }
}
public class RootObject
{
public Response response { get; set; }
}
you bind the lable to the note, but you set the titles.ItemsSource to the RootObject. the RootObject class doesn't have note. note is in Result class.
and you can't set the itemsource like that.
I suggest you to do this
var listItem = JsonConvert.DeserializeObject<List<Result>>(jsonstring);
titles.ItemsSource = l;
According to me are upto:
RootObject item = JsonConvert.DeserializeObject<RootObject>(jsonstring);
and can you try this code also after the above line:
titles.ItemsSource =item.Responce. results;

Resources