Json to C# object display in Windows Phone's ListBox - c#

I used http://json2csharp.com to generate the C# class from the below JSON string.
{
"stationArr":[
{
"id":"9",
"name":"name9",
"sidebar":{
"original":"http://myurl.com/station_images/5/5_s.png",
"m":"http://myurl.com/station_images/5/m/5_s_m.png",
"s":"http://myurl.com/station_images/5/s/5_s_s.png"
}
},
{
"id":"3",
"name":"name3",
"sidebar":{
"original":"http://myurl.com/station_images/5/5_s.png",
"m":"http://myurl.com/station_images/5/m/5_s_m.png",
"s":"http://myurl.com/station_images/5/s/5_s_s.png"
}
]
"stationUrlMap":{
"9":"http://myurl.com/9_64",
"3":"http://myurl.com/3_64",
}
}
The generated classes are (I created different .cs for each class.
public class Sidebar
{
public string original { get; set; }
public string m { get; set; }
public string s { get; set; }
}
public class StationArr
{
public string id { get; set; }
public string name { get; set; }
public Sidebar sidebar { get; set; }
}
/*public class StationUrlMap
{
public string __invalid_name__9 { get; set; }
public string __invalid_name__3 { get; set; }
}
*/
public class StationList
{
public List<StationArr> stationArr { get; set; }
// public StationUrlMap stationUrlMap { get; set; } Dicarded it
}
I have discarded the StationUrlMap as I don't need it.
I am using the following code to create the Object
string resultString = sd.ReadToEnd();
StationList stations = JsonConvert.DeserializeObject<StationList>(resultString);
Debug.Writeline(stations.stationArr.Count); // gives Output 9 Which is correct.
I just don't know how to display the List of stations in the UI (using ListBox). Please guide me in the right direction.

Create a ListBox in the XAML
<ListBox x:Name="ListBoxStations" Height="500" >
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Width="450">
<Image Source="{Binding Path=sidebar.original}"/>
<TextBlock Text="{Binding Path=name}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
And then in your cs file, after the json conversion do the following
StationList stations = JsonConvert.DeserializeObject<StationList>(resultString);
ListBoxStations.ItemsSource = stations.stationArr;
The above XAML code is just a sample, change based on your requirements.

There are two ways.
First you can set the listbox's ItemSource property to the list you just created.
Second, You can create an ObservableCollection of thing in your list, and bind the observable collection to the listbox's ItemSource property. Add items in the Observable Collection and these will be displayed in your listbox.
The second way gives you more control over the items in the lisbox. You can remove or add elements in the observable collection and that will reflect in the listbox contents. The first method will make the contents of the list read-only, and you will not be able to add or remove items from it.
http://msdn.microsoft.com/en-us/library/ms668604.aspx
http://msdn.microsoft.com/en-us/library/system.windows.controls.listbox%28v=vs.95%29.aspx

Related

Modyfing an item in a list of a list doesn't update the UI

I have pretty complicated case where data from API comes in the form of a list which contain lists and these inner lists (there is multiple of them btw) finally contain entries. So what I did is that I have collection view and in that collection view I have another collection view which serves as a template.
So in this inner collection view I have selected item binded to a Entry type property from my viewmodel. I can easily gather the data about the clicked item but trying to modify anything will not update the UI. Which might be because I'm modifying this property in a viewmodel instead of in list where I have it. (I have INotifyPropertyChanged implemented) So I'm looking for solution to solve this.
I will send my view , viewmodel, and my classes for you to see what exactly is going on.
View:
<CollectionView ItemsSource="{Binding AnimListsList}"
BackgroundColor="Transparent"
ItemsLayout="VerticalList"
SelectionMode="None"
x:Name="CollectionView1">
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout>
<Label Text="{Binding name}" FontSize="30">
</Label>
<CollectionView ItemsSource="{Binding entries}"
BackgroundColor="Transparent"
ItemsLayout="VerticalList"
SelectionMode="Single"
SelectedItem="{Binding BindingContext.Entry, Source={x:Reference Name=AnimeListPagee}}"
x:Name="CollectionView2">
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout>
<Label Text="{Binding media.Title.Romaji}"></Label>
<Label Text="{Binding progress}"></Label>
<Frame BackgroundColor="Green">
<StackLayout>
<Frame BackgroundColor="Yellow" xct:TouchEffect.NativeAnimation="True"
xct:TouchEffect.Command="{Binding BindingContext.InfoCommand, Source={x:Reference Name=AnimeListPagee}}"></Frame>
<Frame BackgroundColor="Red" xct:TouchEffect.NativeAnimation="True"
xct:TouchEffect.Command="{Binding BindingContext.InfoCommand2, Source={x:Reference Name=AnimeListPagee}}"></Frame>
</StackLayout>
</Frame>
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
My property to which I bind to as selected item in my collection view
public ListEntry entry;
public ListEntry Entry
{
get => entry;
set
{
if (value == entry)
return;
entry = value;
OnPropertyChanged();
}
}
A task that is fired by command binded to one of the frames in my view
async Task Info()
{
Console.WriteLine($"{Entry.media.Title.Romaji} +");
Entry.progress++;
}
Relevant part of my model:
public class Media
{
public string Id { get; set; }
public string Description { get; set; }
public string BannerImage { get; set; }
public int? AverageScore { get; set; }
public int Favourites { get; set; }
public TitleType Title { get; set; }
public CoverImageType CoverImage { get; set; }
}
public class MediaListCollection
{
public ObservableCollection<lists> lists { get; set; }
}
public class lists
{
public string name { get; set; }
public bool isCustomList { get; set; }
public bool isSplitCompletedList { get; set; }
public string status { get; set; }
public ObservableCollection<ListEntry> entries { get; set; }
}
public class ListEntry : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
void OnPropertyChanged([CallerMemberName] string name = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
public int progress;
public Media media { get; set; }
public int Progress
{
get => progress;
set
{
if (value == progress)
return;
progress = value;
OnPropertyChanged();
}
}
public string status { get; set; }
}
The ultimate goal is to modify the selected entry's progress property. Trying to do it the easy way
Entry.progress++ by modifying the property in the viewmodel did not work. I want these two frames in my view to increase and decrease it as I wish. The reason it is not working is probably the one I mentioned so I am looking for solution to find the right item in the list that is in the list, modify progress property and most importantly make the UI update accordingly. It would be so much easier to do it if it was just a one normal list.
I tried implementing INotifyPropertyChanged to the relevant class and property in my model but that's throwing me an exception when querying to API.
Newtonsoft.Json.JsonSerializationException: 'A member with the name 'progress' already exists on 'OtakuApp.Models.ListEntry'. Use the JsonPropertyAttribute to specify another name.
This is my code for querying and adding
string token = Preferences.Get("token", "default_value");
var anilistGraphQlHttpClient =
new GraphQLHttpClient("https://graphql.anilist.co", new NewtonsoftJsonSerializer());
anilistGraphQlHttpClient.HttpClient.DefaultRequestHeaders.Add("Authorization", "Bearer " + token);
var requestAnimeList = new GraphQLRequest(
"query { MediaListCollection(userId: 5154007, type: ANIME) { lists { name isCustomList isSplitCompletedList status entries { media { id episodes title { romaji } } progress status } } } } ");
var graphQlResponseAnimeList =
await anilistGraphQlHttpClient.SendQueryAsync<ResponseType>(requestAnimeList);
foreach (var name in graphQlResponseAnimeList.Data.MediaListCollection.lists)
{
AnimListsList.Add(name);
}

How to send data from a picker IN a collectionview to an API endpoint using MVVM (Xamarin)

For my current project I am working on a cross-platform application. We are building this using Visual studio, cross platform-app.
In this project we use questionaires which we set in an SQL Database.
Using MVVM I can load this dynamically into the mobile app. (We do this because there are many platforms where we show this, so we choose to set it in the back-end, the questionaire with the answers to each question).
But with saving there seems to be an issue,
I can't seem to send data back to my back end
-> I want to send either the value or the string showed from the picker back to the back end (preferably using MVVM). If I can get the data in the viewmodel (or code behind if MVVM would not be possible), i would be helped enough with my question.
I build my questionaire using a collectionview which contains a DataTemplate with a picker inside.
QuestionaireView.xaml:
<ContentPage.BindingContext>
<viewModels:Questionaire_viewmodel/>
</ContentPage.BindingContext>
<CollectionView
x:Name="CollectionQuestionsView"
SelectionMode="None"
Margin="0,0,0,0" RemainingItemsThreshold="5"
BackgroundColor="Transparent"
SelectedItems="{Binding selectedPickers}"
ItemsSource="{Binding Questionaire.Vraag,Mode=TwoWay}">
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout>
<Label Style="{StaticResource RegularLabelStyleSmallLogin}" Text="{Binding vraagstelling}" HorizontalTextAlignment="Start" VerticalTextAlignment="Start" LineBreakMode="WordWrap"/>
<Picker Style="{StaticResource PickRegularSmall}" SelectedItem="{Binding vraag_id}" x:Name="{Binding vraag_id}" ItemsSource="{Binding Antwoorden}" ItemDisplayBinding="{Binding antwoord_vraagstelling}"/>
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
<Frame Margin="0,0,0,20" Style="{StaticResource OrangeLoginFrame}">
<Grid Margin="-15" VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Label Style="{StaticResource SemiBoldLabelStyleMedium}" Grid.Row="0" Grid.Column="0" Text="Opslaan"/>
<Button Style="{StaticResource LoginButton}" Command="{Binding SaveVragenlijstCompetentiesCommand}" Grid.Row="0" Grid.Column="0" />
</Grid>
</Frame>
<Button x:Name="questionaireButton" Command="{Binding getQuestionaireLoopbaancompetenties}" IsVisible="False"/>
So to explain the bindings (since some names are not English):
itemsource of collectionview: The questionaire.vraag retrieves a collection of questions out of the questionaire object (see the model.cs down below)
label text binding: Displays the question
picker selectedItem binding: vraag id results in a string (retrieved from the backend) which here specifly is vraag_127, vraag_128 (the numbers are an id of the question)
itemsource picker: binding antwoorden, these are the answers to the questions
in the frame below the collectionview, I made a button to (at least i tried) to send the data to the ViewModel.cs
The (hidden)button below the frame loads the data (I execute it in the code behind, not sure if this is the best option)
QuestionaireViewModel.cs
public string vraag_127 { get; set; }
public string vraag_128 { get; set; }
... and so on
public string vraag_161 { get; set; }
public string categorie_vraag { get; set; }
public string SelectedAnswer { get; set; }
public string vraagstelling { get; set; }
public string vraag_id { get; set; }
public string score_antwoord { get; set; }
public string antwoord_vraagstelling { get; set; }
public string default_question { get; set; }
public string description { get; set; }
private Questionaire_model _questionaire;
public Questionaire_model Questionaire
{
get { return _questionaire; }
set
{
_questionaire = value;
OnPropertyChanged();
}
}
private List<Questionaire_model> _vraag;
public List<Questionaire_model> Vraag
{
get { return _vraag; }
set
{
_vraag = value;
OnPropertyChanged();
}
}
private List<Questionaire_model> _antwoorden;
public List<Questionaire_model> Antwoorden
{
get { return _antwoorden; }
set
{
_antwoorden = value;
OnPropertyChanged();
}
}
public ICommand getQuestionaireLoopbaancompetenties
{
get
{
return new Command(async (boolean) =>
{
Questionaire = await _apiServices.GetQuestionaireQuestions("Competenties");
});
}
}
private ObservableCollection<vragenModel> _selectedPicker;
public ObservableCollection<vragenModel> SelectedPicker
{
get
{
return _selectedPicker;
}
set
{
_selectedPicker = value;
OnPropertyChanged();
}
}
public ICommand SaveVragenlijstCompetentiesCommand
{
get
{
return new Command(async () =>
{
var responseBool = await _apiServices.SaveCultuur(vraag_127,vraag_128, ... vraag_161)});
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyname = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyname));
}
And my QuestionaireModel.cs:
public class Questionaire_model
{
public int id { get; set; }
public string default_question { get; set; }
public string description { get; set; }
public IList<Vragen> Vraag{ get; set; }
public class Vragen
{
public string categorie_vraag { get; set; }
public string SelectedAnswer { get; set; }
public string vraagstelling { get; set; }
public string vraag_id { get; set; }
public IList<antwoorden> Antwoorden { get; set; }
}
public class antwoorden
{
public string score_antwoord { get; set; }
public string antwoord_vraagstelling { get; set; }
}
}
The reason we use an collectionView, is because we use the template for various questionaires with various amount of questions and answer possibilities.
So at this moment, when I execute my save action, it gives "null" in my ViewModel - action
I have defined public class Questionaire_viewmodel : INotifyPropertyChanged above my viewmodel
So I hope someone can either help me with my code, that I missed something here
Or if I have to change something because it is simply impossible well then I would like to have an direction where to go to.
As I understand, you want your Picker.SelectedItem actually bind to QuestionaireViewModel.vrag_xxx properties. That won't work like this - what now happens is that your Picker.SelectedItem is bound to Vragen.vraag_id field and is not able to use it, as long as it expects antwoorden type there.
However, I see that you have SelectedAnswer property in Vragen, you can change it to be of antwoorden type and then fetch user choice from that field.
So, if your Vragen class is:
public class Vragen
{
public string categorie_vraag { get; set; }
public antwoorden SelectedAnswer { get; set; }
public string vraagstelling { get; set; }
public string vraag_id { get; set; }
public IList<antwoorden> Antwoorden { get; set; }
}
Then, you can change your binding for Picker to:
<Picker Style="{StaticResource PickRegularSmall}" SelectedItem="{Binding SelectedAnswer}" x:Name="{Binding vraag_id}" ItemsSource="{Binding Antwoorden}" ItemDisplayBinding="{Binding antwoord_vraagstelling}"/>
And, at last in SaveVragenlijstCompetentiesCommand you'll be able to get array of answers (sorted by question id):
Questionaire.Vraag.OrderBy(x => x.vraag_id).Select(x => x.SelectedAnswer.antwoord_vraagstelling)
That will give you array of answer texts. Still it doesn't conform to your apiServices.SaveCultuur call. I would strongly recommend to change that method - method that contains a lot of parameters is certainly a problem.
If you can change it to accept string[], then you will be able to pass Questionaire.Vraag.OrderBy(x => x.vraag_id).Select(x => x.SelectedAnswer.antwoord_vraagstelling) to it.
If you can't change apiServices, there's still possibility to call the method through reflection using array, but it's a bit tricky.

Correct way of binding xamarin forms picker value

My issue is that I have 2 picker controls. 1 of these picker controls is bound to list a and one of these picker controls is bound to list b. Both of these controls display the data I need them to with the correct display bindings working fine.
My problem is when I bind the selectedItem property, it works for list a but doesn't for list b. I've checked that the code is literally a like for like copy of each other.
I have been using the syncfusion Combobox but switched to the picker as I thought there was an issue here but there isn't. Whatever is happening is totally down to whatever I'm doing.
The usage scenario is that I bring down a list of payment types from my API and populate a picker based on this. This works.
The datasource for my main view contains an ID. When I am modifying a record, I run a method called update to find the selectedItem. I'm not happy with this approach and would be interested to see what other people use.
The update method gets the datasources for the pickers and finds what I would expect to be the selected item. This works fine also but doesn't bind.
[Serializable]
public class PaymentInformation :BaseModel
{
public int? ID { get; set; }
public DateTime? StartDate { get; set; }
public DateTime? EndDate { get; set; }
public int? PaymentTypeId { get; set; }
public string PaymentTo { get; set; }
public string Location { get; set; }
public string Notes { get; set; }
public PersonInformation PersonBudget { get; set; }
public decimal AmountPaid { get; set; }
public decimal AmountReceived { get; set; }
public double TotalHours { get; set; }
public void Update(ObservableCollection<ResourceInformation> resources , ObservableCollection<PaymentTypeInformation> paymentTypes)
{
if(PaymentTypeId != null) this.PaymentTypeInformation1 = paymentTypes?.FirstOrDefault((paymentType) => paymentType.ID == PaymentTypeId.Value);
this.Resource = resources?.FirstOrDefault((resource) => resource.ResourceId == PersonBudget?.ID);
}
private PaymentTypeInformation _paymentTypeInformation;
private PaymentTypeInformation PaymentTypeInformation1 { get { return _paymentTypeInformation; } set { _paymentTypeInformation = value; OnPropertyChanged(nameof(PaymentTypeInformation1)); } }
private ResourceInformation _resource;
public ResourceInformation Resource { get { return _resource; } set { _resource = value; OnPropertyChanged(nameof(Resource)); } }
}
The underlying xaml is:
<Label Grid.Row="8" Grid.Column="0" Text="Payment Type:" />
<Picker BackgroundColor="White" Grid.Row="8" Grid.Column="1" ItemsSource="{Binding PaymentTypesDataSource}" ItemDisplayBinding="{Binding Path=DisplayText}" IsEnabled="{Binding IsProcessing, Converter={StaticResource reverseBoolConvertor}}" SelectedItem="{Binding DataSource.PaymentTypeInformation1, Mode=TwoWay}" />
The expected result is that the drop down initializes with the selectedItem which it doesn't (in one usage scenario - the other one works fine).
Couldn't see the wood for the trees.
private PaymentTypeInformation PaymentTypeInformation1
{
get
{
return _paymentTypeInformation;
}
set
{
_paymentTypeInformation = value;
OnPropertyChanged(nameof(PaymentTypeInformation1));
}
}
Can't bind to a private property - changed to public and immediately work. Stuck on this for a day as bonkers as that is to believe.

Unable to see Bindings on XAML page in Xamarin.forms

I have a project that is not showing the bindings on the XAML page.
My RootObject
public class Notification
{
[JsonProperty("posts")]
public Dictionary<string, List<Post>> posts { get; set; }
}
public class Post
{
[JsonProperty("title")]
public string title { get; set; }
[JsonProperty("image_url")]
public Uri imageUrl { get; set; }
[JsonProperty("message")]
public string message { get; set; }
[JsonProperty("time")]
public string time { get; set; }
}
My Service class(Code behind)
var notification = JsonConvert.DeserializeObject<Notification>(apiContent);
List<Post> posts = new List<Post>();
if (notification?.posts != null)
{
foreach (var item in notification.posts.Values)
{
posts.AddRange(item);
}
}
My XAML file
<Label Text="{Binding title}" />
<Label Text="{Binding message}" />
When the parsing has completed on the XAML page it doesnt show the required title or message from the API.When i check the debugger it shows e a key/value pair that is for each post so i know its getting the data from the API.
Any help would be appreciated, thanks.
turn IsGroupingEnabled off. The data in ItemsSource is not grouped, so you should not tell the ListView that it is.
To use grouping, review the Grouping section in the ListView docs

Listbox in other Listbox, Itemsource, Binding

I would like to know How to refresh our ListBox item.
I tried OnPropertyChanged method, ObservableCollection, but it didn't work. I tried set again the itemsource property, so that worked, but now I have got 2 ListBox and now it's complicated. It's a wp7 project there is the main interface. You can see I have 2 listbox
<ListBox Name="lsbNameDays" ItemsSource="ComplexNameDays">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding NameDay.Name}" FontSize="50"/>
<ListBox ItemsSource="ComplexNameDays.FacebookFriends" x:Name="asdf">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Lastname}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
there is the properties:
List<SelectedNameDays> complexNameDays;
public List<SelectedNameDays> ComplexNameDays
{
get { return complexNameDays; }
set
{
complexNameDays = value;
OnPropertyChanged("ComplexNameDays");
}
}
public class SelectedNameDays : Notifier
{
NameDay _nameday;
public NameDay NameDay
{
get { return _nameday; }
set { _nameday = value; OnPropertyChanged("NameDay"); }
}
public List<FacebookFriend> FacebookFriends { get; set; }
public SelectedNameDays()
{
_nameday = new NameDay();
}
}
public class FacebookFriend
{
public long Id { get; set; }
public string Name { get; set; }
public string Firstname { get; set; }
public string Lastname { get; set; }
public string Birthday { get; set; }
public string Gender { get; set; }
public Uri Picture { get; set; }
}
The begining of the code is correct, that works, because when the Constructor set the datas I set retry the itemsource of lbsNameDays, but i cant find the "asdf" listbox, i can't set their datas again.
so the 2 main question are that.
1. how can i fire the property changed if that, and the observable collecton doesn't works.
2. how can I use asdf listbox in the datatemplate
thank the answer, and I sorry my grammer mistakes
Your Bindings won't work, because you dont use the right syntax:
ItemsSource="ComplexNameDays"
should be
ItemsSource="{Binding ComplexNameDays}"
The second binding is also wrong:
ItemsSource="ComplexNameDays.FacebookFriends"

Categories