For a bit of context, I am trying to link my View to my ViewModel. I have a JSON array of Objects, and another of strings in my model. I want to display these objects in my view, however I am having trouble doing so.
My Model:
namespace Timetabler.Models
{
public partial class Timetable
{
[JsonProperty("id")]
public long Id { get; set; }
[JsonProperty("nid")]
public long Nid { get; set; }
[JsonProperty("iid")]
public long Iid { get; set; }
[JsonProperty("lid")]
public long Lid { get; set; }
[JsonProperty("start")]
public double Start { get; set; }
[JsonProperty("dur")]
public double Dur { get; set; }
[JsonProperty("weeks")]
public string Weeks { get; set; }
[JsonProperty("day")]
public long Day { get; set; }
[JsonProperty("note", NullValueHandling = NullValueHandling.Ignore)]
public string Note { get; set; }
}
public partial struct TimetableElement
{
public long? Integer;
public string String;
public Timetable TimetableClass;
public static implicit operator TimetableElement(long Integer) => new TimetableElement { Integer = Integer };
public static implicit operator TimetableElement(string String) => new TimetableElement { String = String };
public static implicit operator TimetableElement(Timetable TimetableClass) => new TimetableElement { TimetableClass = TimetableClass };
}
public partial class Timetable
{
public static TimetableElement[][] FromJson(string json) => JsonConvert.DeserializeObject<TimetableElement[][]>(json, Converter.Settings);
}
public static class Serialize
{
public static string ToJson(this TimetableElement[][] self) => JsonConvert.SerializeObject(self, Converter.Settings);
}
internal static class Converter
{
public static readonly JsonSerializerSettings Settings = new JsonSerializerSettings
{
MetadataPropertyHandling = MetadataPropertyHandling.Ignore,
DateParseHandling = DateParseHandling.None,
Converters =
{
TimetableElementConverter.Singleton,
new IsoDateTimeConverter { DateTimeStyles = DateTimeStyles.AssumeUniversal }
},
};
}
internal class TimetableElementConverter : JsonConverter
{
public override bool CanConvert(Type t) => t == typeof(TimetableElement) || t == typeof(TimetableElement?);
public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer)
{
switch (reader.TokenType)
{
case JsonToken.Integer:
var integerValue = serializer.Deserialize<long>(reader);
return new TimetableElement { Integer = integerValue };
case JsonToken.String:
case JsonToken.Date:
var stringValue = serializer.Deserialize<string>(reader);
return new TimetableElement { String = stringValue };
case JsonToken.StartObject:
var objectValue = serializer.Deserialize<Timetable>(reader);
return new TimetableElement { TimetableClass = objectValue };
}
throw new Exception("Cannot unmarshal type TimetableElement");
}
public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer)
{
var value = (TimetableElement)untypedValue;
if (value.Integer != null)
{
serializer.Serialize(writer, value.Integer.Value);
return;
}
if (value.String != null)
{
serializer.Serialize(writer, value.String);
return;
}
if (value.TimetableClass != null)
{
serializer.Serialize(writer, value.TimetableClass);
return;
}
throw new Exception("Cannot marshal type TimetableElement");
}
public static readonly TimetableElementConverter Singleton = new TimetableElementConverter();
}
}
My View Model:
namespace Timetabler.ViewModels
{
class TimetableViewModel : BaseViewModel
{
// First list in JSON data
public ObservableRangeCollection<TimetableElement> Names {get;}
// Second list in JSON data
public ObservableRangeCollection<TimetableElement> TypeOfClass {get;}
// Third list in JSON data
public ObservableRangeCollection<TimetableElement> Location {get;}
// Fourth list in JSON data
public ObservableRangeCollection<TimetableElement> Courses {get;}
public Command GetCoursesCommand { get; }
public Command GetNamesCommand { get; }
public TimetableViewModel()
{
Title = "Timetable";
// First list in JSON data
Names = new ObservableRangeCollection<TimetableElement>();
// Second list in JSON data
TypeOfClass = new ObservableRangeCollection<TimetableElement>();
// Third list in JSON data
Location = new ObservableRangeCollection<TimetableElement>();
// Fourth list in JSON data
Courses = new ObservableRangeCollection<TimetableElement>();
GetCoursesCommand = new Command(async () => await GetCoursesAsync());
GetNamesCommand = new Command(async () => await GetNamesAsync());
}
async Task GetNamesAsync()
{
if (IsBusy)
{
return;
}
try
{
IsBusy = true;
var timetableElements = await DataService.GetTimetablesAsync();
var names = timetableElements[0];
Names.ReplaceRange(names);
Title = $"Courses available({Names.Count}";
}
catch (Exception ex)
{
Debug.WriteLine($"Unable to get Names: {ex.Message}");
await Application.Current.MainPage.DisplayAlert("Error!", ex.Message, "OK");
}
finally
{
IsBusy = false;
}
}
async Task GetTypeOfClassAsync()
{
if (IsBusy)
{
return;
}
try
{
IsBusy = true;
var timetableElements = await DataService.GetTimetablesAsync();
var typeOfClass = timetableElements[1];
TypeOfClass.ReplaceRange(typeOfClass);
Title = $"Courses available({TypeOfClass.Count}";
}
catch (Exception ex)
{
Debug.WriteLine($"Unable to get TypeOfClass: {ex.Message}");
await Application.Current.MainPage.DisplayAlert("Error!", ex.Message, "OK");
}
finally
{
IsBusy = false;
}
}
async Task GetLocationsAsync()
{
if (IsBusy)
{
return;
}
try
{
IsBusy = true;
var timetableElements = await DataService.GetTimetablesAsync();
var location = timetableElements[2];
Location.ReplaceRange(location);
Title = $"Courses available({Courses.Count}";
}
catch (Exception ex)
{
Debug.WriteLine($"Unable to get Location: {ex.Message}");
await Application.Current.MainPage.DisplayAlert("Error!", ex.Message, "OK");
}
finally
{
IsBusy = false;
}
}
async Task GetCoursesAsync()
{
if (IsBusy)
{
return;
}
try
{
IsBusy = true;
var timetableElements = await DataService.GetTimetablesAsync();
var courses = timetableElements[3];
Courses.ReplaceRange(courses);
Title = $"Courses available({Courses.Count}";
}
catch (Exception ex)
{
Debug.WriteLine($"Unable to get courses: {ex.Message}");
await Application.Current.MainPage.DisplayAlert("Error!", ex.Message, "OK");
}
finally
{
IsBusy = false;
}
}
}
}
My model deserialises the JSON into C# objects and at the moment my ViewModel breaks them up into arrays.
I want to display these objects in a View. The view is a Syncfusion schedule. But at this point, I would just be happy to be able to display these objects in a list view. I also cannot seem to work that out. I have tried:
My View
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:button="clr-namespace:Syncfusion.XForms.Buttons;assembly=Syncfusion.Buttons.XForms"
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"
xmlns:schedule="clr-namespace:Syncfusion.SfSchedule.XForms;assembly=Syncfusion.SfSchedule.XForms"
xmlns:viewmodel="clr-namespace:Timetabler.ViewModels"
mc:Ignorable="d"
Title="{Binding Title}"
x:Class="Timetabler.Views.TimetablePage">
<ContentPage.BindingContext>
<viewmodel:TimetableViewModel/>
</ContentPage.BindingContext>
<ContentPage.ToolbarItems>
<ToolbarItem Text="Add Course" Clicked="AddItem_Clicked" />
</ContentPage.ToolbarItems>
<schedule:SfSchedule x:Name="schedule"
ScheduleView ="WorkWeekView"
TimeIntervalHeight="130"
ShowCurrentTimeIndicator="True"
DataSource="{Binding Courses}">
<schedule:SfSchedule.ViewHeaderStyle>
<schedule:ViewHeaderStyle
BackgroundColor="#FFFFFF"
CurrentDayTextColor="#d1d119"
CurrentDateTextColor="#d1d119"
DayTextColor="#44453e"
DateTextColor="#44453e"
DayFontFamily="Arial"
DateFontFamily="Arial">
</schedule:ViewHeaderStyle>
</schedule:SfSchedule.ViewHeaderStyle>
</schedule:SfSchedule>
As you can see I am trying to Bind to the Courses property that is in my ViewModel. Am I doing something wrong? I added the ContentPage. BindingContext as the correct ViewModel. How am I able to access individual values from within that?
From what I can see the binding is OK, but you're using a custom object. You can't expect the SfScheduler
to know which fields the data need to come from.
As documented here , you will need to tell it which fields to look for in the object to determine properties like the start and end date and time. Ie
<syncfusion:SfSchedule x:Name="schedule">
<syncfusion:SfSchedule.AppointmentMapping>
<syncfusion:ScheduleAppointmentMapping
ColorMapping="color"
EndTimeMapping="To"
StartTimeMapping="From"
SubjectMapping="EventName"
IsAllDayMapping="AllDay"/>
</syncfusion:SfSchedule.AppointmentMapping>
</syncfusion:SfSchedule>
Looking at your TimetableElement
object you will need to add some more properties in there because I am pretty sure that StartTimeMapping
and EndTimeMapping
expect a DateTime
. But I might be wrong there.
Hope this sets you on the right path.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.