How to bind data to Multiple listViews in TabbedPage - xamarin.forms

I have the following:
1) I have a TabbedPage which contain multiple Tabs.
2) Each Tab contains a page which has ListView
3) The data for each of ListView in each Tab base on a single Json result from REST service
Problem:
At where I should call and consume the Json REST web service and how to bind the data to each of the ListView?
say, at Code Behind of TabbedPage.xaml.cs
I start to call the Json Web service
var client = new System.Net.Http.HttpClient();
var response = await client.GetAsync("Http://Rest WebService");
string GeneralJson = await response.Content.ReadAsStringAsync();
GeneralClassforJsonList ObjGeneralList = new GeneralClassForJsonList();
if (GeneralJson != "")
{
ObjGeneralList = JsonConvert.DeserializeObject<GeneralClassforJsonList>(generalList);
}
var Product = ObjectGeneralList.Result.Products
var Service = ObjectGeneralList.Result.Services
I would like to know How to Binding the data to each of the listviews respectively
in product and service pages:
listviewProduct.ItemsSource = Product
listviewService.ItemsSource = Service
Update(1): Approach 1
In TabbedPage, it has 2 or more tab.
<?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:local ="clr-namespace:SembIWIS.View"
x:Class="myApp.MainPage">
<local:ProductPage>
</local:ProductPage>
<local:ServicePage>
</local:ServicePage>
</TabbedPage>
1)
I created a static Class in Model to store Json Result in string
public static class ProductServiceJson
{
public static string StrProductServiceJson;
}
2)
In TabbedPage.xaml.cs (Code Behind)
public partial class MainPage : TabbedPage
{
public MainPage()
{
InitializeComponent();
Init();
}
private void Init()
{
//- code here not execute.
//I start to call the REST api
var response = await client.GetAsync("Http://Rest WebService");
string GeneralJson = await response.Content.ReadAsStringAsync();
ProductServiceJson.StrProductServiceJson = GeneralJson;
}
}
3)
public partial class Product : ContentPage
{
public Product()
{
InitializeComponent();
SetUpListData();
}
private void SetUpListData()
{
//-- code
// how come code here will be executed but not code in tabbedPage.xaml.cs?
}
}
Problem:
1) Why code in TabbedPage.xaml.cs not execute but code in Product.xaml.cs (code behind of Product.xaml)?
2) When code excuted in Product.xaml.cs, the StrProductService is empty or null since web service call in TabbedPage.xaml.cs
3) Will code in Service.xaml.cs be executed?
4) What is the order of execution for TabbedPage, I mean which Tab will have the first execution and if there are 6 tabs?
TIA

Here is how you can do it. I didn't test that code, just wrote to give you an idea.
class ObjGeneral
{
public string Name{get;set;}
}
public class MyViewModel
{
public List<ObjGeneral> MyList{get;set;}
}
Then in page code
var myViewModel=new MyViewModel();
//populate your list
myViewModel.MyList=JsonConvert.DeserializeObject<List<ObjGeneral>>(generalList);
BindingContext=myViewModel;
Then in xaml
<ListView x:Name="toggleListView" ItemsSource="{Binding MyList}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Label x:Name="lblPlaceName" Text="{Binding Name}" />
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>

Related

Binding to ObservableCollection<Tuple<string,string>> results in Error XFC0045 Binding: Property "Item1" not found

I have an ObservableCollection<Tuple<string,string>>. I want to bind this string from the different tuples to my front-end.
When starting the emulator, I get an error that says the following: Error XFC0045 Binding: Property "Item1" not found.
.xaml
<ScrollView>
<CollectionView x:Name="ItemsListView" ItemsSource="{Binding ResultCollection}" SelectionMode="None">
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout>
<!-- Item 1 = UserAnswer -->
<Label Style="{StaticResource SubItem}" Text="{Binding Item1}"/>
<!-- Item 2 = CorrectAnswer -->
<Label Style="{StaticResource SubItem}" Text="{Binding Item2}"/>
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</ScrollView>
ViewModel
UserAnswers and CorrectAnswers are both List.
ResultCollection = new ObservableCollection<Tuple<string, string>>();
for(int i = 0; i < UserAnswers.Count; i++)
{
ResultCollection.Add(new Tuple<string, string>(UserAnswers[i], CorrectAnswers[i]));
}
Can someone help me with the binding of this tuple in my .xaml?
You likely have an x:DataType earlier in the page. Given that, for the binding to work inside the CollectionView, you'll need to also have an x:DataType statement on the ItemTemplate or DataTemplate, specifying its type.
But you can't, if the bound data involves a Generic Type (...<...>).
This is a limitation of x:DataType declaration in XAML.
See Enhancement Allow generics in x:DataType.
Fix #1:
REMOVE ALL x:DataType statements in the XAML file.
PRO: XF will figure out what to do at run-time. Will probably work without further changes in your code.
CON: Possible performance issues, if many items in collection.
Fix #2:
Define a custom class for your items, that has the properties. Use that class name in x:DataType on the ItemTemplate's DataTemplate.
MyTuple.cs:
public class MyTuple : Tuple<string, string>
{
public MyTuple(string item1, string item2) : base(item1, item2)
{
}
}
MyContentPage.xaml:
<ContentPage ...
xmlns:local="clr-namespace:YourNamespaceHere" <-- replace YourNamespaceHere
...>
...
<CollectionView ...
...
<CollectionView.ItemTemplate>
<DataTemplate x:DataType="local:MyTuple">
...
.cs:
public ObservableCollection<MyTuple> ResultCollection { get; set; }
private void Init()
{
ResultCollection = new ObservableCollection<MyTuple> {
new MyTuple("a", "abcabc")
};
}
from the docs
C# tuples, which are backed by System.ValueTuple types, are different
from tuples that are represented by System.Tuple types. The main
differences are as follows:
ValueTuple types are value types. Tuple types are reference types.
ValueTuple types are mutable. Tuple types are immutable.
Data members of ValueTuple types are fields. Data members of Tuple types are properties.
you can only bind to public properties, so binding will only work with System.Tuple
1.Set your ResultCollection property to public.
2.Set the BindingContext in your Background code.
3.Since you set x:name. You can also use similar in your Background code: ItemsListView.ItemsSource = ResultCollection;
I wrote a small example for your reference.
Here is the background code(The xaml code is consistent with the one you provided):
public partial class MainPage : ContentPage
{
public ObservableCollection<Tuple<string, string>> ResultCollection { get; set; }//Set public
public MainPage()
{
InitializeComponent();
ResultCollection = new ObservableCollection<Tuple<string, string>>();
for (int i = 0; i < 5; i++)
{
ResultCollection.Add(new Tuple<string, string>(i.ToString(), i.ToString()));
}
BindingContext = this;// Set BindingContext
}
}

Xamarin.Forms Update Label Text From a Service

I have a Label on MainPage.xaml. I can edit Label Text from MainPage.xaml.cs.
There is a foregroung service running as well. There is function in this service to check a value from SQLite DB for every 10 secs. When value changes, Label text should be updated. I tried binding but it is a bit confusing. I manage updating by using like this: (foreground service timer changes App.SomeValue)
protected override void OnAppearing()
{
lblSyncID.Text = App.SomeValue;
}
But I need to see changes without OnAppearing or any other navigation change.
EDIT:
With #Jason's suggestion I used Messaging Center (and also binding) and it works now:
MainPage.xaml:
<Label Text="{Binding AppWaitingRecordValue}" ...
MainPage.xaml.cs:
public partial class MainPage : ContentPage
{
private string appWaitingRecordValue;
public string AppWaitingRecordValue
{
get { return appWaitingRecordValue; }
set
{
appWaitingRecordValue = value;
OnPropertyChanged(nameof(AppWaitingRecordValue));
}
}
public MainPage()
{
InitializeComponent();
BindingContext = this;
AppWaitingRecordValue = "0";
MessagingCenter.Subscribe<App>((App)Application.Current, "AppRecord", (sender) =>
{
AppWaitingRecordValue = App.recordWaiting.ToString();
});
}
.
.
TimestampService.cs (from Project.Android):
// get i from DB
App.recordWaiting = i;
Xamarin.Forms.MessagingCenter.Send<App>((App)Xamarin.Forms.Application.Current, "AppRecord");

How can I create a TabbedPage from data brought from a web service without blocking the user interface?

I am trying to create a TabbedPage within a MasterDetailPage from data brought from a web service.
The issue with this is that I am blocking the user interface because I have to wait for the task that JSON brings to then iterate it and create the corresponding ViewModels for each ContentPage of the TabbedPage.
My code is something like:
Mi TabbePage:
<?xml version="1.0" encoding="utf-8" ?>
<TabbedPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MyProject.Views.MyTabbedPage"
ItemSource={Binding Tabs}>
<TabbedPage.ItemTemplate>
<DataTemplate>
<ContentPage Title={Binding Description}>
...
</ContentPage>
</DataTemplate>
</TabbedPage.ItemTemplate>
</TabbedPage>
My TabbedPageViewModel:
public TabbedPageViewModel()
{
var task = Task.Run(async () => { await LoadThings(); });
Task.WaitAll(task);
Tabs = new ObservableCollection<TabbedPageDetailViewModel>();
foreach (var t in Things)
{
Tabs.Add(new TabbedPageDetailViewModel(t.IdCode, t.Description));
}
}
private async Task LoadThings()
{
Things = new List<Thing>(await App.WebApiManager.GetCustomerThingsAsync(App.User.IdCustomer));
}
My TabbedPageDetailViewModel:
public TabbedPageDetailViewModel(string idCode, string description)
{
IdCode = idCode;
Description = description;
Task task1 = Task.Run(async () => await LoadTask1(idCode));
var task2 = Task.Run(async () => { await LoadTask2(idCode); });
Task.WaitAll(task1, task2);
}
private async Task LoadTask1(string idCode)
{
//await code that brings API data to load controls
}
private async Task LoadTask(string idCode)
{
// await code that brings API data to load controls
}
All this code blocks the user interface while the data is consumed from the API.
How can I implement this in a correct synchronous way which allows my interface to remain reactive?
PS: I can download my collection of Things in the login process of my app but I still get some delay when TabbedPageDetailViewModel is being instantiated.
How about add the activityindicator in the contentPage? For example:
<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"
x:Class="App526.TabbedPage1">
<!--Pages can be added as references or inline-->
<ContentPage Title="{Binding des}" >
<ActivityIndicator IsRunning="true" x:Name="page1ActIndictor" />
</ContentPage>
<ContentPage Title="{Binding name}" >
<ActivityIndicator IsRunning="true" x:Name="page2ActIndictor" />
</ContentPage>
<ContentPage Title="{Binding age}" >
<ActivityIndicator IsRunning="true" x:Name="page3ActIndictor" />
</ContentPage>
</TabbedPage>
And in your code behind, use messagingCenter to stop the activityindicator :
public TabbedPage1()
{
InitializeComponent();
MessagingCenter.Subscribe<Object>(this, "dataLoadFinish", (sender) =>
{
page1ActIndictor.IsRunning = false;
page2ActIndictor.IsRunning = false;
page3ActIndictor.IsRunning = false;
});
}
Send the message when loading finished:
public class TabbedPageDetailViewModel
{
//...
public void loadingTask() {
//After finish loading data
MessagingCenter.Send<Object>(this, "dataLoadFinish");
}
//...
}

Xamarin Forms -> Activity Indicator not working if Commands of statements to be executed

Using Visual Studio 2017 Community 15.8.1
This is after going through all options of stackoverflow regarding ActivityIndicator. So though it may be a duplication but nothing is helping me out.
So finally decided to post my workouts and get best help from here.
What I have tried till now:-
1. {Binding IsLoading} + INotifyPropertyChanged + public void RaisePropertyChanged(string propName) + IsLoading = true; concept.
2. ActivityIndicator_Busy.IsVisible = false; (Direct control accessed)
These two approaches were mostly recommended and I went into depth of each since lot of hours in last few weeks. But nothing got crack.
What I achieved?:-
ActivityIndicator_Busy.IsVisible = false; concept is working smooth only when I put return before executing the statements (for testing purpose); statement on Button Clicked event. (Attached Image)
But as soon as I remove the return; On Pressing Button, directly after some pause, the HomePage Opens.
MY Questions:-
1. This is particular with the current scenario how to get the ActivityIndicator run Immediately when user clicks the Welcome Button.
2. Pertaining to same, When app starts there is also a blank white screen coming for few seconds almost 30 seconds which I also I want to show ActivityIndicator. But dont know how to impose that logic at which instance.
My Inputs
My MainPage.xaml File:-
(Edited 06-Sept-2018 09.11 pm)
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage x:Name="page_main_page"
NavigationPage.HasBackButton="False"
NavigationPage.HasNavigationBar="False"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:appNutri"
BindingContext="{x:Reference page_main_page}"
x:Class="appNutri.MainPage">
<ContentPage.Content>
<StackLayout BackgroundColor="White"
HorizontalOptions="FillAndExpand"
VerticalOptions="FillAndExpand">
<StackLayout>
<Image x:Name="Image_Welcome"
Source="welcome.png"
HorizontalOptions="CenterAndExpand"
VerticalOptions="CenterAndExpand"
WidthRequest="300"
HeightRequest="300" />
<Button x:Name="Button_Welcome"
Clicked="Button_Welcome_Clicked"
Text="Welcome!"
BackgroundColor="DeepSkyBlue"
HorizontalOptions="CenterAndExpand"
VerticalOptions="CenterAndExpand"
TextColor="White"
HeightRequest="60" />
</StackLayout>
<StackLayout BackgroundColor="White"
HorizontalOptions="FillAndExpand"
VerticalOptions="FillAndExpand">
<ActivityIndicator
x:Name="ActivityIndicator_Busy"
Color="Black"
IsEnabled="True"
HorizontalOptions="Center"
VerticalOptions="Center"
IsRunning="{Binding Source={x:Reference page_main_page}, Path=IsLoading}"
IsVisible="{Binding Source={x:Reference page_main_page}, Path=IsLoading}" />
</StackLayout>
</StackLayout>
</ContentPage.Content>
</ContentPage>
My MainPage.cs Code:-
(Edited on 06-Sept-2018 09.13 pm)
using appNutri.Model;
using SQLite;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Data.SqlClient;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;
namespace appNutri
{
public partial class MainPage : Xamarin.Forms.ContentPage, INotifyPropertyChanged
{
private bool isLoading;
public bool IsLoading
{
get
{
return isLoading;
}
set
{
isLoading = value;
RaisePropertyChanged("IsLoading");
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string propName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
public MainPage()
{
InitializeComponent();
BindingContext = this;
}
protected override void OnAppearing()
{
base.OnAppearing();
BindingContext = this;
}
protected async void Button_Welcome_Clicked(object sender, EventArgs e)
{
IsLoading = true;
await Select_Local_User_Information();
IsLoading = false;
}
private async Task Select_Local_User_Information()
{
IsLoading = true;
string where_clause = "";
try
{
Sql_Common.Database_Folder_Path = System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal);
string Database_Full_Path = Path.Combine(Sql_Common.Database_Folder_Path, Sql_Common.Database_Name);
SQLiteConnection connection = new SQLiteConnection(Database_Full_Path);
//connection.DropTable<User_Master>();
//connection.Delete(connection.Table<User_Master>());
//connection.CreateTable<User_Master>(CreateFlags.ImplicitPK | CreateFlags.AutoIncPK);
connection.CreateTable<User_Master>();
int count = connection.ExecuteScalar<int>("Select count(*) from User_Master");
if (count == 0)
{
connection.DropTable<User_Master>();
connection.CreateTable<User_Master>();
//IsLoading = false;
//IsBusy = false;
await Navigation.PushAsync(new User_Register_Page());
}
else
{
Sql_Common.User_Logged = true;
var Local_User_Data = connection.Table<User_Master>().ToList();
User_Master.Logged_User_Details_Container.First_Name = Local_User_Data[0].First_Name;
User_Master.Logged_User_Details_Container.Cell1 = Local_User_Data[0].Cell1;
where_clause = " Upper ( First_Name ) = " + "'" + User_Master.Logged_User_Details_Container.First_Name.ToUpper().Trim() + "'" + " and " +
" Cell1 = " + "'" + User_Master.Logged_User_Details_Container.Cell1.Trim() + "'";
int records = Sql_Common.Get_Number_Of_Rows_Count("User_Master", where_clause);
if (records == 0)
{
connection.DropTable<User_Master>();
connection.CreateTable<User_Master>();
IsLoading = false;
await Navigation.PushAsync(new User_Register_Page());
}
else
{
User_Master.User_Master_Table(where_clause, User_Master.Logged_User_Details_Container);
IsLoading = false;
await Navigation.PushAsync(new User_Home_Page());
}
}
connection.Close();
}
catch (SQLiteException ex)
{
string ex_msg = ex.Message;
}
IsLoading = false;
}
}
}
04-Oct-2018
Finally resolved with This Answer
Update 2018-09-10
You think that you have implemented INotifyPropertyChanged by adding INotifyPropertyChanged to your class definition and adding the event
public event PropertyChangedEventHandler PropertyChanged;
along with its event invocator
public void RaisePropertyChanged(string propName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
Anyway, since ContentPage already implements INotifyPropertyChanged, adding those did not implement INotifyPropertyChanged. ContentPage already defines the event (or rather BindableObjectfrom which ContentPage indirectly inherits). Any object that relies on being informed about property changes in your page will subscribe to the PropertyChanged event of the ancestor and not the PropertyChanged event you defined, hence the ActivityIndicator will not update.
Just remove the event you defined and call OnPropertyChanged instead of RaisePropertyChanged() and you should be fine.
private bool isLoading;
public bool IsLoading
{
get
{
return isLoading;
}
set
{
isLoading = value;
OnPropertyChanged();
}
}
Since OnPropertyChanged is declared as
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
you don't have to pass the property name by hand. The compiler will do that for you beacsue of the CallerMemberNameAttribute.
End Update
The XAML extension {Binding IsLoading} binds the ActivityIndicator to the BindingContext of your page. By default the BindingContext is null, hence there is nothing to bind to and all your efforts are to no avail.
With a viewmodel
The preferred solution would be to use a viewmodel and assign it to MainPage.BindingContext, e.g.
var page = new MainPage()
{
BindingContext = new MainPageViewModel()
}
but if you take that road, you should move all of your UI logic to that viewmodel and encapsulate your SQL access and business logic in other classes, to keep the viewmodel clean from resource accesses and business logic. Having the resource accesses and logic in code behind may work for that small example, but is likely to become an unmaintainable mess.
Without a viewmodel
Anyway, you don't have to use a viewmodel to use bindings. You can set the BindingContext for the page (or some children) or use the Source of the BindingExtension
Setting the BindingContext
The BindingContext is passed from any page or view to it's children. You first have to give your page a name with x:Name="Page" (don't have to use Page, anyway, you can't use the class name of your page) and set the BindingContext to that page
<ContentPage ...
x:Name="Page"
BindingContext="{x:Reference Page}"
...>
now binding to IsLoading should work.
Using Source in the Binding
If you want to reference something else than the BindingContext of a view, BindingExtension has a property Source. You have to give a name to your page, too (see above)
<ContentPage ...
x:Name="Page"
...>
and now you can reference this in your binding
<ActivityIndicator
...
IsRunning="{Binding Path=IsLoading, Source={x:Reference Page}}"
IsVisible="{Binding Path=IsLoading, Source={x:Reference Page}}"/>

Xamarin.Forms UserControl using XAML and Custom Renderer

There are a few good examples already of how to create a "custom control" by -
Deriving a Class from View or an existing built-in control and then creating a custom renderer for it per platform.
http://blog.xamarin.com/using-custom-controls-in-xamarin.forms-on-android/
http://developer.xamarin.com/guides/cross-platform/xamarin-forms/custom-renderer/
I want to create a "compound custom control OR usercontrol" which contains multiple elements which are defined in XAML (in the shared code), and then customised with a renderer (to say tweak the styling per platform).
Does anyone have an example of doing this please? A simple example with a view that has a bindable label and an entry box should be enough to show the main principles.
Here is what I have so far -
Defined a ContentView to represent our usercontrols layout and contents.
<ContentView xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="News.Forms.VisualNewsContentView">
<ContentView.Content>
<StackLayout>
<Label x:Name="MyLabel" Text="Label"></Label>
<Entry x:Name="MyEntry" Text="Entry"></Entry>
</StackLayout>
</ContentView.Content>
</ContentView>
with codebehind -
public partial class VisualNewsContentView : ContentView
{
public VisualNewsContentView ()
{
InitializeComponent ();
}
// Not sure if I need this to access Entry ...
public Entry GetEntry ()
{
return MyEntry;
}
}
Add an Android Custom Renderer for that ContentView, how do I access and customise natively parts / controls of the ContentView?
[assembly:ExportRenderer (typeof(VisualNewsContentView), typeof(VisualNewsRenderer))]
namespace News.Forms.Android
{
public class VisualNewsRenderer: ViewRenderer
{
public VisualNewsRenderer () { }
protected override void OnModelChanged (VisualElement oldModel, VisualElement newModel)
{
base.OnModelChanged (oldModel, newModel);
if (newModel != null) {
VisualNewsContentView newsContentView = newModel as VisualNewsContentView;
// i.e. How could I get hold of EditText etc so I could natively customise its appearance? When you use a built in renderer like EntryRenderer you can use Control to access native control.
Console.WriteLine (newsContentView.GetLabel ().Text);
EditText ed = (EditText)newsContentView.GetEntry ().???
}
}
}
}
Just can't quite get the pieces together to work, the ContentView seems to render fine on page but cannot work out how to access its Child Native controls in the viewrenderer.
Be nice to also show how you can use Binding for the Label and Entry Text values.
I do not want to have to define a custom renderer for each single label / entry etc of the usercontrol.
Is this what you meant?
Some properties to access the Xamarin.Forms controls:
public partial class VisualNewsContentView : ContentView
{
public VisualNewsContentView()
{
InitializeComponent();
}
public Label Label
{
get
{
return MyLabel;
}
set
{
MyLabel = value;
}
}
public Entry Entry
{
get
{
return MyEntry;
}
set
{
MyEntry = value;
}
}
}
Some magic inside the Renderer to customize the controls on the page:
[assembly:ExportRenderer (typeof(VisualNewsContentView), typeof(VisualNewsRenderer))]
namespace News.Forms.Android
{
public class VisualNewsRenderer: ViewRenderer
{
public VisualNewsRenderer () { }
protected override void OnModelChanged (VisualElement oldModel, VisualElement newModel)
{
base.OnModelChanged (oldModel, newModel);
if (newModel != null) {
VisualNewsContentView newsContentView = newModel as VisualNewsContentView;
newsContentView.Label.Text = "It´s some kind of..";
newsContentView.Entry.Text = "MAGIC!";
newsContentView.Entry.BackgroundColor = Color.Blue;
newsContentView.Entry.RotationX = 180;
newsContentView.Entry.Focus();
}
}
}
}
EDIT:
I don't know if it's possible to map your controls from the XAML-page to native controls. You could add the controls which you want to customize natively # the renderer.
[assembly:ExportRenderer (typeof(VisualNewsContentView), typeof(VisualNewsRenderer))]
namespace News.Forms.Android
{
public class VisualNewsRenderer: NativeRenderer
{
public VisualNewsRenderer () { }
protected override void OnModelChanged (VisualElement oldModel, VisualElement newModel)
{
base.OnModelChanged (oldModel, newModel);
if (newModel != null) {
LinearLayout layout = new LinearLayout (Application.Context);
layout.Orientation = Orientation.Vertical;
TextView tv = new TextView (Application.Context);
tv.Ellipsize = TextUtils.TruncateAt.Middle;
tv.Text = "It´s some kind of..";
EditText et = new EditText (Application.Context);
et.SetTextColor (Graphics.Color.Chocolate);
et.Text = "MAGIC!";
layout.AddView (tv);
layout.AddView (et);
SetNativeControl (layout);
}
}
}
}
But like this you won't be using your ContentView.. I'm sorry, I have nothing better than this..
My solution for customizing compound user control is make a custom control for each control used in compound user control.
For example, which this control:
<ContentView xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="News.Forms.VisualNewsContentView">
<ContentView.Content>
<StackLayout>
<Label x:Name="MyLabel" Text="Label"></Label>
<Entry x:Name="MyEntry" Text="Entry"></Entry>
</StackLayout>
</ContentView.Content>
</ContentView>
I will do something like this:
<ContentView xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:CustomControls="clr-namespace:App.CustomControls;assembly=App" x:Class="News.Forms.VisualNewsContentView">
<ContentView.Content>
<CustomControls:StackLayout>
<CustomControls:Label x:Name="MyLabel" Text="Label"></CustomControls:Label>
<CustomControls:Entry x:Name="MyEntry" Text="Entry"></CustomControls:Entry>
</CustomControls:StackLayout>
</ContentView.Content>
</ContentView>
Example class for CustomControls:StackLayout is:
(in StackLayout.cs)
using Xamarin.Forms;
namespace App.CustomControls
{
public class StackLayout : Xamarin.Forms.StackLayout
{
}
}
(in StackLayoutRenderer.cs for android project)
[assembly: ExportRenderer(typeof(App.CustomControls.StackLayout), typeof(App.Droid.CustomRenderers.StackLayoutRenderer))]
namespace App.Droid.CustomRenderers.MapView
{
public class StackLayoutRenderer : ViewRenderer<StackLayout, Android.Widget.LinearLayout>
{
protected override void OnElementChanged(ElementChangedEventArgs<StackLayout> e)
{
base.OnElementChanged(e);
}
}
}

Resources