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.
Related
I'm getting two collections of data from a web service - the full collection and then a subset of items. The subset should be selected on load. I cannot get the SelectedItems to show as selected in the CollectionView, event though that property is set. I've looked over the Monkeys example and all over the web but can't find an example just like this
View:
<RefreshView IsRefreshing="{Binding IsBusy, Mode=TwoWay}" Command="{Binding LoadItemsCommand}">
<CollectionView x:Name="ExperiencesCollectionView"
ItemsSource="{Binding Experiences}"
SelectedItems="{Binding ProfileExperiences, Mode=TwoWay}"
SelectionMode="Multiple">
<CollectionView.ItemTemplate>
<DataTemplate>
<Label
Text="{Binding Description}"/>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</RefreshView>
</StackLayout>
Code Behind:
public partial class ExperiencesPage : ContentPage
{
ExperiencesViewModel viewModel;
public ExperiencesPage()
{
InitializeComponent();
BindingContext = viewModel = new ExperiencesViewModel();
}
protected override void OnAppearing()
{
base.OnAppearing();
if (viewModel.Experiences.Count == 0)
viewModel.IsBusy = true;
}
ViewModel:
public class ExperiencesViewModel : BaseViewModel
{
public ObservableCollection<Experience> Experiences { get; set; }
public ObservableCollection<object> ProfileExperiences { get; set; }
public Command LoadItemsCommand { get; set; }
public string InterestedIn { get; set; }
public ExperiencesViewModel()
{
Title = "My Experiences";
Experiences = new ObservableCollection<Experience>();
LoadItemsCommand = new Command(async () => await ExecuteLoadItemsCommand());
}
async Task ExecuteLoadItemsCommand()
{
IsBusy = true;
try
{
Experiences.Clear();
//Get these from the REST service
var experiences = await App.ExperienceManager.GetExperiencesAsync();
var serviceExperiences = await App.ExperienceManager.GetProfileExperiencesAsync();
foreach (var exp in experiences)
{
Experiences.Add(exp);
}
foreach (var profileExp in serviceExperiences)
{
ProfileExperiences.Add(profileExp);
}
}
catch (Exception ex)
{
Debug.WriteLine(ex);
}
finally
{
IsBusy = false;
}
}
I followed this example https://medium.com/swlh/xamarin-forms-mvvm-how-to-work-with-sqlite-db-c-xaml-26fcae303edd
I put a breakpoint in my OnRouteSelected event handler, and e.SelectedItem has the selected object, even though the ListView doesn't display it.
Am I missing something glaring below?
Here is my code:
RoutesPage.xaml
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml">
<ContentPage.Content>
<ListView ItemsSource="{Binding Routes}" SelectedItem="{Binding SelectedRoute, Mode=TwoWay}" HasUnevenRows="False" SeparatorColor="Gray" ItemSelected="OnRouteSelected">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<ViewCell.View>
<Label TextColor="Black" Text="{Binding ROName}"/>
</ViewCell.View>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ContentPage.Content>
</ContentPage>
RoutesPage.xaml.cs
public partial class RoutesPage : ContentPage
{
public RoutesPage()
{
InitializeComponent();
var routeStore = new RouteStore(DependencyService.Get<ISQLiteDb>());
var pageService = new PageService();
ViewModel = new RoutesPageViewModel(routeStore, pageService);
}
protected override void OnAppearing()
{
base.OnAppearing();
ViewModel.LoadDataCommand.Execute(null);
}
void OnRouteSelected(object sender, SelectedItemChangedEventArgs e)
{
ViewModel.SelectRouteCommand.Execute(e.SelectedItem);
}
public RoutesPageViewModel ViewModel
{
get { return BindingContext as RoutesPageViewModel; }
set { BindingContext = value; }
}
}
RoutesPageViewModel.cs The LoadData() method gets the data and adds it to the Routes Collection successfully.
public class RoutesPageViewModel : BaseViewModel
{
private RouteViewModel _selectedRoute;
private IRouteStore _routeStore;
private IPageService _pageService;
private bool _isDataLoaded;
public ObservableCollection<RouteViewModel> Routes { get; private set; }
= new ObservableCollection<RouteViewModel>();
public RouteViewModel SelectedRoute
{
get { return _selectedRoute; }
set { SetValue(ref _selectedRoute, value); }
}
public ICommand LoadDataCommand { get; private set; }
public ICommand AddRouteCommand { get; private set; }
public ICommand SelectRouteCommand { get; private set; }
public ICommand DeleteRouteCommand { get; private set; }
public ICommand CallRouteCommand { get; private set; }
public RoutesPageViewModel(IRouteStore routeStore, IPageService pageService)
{
_routeStore = routeStore;
_pageService = pageService;
LoadDataCommand = new Command(async () => await LoadData());
AddRouteCommand = new Command(async () => await AddRoute());
SelectRouteCommand = new Command<RouteViewModel>(async c => await SelectRoute(c));
}
private async Task LoadData()
{
if (_isDataLoaded)
return;
_isDataLoaded = true;
var routes = await _routeStore.GetRoutesAsync();
foreach (var route in routes)
Routes.Add(new RouteViewModel(route));
}
private async Task AddRoute()
{
// await _pageService.PushAsync(new RoutesDetailPage(new RouteViewModel()));
}
private async Task SelectRoute(RouteViewModel route)
{
if (route == null)
return;
SelectedRoute = null;
// await _pageService.PushAsync(new RoutesDetailPage(route));
}
}
The property in the viewmodels are being set like this:
private string _roName;
public string ROName
{
get { return _roName; }
set
{
SetValue(ref _roName, value);
OnPropertyChanged(nameof(ROName));
}
}
The constructor:
public RouteViewModel(Route route)
{
//other properties
ROName = route.ROName;
}
I have an api URL here which provides the response below.
Json
{"status":200,"message":"Operation done successfully","data":{"enableNext":false,"products":[{"image":"http://bresa.lazyhost.in/upload/product/1/Tjtqr8.jpg","id":1,"code":"PROi6v8X5261","name":"Spandex Stretch Lounge Sofa with Couch Seat Cover SlipCover","description":"nice sofa , very comfortable for sitting in Living room","tags":"chairs, sofa, ","price":"1000.00","quantity":100,"images":["http://bresa.lazyhost.in/upload/product/1/Tjtqr8.jpg"]}]}}
The model class for above response is below.
CarouselModel
namespace CameliaMaison.Models
{
public partial class CarouselModel
{
[JsonProperty("status")]
public long Status { get; set; }
[JsonProperty("message")]
public string Message { get; set; }
[JsonProperty("data")]
public List<CarouselData> Carouseldata { get; set; }
}
public partial class CarouselData
{
[JsonProperty("id")]
public long Id { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("path")]
public string Path { get; set; }
}
}
The ViewModel
namespace CameliaMaison.ViewModels
{
public class CarouselImagesViewModel
{
private List<CarouselData> items;
public List<CarouselData> Items
{
get { return items; }
set
{
items = value;
}
}
public CarouselImagesViewModel()
{
var responseObj = MyHTTP.GetApiData().Result;
foreach(CarouselData item in responseObj){
Items.Add(item);
}
}
}
public class MyHTTP
{
public static async Task<List<CarouselData>> GetApiData()
{
HttpClient httpClient = new HttpClient();
HttpResponseMessage response = await httpClient.GetAsync("http://bresa.lazyhost.in/api/banners");
CarouselModel categoriesData = new CarouselModel();
var content = await response.Content.ReadAsStringAsync();
if (response.StatusCode == System.Net.HttpStatusCode.OK)
{
categoriesData = JsonConvert.DeserializeObject<CarouselModel>(await response.Content.ReadAsStringAsync());
}
return categoriesData.Carouseldata;
}
}
}
I need to parse the JSON and store in Model Object and populate the data in the listview via MVVM.However, there seems to be something wrong with the implementation. I am unable to figure out and the content page class is below.
ProductsListPage.xaml.cs
public partial class ProductsListPage : ContentPage
{
public ProductsListPage()
{
InitializeComponent();
}
}
ProductsListPage.xaml
<?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:CameliaMaison;assembly=CameliaMaison"
x:Class="CameliaMaison.Views.ProductsListPage"
Title="Employee List">
<ListView x:Name="ListView">
<ListView.ItemTemplate>
<DataTemplate>
<TextCell Text="ABC" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ContentPage>
First you must have a button on your view page.
<Button Command = "{Binding GetInfoCommand}" />
Than you must create a view-model. Something like this :
public class CarouselModel: INotifyPropertyChanged
{
//get a reference from the another class where you make the call to get the json
Tasks ts = new Tasks();
List<CarouselData> _carouseldata ;
public List<CarouselData> CarouselData
{
get { return _carouseldata ; }
set
{
if (value == _carouseldata ) return;
_carouseldata = value;
OnPropertyChanged();
}
}
public ICommand GetIInfoComand
{
get
{
return new Command(async()=>
{
CarouselData= await _apiServices.GetInfo();
});
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Than in your tasks.cs class you must consume the call where you get the json. This simple. :)
==edit==
In your view you must replace your textcell.Something like this :
<ListView x:Name="ListView">
<ListView.ItemTemplate>
<DataTemplate>
<TextCell Text="{Binding Name }" />
</DataTemplate>
</ListView.ItemTemplate>
Also you get a reference of your view model and place it before the listview :
<ContentPage.BindingContext>
<CarouselModel/>
</ContentPage.BindingContext>
I am developing an app with Xamarin.Forms, trying to leverage ReactiveUI, but Xamarin.Forms ListView does not behave as expected.
The test setup is an Entry field which I input values into. I subscribe to changes on the ReactiveList and add the value to a ListView.
The problem: The ListView updates for a few seconds and then just stops.
Console still logs inputs.
Sample code below:
<!-- xaml layouts omitted for brevity -->
<Entry x:Name="searchbox" HorizontalOptions="FillAndExpand"
Text="{Binding SearchQuery, Mode=TwoWay}"
/>
<ListView x:Name="ResultView">
<ListView.ItemTemplate>
<DataTemplate>
<TextCell Text="{Binding Address}"></TextCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
ViewModel + SearchResult class:
public class SearchViewModel : ReactiveObject
{
public ReactiveList<SearchResult> SearchResults { get; set; }
private string searchQuery;
public string SearchQuery
{
get { return searchQuery; }
set { this.RaiseAndSetIfChanged(ref searchQuery, value); }
}
public ReactiveCommand<List<SearchResult>> Search { get; set; }
public SearchViewModel()
{
// Set up our ListView data list
this.SearchResults = new ReactiveList<SearchResult>();
this.SearchResults.ChangeTrackingEnabled = true;
Search = ReactiveCommand.CreateAsyncTask(async _ => {
return await GenerateSearchResultAsync(this.SearchQuery);
});
Search.Subscribe(results => {
SearchResults.Clear(); // just replace output every time
SearchResults.AddRange(results);
// output results to console
results.ForEach(r => Console.WriteLine(r.Address));
});
// this used to contain a condition I removed for brevity
this.WhenAnyValue(x => x.SearchQuery).InvokeCommand(this, x => x.Search);
}
// create a new result list and return it, async code removed for demo-simplicity
private static async Task<List<SearchResult>> GenerateSearchResultAsync(string value)
{
var rv = new List<SearchResult>();
rv.Add(new SearchResult(value + " " + DateTime.Now.ToString("hh:mm:ss.FFFF")));
return rv;
}
}
public class SearchResult
{
private string address;
public SearchResult(string s)
{
this.Address = s;
}
public string Address { get; set; }
}
This seems to be a bug when WeakReferences are mistakenly collected during GC.
I'm not knowledgable enough to do the debugging, but some other smart guys did:
https://github.com/reactiveui/ReactiveUI/issues/806
https://bugzilla.xamarin.com/show_bug.cgi?id=31415 (open xamarin bug report)
Workaround:
Add this class to your project:
public class ReactiveObservableCollection<T> : ReactiveList<T>
{
public ObservableCollection<T> ObservableCollection { private set; get; }
public ReactiveObservableCollection()
{
this.ObservableCollection = new ObservableCollection<T>();
ItemsAdded.Subscribe(ObservableCollection.Add);
ItemsRemoved.Subscribe((x) => ObservableCollection.Remove(x));
}
}
and use it instead of the ReactiveList:
public class ViewModel : ReactiveObject {
public ReactiveObservableCollection<SearchResult> SearchResults { set; get; }
public ViewModel()
{
// use ReactiveObservableCollection instead of ReactiveList
this.SearchResults = new ReactiveObservableCollection<SearchResult>()
{
ChangeTrackingEnabled = true
};
}
Also, make sure to use the .ObservableCollection property on the collection as ItemSource (instead of just the collection)!
this.ResultView.ItemsSource = viewModel.SearchResults.ObservableCollection;
instead of
this.ResultView.ItemsSource = viewModel.SearchResults;
This should solve the problem.
HTH!
Hy
I would like to share my approach for a custom xamarin.forms control within a Xamarin PCL Project with MVVM-Light. What do you think about it?
Custom Control -> PersonPanel.xaml:
<?xml version="1.0" encoding="utf-8" ?>
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="xxx.PersonPanel">
<StackLayout Orientation="Vertical">
<Label x:Name="titleLabel" Text="{Binding TitleLabel}"/>
<Entry x:Name="filterText" Placeholder="{Binding FilterPlaceholderText}" Text="{Binding Filter.Lookup}" TextChanged="OnFilterTextChanged"/>
<Label x:Name="resultText" Text="{Binding ResultText}" IsVisible="{Binding ResultTextVisible}"/>
</StackLayout>
</ContentView>
Code-Behind -> PersonPanel.xaml.cs:
public partial class PersonPanel : ContentView
{
public PersonPanel()
{
InitializeComponent();
//Init ViewModel
BindingContext = ServiceLocator.Current.GetInstance<PersonPanelViewModel>();
}
private PersonPanelViewModel PersonPanelViewModel
{
get
{
return (PersonPanelViewModel)BindingContext;
}
}
public string TitleLabel
{
get
{
return PersonPanelViewModel.TitleLabel;
}
set
{
PersonPanelViewModel.TitleLabel = value;
}
}
public string FilterPlaceholderText
{
get
{
return PersonPanelViewModel.FilterPlaceholderText;
}
set
{
PersonPanelViewModel.FilterPlaceholderText = value;
}
}
private void OnFilterTextChanged(object sender, EventArgs e)
{
PersonPanelViewModel.SearchCommand.Execute(null);
}
}
ViewModel -> PersonPanelViewModel:
public class PersonPanelViewModel : ViewModelBase
{
private IPersonService _personService;
private decimal _personId = 0;
private string _titleLabel = string.Empty;
private string _filterPlaceholderText = string.Empty;
private string _resultText = string.Empty;
private bool _resultTextVisible = true;
public PersonPanelViewModel(IPersonService personService)
{
_personService = personService;
// Init Filter
Filter = new PersonFilter();
// Init Commands
SearchCommand = new RelayCommand(Search);
}
public ICommand SearchCommand { get; set; }
public PersonFilter Filter
{
get;
private set;
}
public string ResultText
{
get
{
return _resultText;
}
set
{
Set(() => ResultText, ref _resultText, value);
}
}
public bool ResultTextVisible
{
get
{
return _resultTextVisible;
}
set
{
Set(() => ResultTextVisible, ref _resultTextVisible, value);
}
}
public string FilterPlaceholderText
{
get
{
return _filterPlaceholderText;
}
set
{
Set(() => FilterPlaceholderText, ref _filterPlaceholderText, value);
}
}
public string TitleLabel
{
get
{
return _titleLabel;
}
set
{
Set(() => TitleLabel, ref _titleLabel, value);
}
}
public decimal PersonId
{
get
{
return _PersonId;
}
set
{
Set(() => PersonId, ref _PersonId, value);
}
}
private async void Search()
{
//Reset
ResultText = string.Empty;
ResultTextVisible = false;
PersonId = 0;
if (Filter.PersonLookup != null && Filter.PersonLookup.Length >= 3)
{
//Call to Person Service
List<PersonResult> Person = await _personService.FindpersonByFilter(Filter);
if (Person.Count == 1)
{
PersonId = Person[0].PersonId;
ResultText = Person[0].PersonName;
ResultTextVisible = true;
}
}
}
}
Using of Control in another View:
<?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:components="clr-namespace:xxx.ViewElements.Components"
x:Class="xxx.MainPage">
<StackLayout Orientation="Vertical">
<components:PersonPanel x:Name="personPanel" TitleLabel="Person" FilterPlaceholderText="Type your search criteria here..."/>
</StackLayout>
</ContentPage>
I'm using Autofac as the IOC Container.
What do you think about it? I am using MVVM the right way (it's very new to me)?
Is there a better way to deal with calling the Command from the Event (TextChanged) on the view?
What's about the properties in the Code-Behind (which do a routing to the ViewModel)?
Edit:
I'll try to describe, what I want to achieve:
Creating our own control (reusable in different views, cross-platform) -> PersonPanel.xaml
Written in XAML in our PCL Project with pure Xamarin.Forms controls in it
The control should have it's own commands (Search) and properties
One of the commands is using a Service
The control should get the Service (as an Interface) injected through IOC
The Service itself is also implemented in the PCL Project and makes a REST Call to a Webservice
The Result of the Call is than set to a property of the control -> ResultText Property
The Result is visible to the User
-> With the above implementation, all of that works, but I'm not sure if this is the right way...
Thanks for your help!
Kind regards,
Peter
The approach for mapping the event to the command is exactly as I would perform.
The rest is a little bit confusing. The general pattern is to create bindable properties in your control that are exposed to the view model when instantiated within the host view. A very basic sample structure is below:
public class TestLabelControl : Label
{
public static readonly BindableProperty TestTitleProperty = BindableProperty.Create< TestLabelControl, string> (p => p.TestTitle, null);
public string TestTitle {
get {
return (object)GetValue (TestTitleProperty);
}
set {
SetValue (TestTitleProperty, value);
}
}
}
public class TestContentPage : ContentPage
{
public TestContentPage()
{
var testLabel = new TestLabel();
testLabel.SetBinding<TestContentPageViewModel>(TestLabel.TestTitleProperty, vm => vm.TestLabelTitle, BindingMode.Default);
Content = testLabel;
}
}
public class TestContentPageViewModel
{
public string TestLabelTitle{get;set;}
public TestContentPageViewModel()
{
TestLabelTitle = "Something random";
}
}
You would then create the native renderers to handle the drawing on each platform.
By following this approach you keep the code separated and concise. It does seem a slightly long winded way of getting things done but it is highly scalable and configurable.