I'm new to xamarin forms and learning it through video tutorials done using iOS development Mac laptop.I'm using windows & VS2019 for Xamarin forms. When using ListView, TextCell and ContextActions, my mentuItem text="Delete" or text="Call" aren't showing up when i select a name in the android emulator. But the same action of selection a name on the iOS emulator makes two buttons("Delete" & "Call") slides out. I attached my image as well as the tutotial image. Below are the codes from the tutorial.
My xaml file is `
<? 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 = "WarMemorial.Excercises.ListViewContextActions" >
< ListView x: Name = "listView" >
< ListView.ItemTemplate >
< DataTemplate >
< TextCell Text = "{Binding Name}"
Detail = "{Binding Status}" >
< TextCell.ContextActions >
< MenuItem Text = "Call" IconImageSource = "phone.png"
Clicked = "Call_Clicked"
CommandParameter = "{Binding .}" ></ MenuItem >
< MenuItem Text = "Delete"
Clicked = "Delete_Clicked" IconImageSource = "trash-can.png"
CommandParameter = "{Binding .}" ></ MenuItem >
</ TextCell.ContextActions >
</ TextCell >
</ DataTemplate >
</ ListView.ItemTemplate >
</ ListView >
</ ContentPage >
XAML.cs
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
using WarMemorial.Models;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace WarMemorial.Excercises
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class ListViewContextActions : ContentPage
{
private ObservableCollection<Contacts> _contacts;
public ListViewContextActions()
{
InitializeComponent();
_contacts = new ObservableCollection<Contacts> {
new Contacts { Name = "Susana" },
new Contacts { Name = "Sara", Status = "let's tallk" }
};
listView.ItemsSource = _contacts;
}
private void Call_Clicked(object sender, EventArgs e)
{
var contact = (sender as MenuItem).CommandParameter as Contacts;
DisplayAlert("Call", contact.Name, "OK");
}
private void Delete_Clicked(object sender, EventArgs e)
{
var contact = (sender as MenuItem).CommandParameter as Contacts;
_contacts.Remove(contact);
}
}
}
as #Jason said you can't get the same behavior on iOS and Android for ContextActions, on the other hand you might want to use the experimental SwipeView which will give you the same swipe behavior on both platforms.
Related
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.
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
I recently changed to Xamarin Forms and notice that the title isn't centered at the top of the page for Android devices.
Is there a way that I can do this?
Here's an example of what I mean with the title:
You can use the TitleView:
<?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:local="clr-namespace:TitleViewSample"
x:Class="TitleViewSample.MainPage">
<NavigationPage.TitleView>
<Label Text="Hello World" HorizontalTextAlignement="Center"/>
</NavigationPage.TitleView>
<ContentPage.Content>
<StackLayout>
<!-- Place new controls here -->
<Label Text="Welcome to Xamarin.Forms!"
HorizontalOptions="Center"
VerticalOptions="CenterAndExpand" />
</StackLayout>
</ContentPage.Content>
</ContentPage>
https://www.andrewhoefling.com/Blog/Post/xamarin-forms-title-view-a-powerful-navigation-view
You will have to implement ShellRenderer in this case as you have Xamarin.Forms Shell Project.
using System;
using System.Collections.Specialized;
using System.ComponentModel;
using Android.App;
using Android.Content;
using Android.Content.Res;
using Android.Support.V4.Widget;
using Android.Support.V7.Widget;
using Android.Util;
using Android.Widget;
using Japanese.Droid.CustomRenderers;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
using Toolbar = Android.Support.V7.Widget.Toolbar;
[assembly: ExportRenderer(typeof(Xamarin.Forms.Shell), typeof(MyShellRenderer))]
namespace MyProject.Droid.CustomRenderers
{
public class MyShellRenderer : ShellRenderer
{
public MyShellRenderer(Context context) : base(context)
{
}
protected override IShellToolbarAppearanceTracker CreateToolbarAppearanceTracker()
{
return new MyShellToolbarAppearanceTracker(this);
}
protected override IShellToolbarTracker CreateTrackerForToolbar(Toolbar toolbar)
{
return new MyShellToolbarTracker(this, toolbar, ((IShellContext)this).CurrentDrawerLayout);
}
}
public class MyShellToolbarAppearanceTracker : ShellToolbarAppearanceTracker
{
public MyShellToolbarAppearanceTracker(IShellContext context) : base(context)
{
}
public override void SetAppearance(Android.Support.V7.Widget.Toolbar toolbar, IShellToolbarTracker toolbarTracker, ShellAppearance appearance)
{
base.SetAppearance(toolbar, toolbarTracker, appearance);
//Change the following code to change the icon of the Header back button.
toolbar?.SetNavigationIcon(Resource.Drawable.back);
}
}
public class MyShellToolbarTracker : ShellToolbarTracker
{
public MyShellToolbarTracker(IShellContext shellContext, Toolbar toolbar, DrawerLayout drawerLayout) : base(shellContext, toolbar, drawerLayout)
{
}
protected override void UpdateTitleView(Context context, Toolbar toolbar, View titleView)
{
base.UpdateTitleView(context, toolbar, titleView);
for (int index = 0; index < toolbar.ChildCount; index++)
{
if (toolbar.GetChildAt(index) is TextView)
{
var title = toolbar.GetChildAt(index) as TextView;
//Change the following code to change the font size of the Header title.
title.SetTextSize(ComplexUnitType.Sp, 20);
toolbar.SetTitleMargin(MainActivity.displayMetrics.WidthPixels / 4 - Convert.ToInt32(title.TextSize) - title.Text.Length / 2, 0, 0, 0);
}
}
}
}
}
Here is the code for MainActivity.cs
public class MainActivity : FormsAppCompatActivity
{
public static DisplayMetrics displayMetrics;
protected override void OnCreate(Bundle savedInstanceState)
{
Window.AddFlags(WindowManagerFlags.Fullscreen);
base.OnCreate(savedInstanceState);
Xamarin.Essentials.Platform.Init(this, savedInstanceState);
global::Xamarin.Forms.Forms.Init(this, savedInstanceState);
displayMetrics = new DisplayMetrics();
WindowManager.DefaultDisplay.GetRealMetrics(displayMetrics);
LoadApplication(new App());
if (Window != null) Window.SetStatusBarColor(Android.Graphics.Color.Transparent);
if (isPhone(this)) RequestedOrientation = ScreenOrientation.Portrait;
}
}
As the title textview having wrapped width within toolbar not updating alignment on TextAlignment with center, you can update the layout params of the toolbar to matchparent and textview gravity as follows.
If the hamburger image added with custom image then need check that resoulution if that is too big just reduce it lower one
[assembly: ExportRenderer(typeof(MainPage), typeof(MyRenderer))]//MainPage - navigation page
namespace MyProject.Droid
{
public class MyRenderer: MasterDetailPageRenderer
{
public MyRenderer(Context context) : base(context)
{
}
protected override void OnLayout(bool changed, int l, int t, int r, int b)
{
base.OnLayout(changed, l, t, r, b);
var toolbar = FindViewById<AndroidX.AppCompat.Widget.Toolbar>(Resource.Id.toolbar);
for (var i = 0; i < toolbar.ChildCount; i++)
{
var title = toolbar.GetChildAt(i) as TextView;
if (title != null && !string.IsNullOrEmpty(title.Text))
{
title.TextAlignment = Android.Views.TextAlignment.Center;
title.Gravity = GravityFlags.CenterHorizontal;
var layoutParams = (AndroidX.AppCompat.Widget.Toolbar.LayoutParams)title.LayoutParameters;
layoutParams.Width = ViewGroup.LayoutParams.MatchParent;
toolbar.RequestLayout();
}
}
}
}
}
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
I am working with search bar in xamarin forms. I am not able to remove search text in search bar by clicking cross button. I am using custom renderer for remove cancel text in search bar. When I am using that renderer I am not able delete the text, If I remove that renderer it work's fine. what the wrong I have done in renderer file. Here is the sample code in renderer for hide the cancel button beside the search bar.
Renderer Sample code:
protected override void OnElementPropertyChanged(object sender, PropertyChnagedEventArgs e)
{
Control.ShowCancelButton = false;
}
When I try to remove text in search bar by clicking cross image in search bar it's not working in xamarin ios but it's working fine in ios.
Sample code :
<StackLayout Grid.Column = "0" Orientation = "Horizontal">
<Image Source = "backarrow.png" HorizontalOptions = "StartAndExpand" VerticalOptions = "CenterAndExpand" />
<controls:CustomSearchbar x:Name = "CustomSearchbar" BackgroundColor ="Transparent" Text ="{Binding SearchTag}" SearchCommand ="{Binding RestaurantSearchCommand}" GHorizontalOptions = "StartAndExpand" VerticalOptions = "CenterAndExpand" >
Here is the code I am using for search bar. Here 'customsearchbar' class is inherited from searchrenderer.
It works fine for me.
Here's my code for example:
MySearchBar.cs in PCL:
using Xamarin.Forms;
namespace Kevin_XF
{
public class MySearchBar : SearchBar
{
}
}
MySearchBarRenderer.cs in iOS platform:
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
using Kevin_XF.iOS;
using Kevin_XF;
using System.ComponentModel;
[assembly: ExportRenderer(typeof(MySearchBar),typeof(MySearchBarRenderer))]
namespace Kevin_XF.iOS
{
public class MySearchBarRenderer: SearchBarRenderer
{
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (Control != null)
{
Control.ShowsCancelButton = false;
}
}
}
}
Xaml code in MainPage.xaml:
<StackLayout>
<local:MySearchBar x:Name = "CustomSearchbar" BackgroundColor ="Transparent" HorizontalOptions = "StartAndExpand" VerticalOptions = "CenterAndExpand" />
</StackLayout>
It works like this: