Databinding doesnt display anything - xamarin

I am trying to print out observable collection into the listview.
It is for custom navigation. It should output 2 buttons with icon and one button should have active element.
For some reason I am not able to see anything. It works without problem when I use same code in Main.xaml/Main.xaml.cs. Maybe there is limitation on Application class that prevents databinding?
App.Xaml
<!--test-->
<ListView ItemsSource="{Binding NavigationItemss}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout>
<Label Text="{Binding Icon}"></Label>
<Label Text="Testing"></Label>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<!--test-->
App.xaml.cs
public partial class App : Application
{
//TODO: Replace with *.azurewebsites.net url after deploying backend to Azure
public static string AzureBackendUrl = "http://localhost:5000";
public static bool UseMockDataStore = true;
Navigation AppNavigation = new Navigation();
public App()
{
InitializeComponent();
if (UseMockDataStore)
DependencyService.Register<MockDataStore>();
else
DependencyService.Register<AzureDataStore>();
this.BindingContext = AppNavigation;
MainPage = new NavigationPage(new Main());
}
Navigation.cs
using HOT_App.Views;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Text;
using Xamarin.Forms;
namespace HOT_App.ViewModels
{
class Navigation
{
//public List<NavigationItem> NavigationItems;
public ObservableCollection<NavigationItem> NavigationItemss { get; set; }
public Navigation()
{
NavigationItemss = new ObservableCollection<NavigationItem>();
//NavigationItems = new List<NavigationItem>();
NavigationItem Home = new NavigationItem("Home","NavigationHome.png",true);
NavigationItem Trends = new NavigationItem("Trends","NavigationTrend.png",false);
NavigationItemss.Add(Home);
NavigationItemss.Add(Trends);
//NavigationItems.Add(Home);
//NavigationItems.Add(Trends);
}
public void ShowActiveNavigation()
{
System.Diagnostics.Debug.WriteLine(string.Join<NavigationItem>("\n", NavigationItemss));
}
public void SetActiveNavigation(string activeNavigationName)
{
ChangeActiveValue(activeNavigationName);
switch (activeNavigationName)
{
case "Home":
//activeNavigation = new NavigationItem("Home");
Application.Current.MainPage.Navigation.PushAsync(new Main(), false);
System.Diagnostics.Debug.WriteLine("Home");
break;
case "Trends":
//activeNavigation = new NavigationItem("Trends");
Application.Current.MainPage.Navigation.PushAsync(new Trends(), false);
System.Diagnostics.Debug.WriteLine("Trends");
break;
default:
//activeNavigation = new NavigationItem("Home");
Application.Current.MainPage.Navigation.PushAsync(new Main(), false);
System.Diagnostics.Debug.WriteLine("Home");
break;
}
}
public void ChangeActiveValue(string activeNavigationName)
{
foreach(NavigationItem navigationItem in NavigationItemss)
{
if (navigationItem.NavigationItemName == activeNavigationName)
{
navigationItem.Active = true;
}
else { navigationItem.Active = false; }
}
}
}
}

Normally we don't create pages in App.xaml
i think MasterDetailPage is the the fastest way to solve your problem, you just need to configure Master and Detail page,Master page here is your navigation page which you use ListView,and the detail page is your Main and Home pages.
you could refer to the MasterDetails Page
ps: you could also use Shell if your Xamarin.Forms is 4.0 and above

DataBinding works inside the controlTemplate as well. You can find more info about it here.
https://learn.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/templates/control-templates/template-binding

Related

Xamarin WebView GestureRecognizer not working

I have an awkward problem with GestureRecognizers on Xamarin WebView:
Although the documentation any some questions/answers here and in Xamarin Forum say that WebView GestureRecognizers should all work, I can't get it to fire any event.
My XAML code looks like this:
<StackLayout BackgroundColor="LightGray" >
<WebView x:Name="webView" VerticalOptions="FillAndExpand" >
<WebView.GestureRecognizers>
<SwipeGestureRecognizer Direction="Left" Swiped="onSwiped"/>
</WebView.GestureRecognizers>
<WebView.Source>
<HtmlWebViewSource Html="{Binding HTML}" />
</WebView.Source>
</WebView>
</StackLayout>
Alternatives treid so far:
Same GestureRecognizer on the Title of the same page: works
Same GestureRecognizer on a ListView of another page: works
Tried Nuget package Vapolia.XamarinGestures which also didn't work on the webview
Tried to put the GestureRecoginzer on the StackLayout around the WebView: didn't work either.
Tried it on iOS device and simulator. Normally iOS should be the easy part here...
What I actually want to achieve: with a swipe left move forward to another (programatically defined) web page.
I assume those gestures are somehow absorbed by the webview for regular navigation, but I was wondering why some examples would say that all gestures work on the webview.
An alternative could be to add that target webpage to the webview history stack on the "forward" path.. but not sure how to do that.
Anyone has some hints?
You could use Custom Renderer to add the swipe event on specific platform. And handle them in Forms .
in Forms
create a CustomWebView
public class CustomWebView : WebView
{
public event EventHandler SwipeLeft;
public event EventHandler SwipeRight;
public void OnSwipeLeft() =>
SwipeLeft?.Invoke(this, null);
public void OnSwipeRight() =>
SwipeRight?.Invoke(this, null);
}
in Android
using Android.Content;
using Android.Views;
using App11;
using App11.Droid;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
[assembly: ExportRenderer(typeof(CustomWebView), typeof(MyWebViewRenderer))]
namespace App11.Droid
{
public class MyWebViewRenderer : WebViewRenderer
{
public MyWebViewRenderer(Context context) : base(context)
{
}
protected override void OnElementChanged(ElementChangedEventArgs<WebView> e)
{
base.OnElementChanged(e);
Control.SetOnTouchListener(new MyOnTouchListener((CustomWebView)Element));
}
}
public class MyOnTouchListener : Java.Lang.Object, Android.Views.View.IOnTouchListener
{
float oldX;
float newX;
CustomWebView myWebView;
public MyOnTouchListener(CustomWebView webView)
{
myWebView = webView;
}
public bool OnTouch(Android.Views.View v, MotionEvent e)
{
if (e.Action == MotionEventActions.Down)
{
oldX = e.GetX(0);
}
if (e.Action == MotionEventActions.Up)
{
newX = e.GetX();
if (newX - oldX > 0)
{
myWebView.OnSwipeRight();
}
else
{
myWebView.OnSwipeLeft();
}
}
return false;
}
}
}
in iOS
using App11;
using App11.iOS;
using Foundation;
using UIKit;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
using ObjCRuntime;
[assembly: ExportRenderer(typeof(CustomWebView), typeof(MyWebViewRenderer))]
namespace App11.iOS
{
public class MyWebViewRenderer:WkWebViewRenderer
{
protected override void OnElementChanged(VisualElementChangedEventArgs e)
{
base.OnElementChanged(e);
if(e.NewElement!=null)
{
this.BackgroundColor = UIColor.Red;
UISwipeGestureRecognizer leftgestureRecognizer = new UISwipeGestureRecognizer(this,new Selector("SwipeEvent:"));
leftgestureRecognizer.Direction = UISwipeGestureRecognizerDirection.Left;
UISwipeGestureRecognizer rightgestureRecognizer = new UISwipeGestureRecognizer(this, new Selector("SwipeEvent:"));
rightgestureRecognizer.Direction = UISwipeGestureRecognizerDirection.Right;
leftgestureRecognizer.Delegate = new MyWebViewDelegate();
rightgestureRecognizer.Delegate = new MyWebViewDelegate();
this.AddGestureRecognizer(leftgestureRecognizer);
this.AddGestureRecognizer(rightgestureRecognizer);
}
}
[Export("SwipeEvent:")]
void SwipeEvent(UISwipeGestureRecognizer recognizer)
{
var webview = Element as CustomWebView;
if(recognizer.Direction == UISwipeGestureRecognizerDirection.Left)
{
webview.OnSwipeLeft();
}
else if(recognizer.Direction == UISwipeGestureRecognizerDirection.Right)
{
webview.OnSwipeRight();
}
}
}
public class MyWebViewDelegate: UIGestureRecognizerDelegate
{
public override bool ShouldRecognizeSimultaneously(UIGestureRecognizer gestureRecognizer, UIGestureRecognizer otherGestureRecognizer)
{
return false;
}
}
}
Now you just need to use it like
<local:CustomWebView x:Name="browser"
HorizontalOptions="Center"
VerticalOptions="CenterAndExpand"
SwipeLeft="browser_SwipeLeft"
SwipeRight="browser_SwipeRight">
There was an additional trick to make it finally work. All the above (correct) solution was ignored due to my Xamarin MasterDetailPage setup.
This was capturing all horizontal swipes and not putting them through to the HybridWebView.
MasterDetailPage.IsGestureEnabled = false;
finally fixed it and enabled the swipe gestures in my WebView.

Admob ads are not appearing on xamarin forms

I am trying to place a google admob banner ad on a xamarin forms project but I cannot.
I have not tried from many sites. When I run the application on both the phone and the emulator, the colorful template of the ad appears at the bottom, but the ad does not. I also tried it with test id.
MainPage.xaml
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:App6.CustomRenders;assembly=App6"
x:Class="App6.MainPage">
<StackLayout>
<!-- Place new controls here -->
<Label Text="Welcome to Xamarin.Forms!"
HorizontalOptions="Center"
VerticalOptions="CenterAndExpand" />
<local:AdBanner WidthRequest="320" HeightRequest="50"/>
</StackLayout>
</ContentPage>
AdBanner.cs
using System;
using System.Collections.Generic;
using System.Text;
using Xamarin.Forms;
namespace App6.CustomRenders
{
public class AdBanner : View
{
public enum Sizes { Standardbanner, LargeBanner, MediumRectangle, FullBanner, Leaderboard, SmartBannerPortrait }
public Sizes Size { get; set; }
public AdBanner()
{
this.BackgroundColor = Color.Accent;
}
}
}
enter code here
AdBanner_Droid.cs (in Android)
using System;
using App6;
using Android.Gms.Ads;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
using App6.CustomRenders;
using App6.Droid.CustomRenders;
using Android.Content;
[assembly: ExportRenderer(typeof(AdBanner), typeof(AdBanner_Droid))]
namespace App6.Droid.CustomRenders
{
public class AdBanner_Droid : ViewRenderer
{
Context context;
public AdBanner_Droid(Context _context) : base(_context)
{
context = _context;
}
protected override void OnElementChanged(ElementChangedEventArgs<View> e)
{
base.OnElementChanged(e);
if (e.OldElement == null)
{
var adView = new AdView(Context);
switch ((Element as AdBanner).Size)
{
case AdBanner.Sizes.Standardbanner:
adView.AdSize = AdSize.Banner;
break;
case AdBanner.Sizes.LargeBanner:
adView.AdSize = AdSize.LargeBanner;
break;
case AdBanner.Sizes.MediumRectangle:
adView.AdSize = AdSize.MediumRectangle;
break;
case AdBanner.Sizes.FullBanner:
adView.AdSize = AdSize.FullBanner;
break;
case AdBanner.Sizes.Leaderboard:
adView.AdSize = AdSize.Leaderboard;
break;
case AdBanner.Sizes.SmartBannerPortrait:
adView.AdSize = AdSize.SmartBanner;
break;
default:
adView.AdSize = AdSize.Banner;
break;
}
// TODO: change this id to your admob id
adView.AdUnitId = "ca-app-p";
var requestbuilder = new AdRequest.Builder();
adView.LoadAd(requestbuilder.Build());
SetNativeControl(adView);
}
}
}
}
looks like this

Xamarin iOS webview underlap behind navigation bar

I am using xamarin custom webview to load my page in app. But facing issue that title of webpage hides behind navigation bar . Or sometimes bottom of page not shown. I have tried adding scrollbar to my layout but still facing issue. Same works perfectly on android. Is it due to custom webview? I just want my webview to start below navigation bar and load completely according to device size.
my custom webview code :
public class CustomWebView : WebView
{
public static readonly BindableProperty UriProperty = BindableProperty.Create(
propertyName: "Uri",
returnType: typeof(string),
declaringType: typeof(CustomWebView),
defaultValue: default(string));
public string Uri
{
get { return (string)GetValue(UriProperty); }
set { SetValue(UriProperty, value); }
}
}
Xaml Page :
<StackLayout Orientation="Vertical" HorizontalOptions="StartAndExpand" VerticalOptions="StartAndExpand">
<StackLayout>
<Label x:Name="type" Text="Loading..." FontSize="Medium"/>
</StackLayout>
<StackLayout VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand">
<ScrollView Orientation="Vertical" FlowDirection="MatchParent" HorizontalOptions="StartAndExpand" VerticalOptions="StartAndExpand" Visual="Material" VerticalScrollBarVisibility="Always">
<OnPlatform x:TypeArguments="View">
<On Platform="Android">
<WebView x:Name="dashboard_android" HeightRequest="1000" WidthRequest="1000" />
</On>
<On Platform="iOS">
<local:CustomWebView x:Name="dashboard_ios" VerticalOptions="StartAndExpand" HorizontalOptions="FillAndExpand" WidthRequest="1000" HeightRequest="1000"/>
</On>
</OnPlatform>
</ScrollView>
</StackLayout>
</StackLayout>
code behind :
dashboard_android.Source = url;
dashboard_ios.Uri = url;
Following are solutions i have tried but no success
Solution 1 :
I have tried adding two properties, but no use
this.EdgesForExtendedLayout = UIRectEdge.None;
this.ExtendedLayoutIncludesOpaqueBars = false;
Solution 2 :
Tried enabling this unsafe area property , still no success
ios:Page.UseSafeArea="true"
Solution 3 :
Tried setting webview height on content size dynamically , but no success
public override async void DidFinishNavigation(WKWebView webView, WKNavigation navigation)
{
// base.DidFinishNavigation(webView, navigation);
var wv = _webViewRenderer.Element as CustomWebView;
if (wv != null)
{
await System.Threading.Tasks.Task.Delay(100); // wait here till content is rendered
wv.HeightRequest = (double)webView.Frame.Size.Height; // ScrollView.ContentSize.Height;
}
}
Updated Xaml Code :
<StackLayout VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand">
<local:CustomWebView x:Name="dashboard" HeightRequest="1000" WidthRequest="1000" />
</StackLayout>
Updated Code behind :
public partial class DashboardView : ContentPage
{
string url;
public DashboardView()
{
InitializeComponent();
url= ""; //adding url to load here
dashboard.Uri = url;
}
}
Custom WebView Renderer
[assembly: ExportRenderer(typeof(CustomWebView), typeof(MyCustomWebViewRenderer))]
namespace Report.iOS
{
public class MyCustomWebViewRenderer : ViewRenderer<CustomWebView, WKWebView>
{
WKWebView webView;
protected override void OnElementChanged(ElementChangedEventArgs<CustomWebView> e)
{
base.OnElementChanged(e);
if (Control == null)
{
webView = new WKWebView(Frame, new WKWebViewConfiguration());
webView.NavigationDelegate = new WebViewDelegate();
SetNativeControl(webView);
}
if (e.NewElement != null)
{
Control.LoadRequest(new NSUrlRequest(new NSUrl(Element.Uri)));
}
}
}
public class WebViewDelegate : WKNavigationDelegate, INSUrlConnectionDataDelegate
{
string uname = null;
string pass = null;
public override async void DidReceiveAuthenticationChallenge(WKWebView webView, NSUrlAuthenticationChallenge challenge, Action<NSUrlSessionAuthChallengeDisposition, NSUrlCredential> completionHandler)
{
try
{
uname = Xamarin.Forms.Application.Current.Properties.ContainsKey("Username") ? Convert.ToString(Xamarin.Forms.Application.Current.Properties["Username"]) : null;
pass = await SecureStorage.GetAsync("Password");
}
catch (Exception ex)
{
}
completionHandler(NSUrlSessionAuthChallengeDisposition.UseCredential, new NSUrlCredential(uname, pass, NSUrlCredentialPersistence.ForSession));
return;
}
}
}
Screenshot of webview screen :
Here i am loading this webpage(https://learn.microsoft.com/en-us/xamarin/essentials/device-display?tabs=android). As you can see half of footer is hidden and i am not able to scroll it.
Screenshot of app
The reason for it quite simple actually you have added the WebView inside a scrollView which is, in turn, causing the issue webview has its own scroll so all you have to do is something like:
<StackLayout VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand">
<local:CustomWebView x:Name="dashboard" />
</StackLayout>
Also, you do not need the on the platform you can directly use the below and the custom renderer you have created.
The Height/Width request & layout options are not needed Webview by default will capture the whole viewport, You could actually even remove the StackLayouts, But that's on you.
Also, you might wanna read more about the webview
Good luck
Feel free to get back if you have queries
You can use latest WkWebViewRenderer:
public class MyCustomWebViewRenderer : WkWebViewRenderer
{
protected override void OnElementChanged(VisualElementChangedEventArgs e)
{
base.OnElementChanged(e);
//this.LoadUrl("https://learn.microsoft.com/en-us/xamarin/essentials/device-display?tabs=android");
this.NavigationDelegate = new WebViewDelegate();
}
}
In your code behind, you can directly set the source or set your binding:
dashboard.Source = "https://learn.microsoft.com/en-us/xamarin/essentials/device-display?tabs=android";
Also, start from xamarin.forms 4.5+, xamarin use WKWebview as the default control in iOS and that means you no longer need a custom renderer if you use xamarin.forms 4.5+. Refer:
UIWebView Deprecation and App Store Rejection (ITMS-90809)
I was facing that issue just beacuse i was using custom renderer.
My solution code is as follows :
Xaml Code :
<ContentPage.Content>
<StackLayout VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand">
<WebView x:Name="dashboard" HeightRequest="1000" WidthRequest="1000"/>
</StackLayout>
</ContentPage.Content>
Code Behind :
public partial class DashboardView : ContentPage
{
public DashboardView()
{
InitializeComponent();
dashboard.Source = "url";
}
}
Authentication Renderer iOS :
[assembly: ExportRenderer(typeof(WebView), typeof(Report.iOS.WebViewRenderer))]
namespace Report.iOS
{
class WebViewRenderer : WkWebViewRenderer
{
protected override void OnElementChanged(VisualElementChangedEventArgs e)
{
base.OnElementChanged(e);
this.NavigationDelegate = new WebViewDelegate();
}
}
public class WebViewDelegate : WKNavigationDelegate, INSUrlConnectionDataDelegate
{
string uname = null;
string pass = null;
public override async void DidReceiveAuthenticationChallenge(WKWebView webView, NSUrlAuthenticationChallenge challenge, Action<NSUrlSessionAuthChallengeDisposition, NSUrlCredential> completionHandler)
{
try
{
uname = Xamarin.Forms.Application.Current.Properties.ContainsKey("Username") ? Convert.ToString(Xamarin.Forms.Application.Current.Properties["Username"]) : null;
pass = await SecureStorage.GetAsync("Password");
}
catch (Exception ex)
{
}
completionHandler(NSUrlSessionAuthChallengeDisposition.UseCredential, new NSUrlCredential(uname, pass, NSUrlCredentialPersistence.ForSession));
return;
}
}
}
Authentication Renderer Android :
[assembly: ExportRenderer(typeof(WebView), typeof(AuthWebViewRenderer))]
namespace Report.Droid
{
public class AuthWebViewRenderer : Xamarin.Forms.Platform.Android.WebViewRenderer
{
AuthWebViewClient _authWebClient = null;
public AuthWebViewRenderer(Context context) : base(context)
{
}
protected override void OnElementChanged(Xamarin.Forms.Platform.Android.ElementChangedEventArgs<WebView> e)
{
base.OnElementChanged(e);
if (_authWebClient == null)
{
_authWebClient = new AuthWebViewClient();
}
Control.SetWebViewClient(_authWebClient);
}
}
public class AuthWebViewClient : WebViewClient
{
public AuthWebViewClient()
{
}
public override async void OnReceivedHttpAuthRequest(global::Android.Webkit.WebView view, HttpAuthHandler handler, string host, string realm)
{
string uname = null;
string pass = null;
try
{
uname = Application.Current.Properties.ContainsKey("Username") ? Convert.ToString(Application.Current.Properties["Username"]) : null;
pass = await SecureStorage.GetAsync("Password");
}
catch (Exception ex)
{
Log.Error("Apprise :", "Error Occurred while getting login credentials " + ex);
}
handler.Proceed(uname, pass);
}
}
}

How to create a Xamarin Tooltip in code-behind

I am testing using the following example. https://github.com/CrossGeeks/TooltipSample
The sample works fine, it even works with Labels (sample uses buttons, images and boxviews). The issue is in my main App I need to create the tooltips in code behind.
To test how to do it, in the very same solution (from that above example) I created a TestPage and made it my MainPage in App.xaml.cs. The XAML looks like this:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="ToolTipSample.TestPage">
<ContentPage.Content>
<StackLayout
x:Name="mainLayout"
BackgroundColor="Yellow">
<StackLayout.GestureRecognizers>
<TapGestureRecognizer Tapped="Handle_Tapped"/>
</StackLayout.GestureRecognizers>
</StackLayout>
</ContentPage.Content>
The code-behind looks like this:
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
using ToolTipSample.Effects;
namespace ToolTipSample
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class TestPage : ContentPage
{
public TestPage()
{
InitializeComponent();
var actionLabel = new Label
{
Text = "Show Tooltip",
WidthRequest = 150,
VerticalOptions = LayoutOptions.StartAndExpand,
HorizontalOptions = LayoutOptions.Center,
BackgroundColor = Color.Wheat
};
// Add tooltip to action label
TooltipEffect.SetPosition(actionLabel, TooltipPosition.Bottom);
TooltipEffect.SetBackgroundColor(actionLabel, Color.Silver);
TooltipEffect.SetTextColor(actionLabel, Color.Teal);
TooltipEffect.SetText(actionLabel, "This is the tooltip");
TooltipEffect.SetHasTooltip(actionLabel, true);
actionLabel.Effects.Add(Effect.Resolve($"CrossGeeks.{nameof(TooltipEffect)}"));
mainLayout.Children.Add(actionLabel);
}
void Handle_Tapped(object sender, System.EventArgs e)
{
foreach (var c in mainLayout.Children)
{
if (TooltipEffect.GetHasTooltip(c))
{
TooltipEffect.SetHasTooltip(c, false);
TooltipEffect.SetHasTooltip(c, true);
}
}
}
}
}
All other code unchanged.
When I tap the label, the tooltip appears as expected. But when I tap the background it does not disappear (like those created in XAML in the sample).
One other thing. If I tap twice it disappears.
Can anyone see what I am missing?
Thanks.
According to your description and code, you can delete the following line code to achieve your requirement.
actionLabel.Effects.Add(Effect.Resolve($"CrossGeeks.{nameof(TooltipEffect)}"));
You don't need to add effect for control when page load, because this effect will be added when you click this control by these code:
static void OnHasTooltipChanged(BindableObject bindable, object oldValue, object newValue)
{
var view = bindable as View;
if (view == null)
{
return;
}
bool hasTooltip = (bool)newValue;
if (hasTooltip)
{
view.Effects.Add(new ControlTooltipEffect());
}
else
{
var toRemove = view.Effects.FirstOrDefault(e => e is ControlTooltipEffect);
if (toRemove != null)
{
view.Effects.Remove(toRemove);
}
}
}

How can I set up different footers for TableSections when using a Custom TableView Renderer

I am using a renderer to allow me to set a custom footer in my TableView. The renderer works but I would like to have the capability to set up different footers for the different table sections. For example one footer for table section 0 and another for table section 1, all the way up to table section 5.
Here's the XAML that I am using:
<!-- <local:ExtFooterTableView x:Name="tableView" Intent="Settings" HasUnevenRows="True">-->
<TableView x:Name="tableView" Intent="Settings" HasUnevenRows="True">
<TableSection Title="Cards1">
<ViewCell Height="50">
<Label Text="Hello1" />
</ViewCell>
<ViewCell Height="50">
<Label Text="Hello2" />
</ViewCell>
</TableSection>
<TableSection Title="Cards2">
<TextCell Height="50" Text="Hello"></TextCell>
</TableSection>
</TableSection>
<!-- </local:ExtFooterTableView>-->
</TableView>
and here is the C# class and renderer:
public class ExtFooterTableView : TableView
{
public ExtFooterTableView()
{
}
}
and:
using System;
using Japanese;
using UIKit;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
[assembly: ExportRenderer(typeof(ExtFooterTableView), typeof(Japanese.iOS.ExtFooterTableViewRenderer))]
namespace Japanese.iOS
{
public class ExtFooterTableViewRenderer : TableViewRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<TableView> e)
{
base.OnElementChanged(e);
if (Control == null)
return;
var tableView = Control as UITableView;
var formsTableView = Element as TableView;
tableView.WeakDelegate = new CustomFooterTableViewModelRenderer(formsTableView);
}
private class CustomFooterTableViewModelRenderer : TableViewModelRenderer
{
public CustomFooterTableViewModelRenderer(TableView model) : base(model)
{
}
public override UIView GetViewForFooter(UITableView tableView, nint section)
{
Debug.WriteLine("xx");
if (section == 0)
{
return new UILabel()
{
// Text = TitleForFooter(tableView, section), // or use some other text here
Text = "abc",
TextAlignment = UITextAlignment.Left
// TextAlignment = NSTextAlignment.NSTextAlignmentJustified
};
}
else
{
return new UILabel()
{
// Text = TitleForFooter(tableView, section), // or use some other text here
Text = "def",
TextAlignment = UITextAlignment.Left
// TextAlignment = NSTextAlignment.NSTextAlignmentJustified
};
}
}
}
}
}
The code works but I would like to find out how I can set up a different footer text for different sections in the XAML. Something like this:
From what I see it looks like the code is partly there TitleForFooter(tableView, section) but I am not sure how to use it and how I could set it up. Note that I am not really looking for a view model solution. I would be happy to be simply able to specify the section footer text as part of the TableView XAML.
I'd appreciate if anyone could give me some advice on this.
First of all, in order to be able to specify the section footer text in XAML - simplest option would be to create a bindable property in TableSection. But as TableSection is sealed, we can't derive it to define our custom bindable properties.
So, the next option is to create a attached bindable property.
public class Ex
{
public static readonly BindableProperty FooterTextProperty =
BindableProperty.CreateAttached("FooterText", typeof(string), typeof(Ex), defaultValue: default(string));
public static string GetFooterText(BindableObject view)
{
return (string)view.GetValue(FooterTextProperty);
}
public static void SetFooterText(BindableObject view, string value)
{
view.SetValue(FooterTextProperty, value);
}
}
Next step would be to update renderer to retrieve this value for every section:
private class CustomFooterTableViewModelRenderer : TableViewModelRenderer
{
public CustomFooterTableViewModelRenderer(TableView model) : base(model)
{
}
public override UIView GetViewForFooter(UITableView tableView, nint section)
{
return new UILabel()
{
Text = TitleForFooter(tableView, section), // or use some other text here
Font = UIFont.SystemFontOfSize(14),
ShadowColor = Color.White.ToUIColor(),
ShadowOffset = new CoreGraphics.CGSize(0, 1),
TextColor = Color.DarkGray.ToUIColor(),
BackgroundColor = Color.Transparent.ToUIColor(),
Opaque = false,
TextAlignment = UITextAlignment.Center
};
}
//Retrieves the footer text for corresponding section through the attached property
public override string TitleForFooter(UITableView tableView, nint section)
{
var tblSection = View.Root[(int)section];
return Ex.GetFooterText(tblSection);
}
}
Sample Usage
<local:ExtFooterTableView x:Name="tableView" Intent="Settings" HasUnevenRows="True">
<TableSection Title="Cards1" local:Ex.FooterText="Sample description">
<ViewCell Height="50">
<Label Margin="20,0,20,0" Text="Hello1" />
</ViewCell>
<ViewCell Height="50">
<Label Margin="20,0,20,0" Text="Hello2" />
</ViewCell>
</TableSection>
<TableSection Title="Cards2" local:Ex.FooterText="Disclaimer note">
<TextCell Height="50" Text="Hello"></TextCell>
</TableSection>
</local:ExtFooterTableView>
It is very simple. you need to add the bindable property for pass value from XAML to CustomRenderer in CustomControl like this:
Customer TableView
public class ExtFooterTableView : TableView
{
public ExtFooterTableView()
{
}
}
Xaml control code
<local:ExtFooterTableView x:Name="tableView" Intent="Settings" HasUnevenRows="True">
Renderer class
using System;
using UIKit;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
using yournamespace;
using System.ComponentModel;
[assembly: ExportRenderer(typeof(ExtFooterTableView), typeof(FooterTableViewRenderer))]
namespace yournamespace
{
public class FooterTableViewRenderer : TableViewRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<TableView> e)
{
base.OnElementChanged(e);
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
var view = (ExtFooterTableView)Element;
if (e.PropertyName == ExtFooterTableView.IntentProperty.PropertyName)
{
string intent = view.Intent;
// Do your stuff for intent property
}
if (e.PropertyName == ExtFooterTableView.HasUnevenRowsProperty.PropertyName)
{
bool hasUnevenRows = view.HasUnevenRows;
// Do yout stuff for HasUnevenRow
}
}
}
}

Resources