System.NotSupportedException: Unable to activate instance of type MyProject.CancelListener from native handle - xamarin.forms

In my Xamarin Forms app, I am using native Android code for the platform. I want to show a AlertDialog.Builder and catch the event when the user taps outside of the dialog box with SetOnCancelListener. This is my code:
AlertDialog.Builder adb = new AlertDialog.Builder(this);
adb.SetTitle("title");
adb.SetItems(myItems.Select(x => x.Name).ToArray(), (s, e) =>
{
// not important code
});
var cancelled = new CancelListener();
cancelled.Cancelled += (s, e) =>
{
// not important code
};
adb.SetCancelable(true);
adb.SetOnCancelListener(cancelled);
Dialog d = adb.Create();
d.Show();
My CancelListener class:
public class CancelListener : Java.Lang.Object, IDialogInterfaceOnCancelListener
{
public event EventHandler Cancelled;
public IntPtr Handle => IntPtr.Zero;
public CancelListener() : base()
{
}
public void Dispose()
{
Cancelled = null;
}
public void OnCancel(IDialogInterface dialog)
{
Cancelled?.Invoke(null, EventArgs.Empty);
}
}
When I tap outside of the dialog box I get the message:
System.NotSupportedException: Unable to activate instance of type
MyProject.CancelListener from native handle

Related

Notify Activity of changes in viewModel

I try to exit 'lock task mode' in Xamarin Android app. Here is what I am trying to achieve:
User taps on label (view in Xamarin.Forms) -> it cause change in ViewModel's boolean property to true
MainActivity (Xamarin.Android) observe that property has changed to true -> it makes application exit 'lock task mode'
My viewModel is placed in Xamarin.Forms 'App.xaml' class so it is accessible in Forms and Android part.
How Can I notify my Activity that property has changed so it can exit locked mode? I know this is propably very poor workaround, I would love to hear any advices and tips to make it more professional.
Thank you in advance!
EDIT
So the point is that I have got ViewModel with boolean property exitLockMode which indicates if app should be in lock mode or not:
public class AdminViewModel : BaseViewModel
{
//Number of taps to touch at main banner in 'MainPage' to open Admin Window
private int _tapsRequiredToAdmin = 5;
//Number of tolerance in miliseconds between next taps
private int _toleranceInMs = 1000;
private bool _exitLockMode = false;
public int ToleranceInMs { get => _toleranceInMs; }
public int TapsRequiredToAdmin { get => _tapsRequiredToAdmin; }
public bool ExitLockMode
{
get => _exitLockMode;
set => _exitLockMode=value;
}
}
AdminViewModel is created in 'App.xaml' class:
public partial class App : Application
{
private static AdminViewModel _adminViewModel;
public App()
{
InitializeComponent();
MainPage = new NavigationPage(new MainPage());
}
public static AdminViewModel AdminViewModel
{
get
{
if(_adminViewModel == null )
_adminViewModel = new AdminViewModel();
return _adminViewModel;
}
}
protected override void OnStart() { }
protected override void OnSleep() { }
protected override void OnResume() { }
}
In my main view (Xamarin.Forms) I have got label where admin want to tap few times in order to exit lock mode:
private DateTime? LastTap = null;
private byte NumberOfTaps = 0;
AdminViewModel adminViewModel = App.AdminViewModel;
**********************************************
//This is method binded to Label in <TapGestureRecognizer Tapped="OnLabelTapped">
private async void OnLabelTapped(object sender, EventArgs e)
{
if (LastTap == null || (DateTime.Now - LastTap.Value).TotalMilliseconds < adminViewModel.ToleranceInMs)
{
if (NumberOfTaps == (adminViewModel.TapsRequiredToAdmin - 1))
{
NumberOfTaps = 0;
LastTap = null;
adminViewModel.ExitLockMode = true;
return;
}
else
{
NumberOfTaps++;
LastTap = DateTime.Now;
}
}
else
{
NumberOfTaps = 1;
LastTap = DateTime.Now;
}
}
Now I want to achieve that when I turn 'ExitLockMode' bool to true, it notify my 'MainActivity' (Xamarin.Android) to fire 'StopLockTask()' method. I know that in native Android it could be handled by observing bool property, but I don't know how to do it here.
I am newbie so it could be very messy, every help appreciated.
As Jason said, you can use messagecenter.The Xamarin.Forms MessagingCenter class implements the publish-subscribe pattern, allowing message-based communication between components that are inconvenient to link by object and type references.
This mechanism allows publishers and subscribers to communicate without having a reference to each other, helping to reduce dependencies between them.
You can follow this document and the sample in it https://learn.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/messaging-center

C#, Xamarin Forms: No Custom TextChangedEvent Raised on initialization

I'm creating an Xamarin.Forms MVVM App (only using Android) which needs certain buttons to be outlined red, whenever their text property holds a specific value. (Purpose: alert the user to press the button and select a value, which will change the Button Text Property and therefore remove the red outline)
To achieve this I've create the following documents:
A custom button CButton that extents the default Button:
public class CButton : Button
{
// this Hides the Default .Text-Property
public string Text
{
get => base.Text;
set
{
base.Text = value;
TextChangedEvent(this, new EventArgs());
}
}
// The Raised Event
protected virtual void TextChangedEvent(object sender, EventArgs e)
{
EventHandler<EventArgs> handler = TextChanged;
handler(sender, e);
}
public event EventHandler<EventArgs> TextChanged;
}
A custom behavior makes use of the raised TextChangedEvent
public class ButtonValBehavior : Behavior<CButton>
{
protected override void OnAttachedTo(CButton bindable)
{
bindable.TextChanged += HandleTextChanged;
base.OnAttachedTo(bindable);
}
void HandleTextChanged(object sender, EventArgs e)
{
string forbidden = "hh:mm|dd.mm.yyyy";
if (forbidden.Contains((sender as CButton).Text.ToLower()))
{
//Do when Button Text = "hh:mm" || "dd.mm.yyyy"
(sender as CButton).BorderColor = Color.Gray;
}
else
{
//Do whenever Button.Text is any other value
(sender as CButton).BorderColor = Color.FromHex("#d10f32");
}
}
protected override void OnDetachingFrom(CButton bindable)
{
bindable.TextChanged -= HandleTextChanged;
base.OnDetachingFrom(bindable);
}
}
The relevant parts of the ViewModel look the following:
public class VM_DIVI : VM_Base
{
public VM_DIVI(O_BasisProtokoll base)
{
Base = base;
}
private O_BasisProtokoll _base = null;
public O_BasisProtokoll Base
{
get => _base;
set
{
_base = value;
OnPropertyChanged();
}
}
Command _datePopCommand;
public Command DatePopCommand
{
get
{
return _datePopCommand ?? (_datePopCommand = new Command(param => ExecuteDatePopCommand(param)));
}
}
void ExecuteDatePopCommand(object param)
{
//launch popup
var p = new PP_DatePicker(param);
PopupNavigation.Instance.PushAsync(p);
}
}
The .xmal looks the following (b is the xmlns of the Namespace):
<b:CButton x:Name="BTN_ED_Datum"
Text="{Binding Base.ED_datum, Mode=TwoWay}"
Grid.Column="1"
Command="{Binding DatePopCommand}"
CommandParameter="{x:Reference BTN_ED_Datum}">
<b:CButton.Behaviors>
<b:ButtonValBehavior/>
</b:CButton.Behaviors>
</b:CButton>
This solution works fine whenever the input is caused by user interaction. However, when a Value is assigned during the initialization of the Page no red outline is created, in fact the TextChangedEvent isn't raised. By using breakpoints I noticed that during initialization the Text Property of CButton is never set, eventhough it actually will be in the view.
Despite fiddling around with my solution I cannot make this work on initialization. I tried to work around this issue by outlining every button by default in their constructor, however this will outline every button red, even when their text value doesn't require them to be.
How can I achieve my initial goal?
Many thanks in advance!
It's been a while but if I recall correctly what I ended up doing was:
Changing the new Text-Property of my custom Button to CText and
Making sure that I have Mode=TwoWay activated for any Element, that doesn't have it enabled by default. (Look up Binding modes on msdn for more)
making CText a bindable property of CButton
My custom button now looks the following:
using System;
using System.Collections.Generic;
using System.Text;
using Xamarin.Forms;
namespace EORG_Anton.Model
{
public class CButton : Button
{
public static readonly BindableProperty CTextProperty =
BindableProperty.Create(nameof(CText),
typeof(string),
typeof(CButton),
default(string),
BindingMode.TwoWay,
propertyChanged: OnTextChanged);
private static void OnTextChanged(BindableObject bindable, object oldValue, object newValue)
{
var control = (CButton)bindable;
var value = (string)newValue;
control.CText = value;
}
public string CText
{
get => base.Text;
set
{
base.Text = value;
TextChangedEvent(this, new EventArgs());
}
}
protected virtual void TextChangedEvent(object sender, EventArgs e)
{
EventHandler<EventArgs> handler = TextChanged;
handler(sender, e);
}
public event EventHandler<EventArgs> TextChanged;
}
}

DisplayAlert from ViewModel not displaying

I need to display DisplayAlert from the View Model, however its simply doesn't display. Is there some other way how to display alert from the VM? The permission is true so that works.
private async Task TakePicture()
{
await Permission();
var imageSource = Application.Current.MainPage.DisplayActionSheet(AppResources.AlertNewPhoto, AppResources.AlertNewPhoto, AppResources.AlertGallery);
if (imageSource.Result == AppResources.AlertNewPhoto)
}
You can change your constructor of ViewModel like following code.
public PersonsViewModel(ContentPage page){
page.DisplayAlert("info","test","Ok");
}
In your Layout background code, you can use it following code.
public partial class MainPage : ContentPage
{
PersonsViewModel personsViewModel;
public MainPage()
{
InitializeComponent();
personsViewModel = new PersonsViewModel(this);
this.BindingContext = personsViewModel;
}
If you can use plugin, you can use ACR.UserDialogs. https://github.com/aritchie/userdialogs
I solved this problem using events
public MainPageVewModel()
{
Application.Current.MainPage.Loaded += LoadCards;
}
private async void LoadCards(object sender, EventArgs e)
{
// your code on View Loaded
await Application.Current.MainPage.DisplayAlert("working alert", "alert", "ok");
}

Xamarin forms check if keyboard is open or not

Is there any way to check if keyboard is open or not in Xamarin Forms? Are there any events getting fired when the keyboard opens or closes? If so, where can I find an example of it?
I don't believe that there's a Xamarin.Forms way of doing it. Anyway, for the different platforms (at least Android and iOS) there is a way to achieve what you want.
Android
Under android there is InputMethodManager class. You can obtain it from your activity
var inputMethodManager = (InputMethodManager)this.GetSystemService(Context.InputMethodService);
Now you can check if the keyboard is shown with
var keyboardIsShown = inputMethodManager.IsAcceptingText;
According to this article on CodeProject you can use a class derived from IOnGlobalLayoutListener to listen to global layout events. When this event has fired, you can use the code above to check, if the layout has been changed due to the keyboard popping up.
iOS
Under iOS you may use UIKeyboard class which allows you to observe the DidShowNotification (see here).
notification = UIKeyboard.Notifications.ObserveDidShow ((sender, args) => {
Debug.WriteLine("Keyboard is shown.");
// whatever
});
similarly you can observe DidHideNotification (and some others - see here).
Xamarin.Forms
To implement the keyboard-notification in your Xamarin.Forms the easiest way will be to implement platform dependencies which are resolved with the DependencyService. To do this, you'll first have to introduce an interface for the platform service.
public interface IKeyboardService
{
event EventHandler KeyboardIsShown;
event EventHandler KeyboardIsHidden;
}
In your platform specific projects you'll have to implement the functionality in a platform specific way. See the following code section for iOS implementation
[assembly: Xamarin.Forms.Dependency(typeof(Your.iOS.Namespace.KeyboardService))]
namespace Your.iOS.Namespace
{
public class KeyboardService : IKeyboardService
{
public event EventHandler KeyboardIsShown;
public event EventHandler KeyboardIsHidden;
public KeyboardService()
{
SubscribeEvents();
}
private void SubscribeEvents()
{
UIKeyboard.Notifications.ObserveDidShow(OnKeyboardDidShow);
UIKeyboard.Notifications.ObserveDidHode(OnKeyboardDidHide);
}
private void OnKeyboardDidShow(object sender, EventArgs e)
{
KeyboardIsShown?.Invoke(this, EventArgs.Empty);
}
private void OnKeyboardDidHide(object sender, EventArgs e)
{
KeyboardIsHidden?.Invoke(this, EventArgs.Empty);
}
}
}
The Xamarin.Forms.Dependency makes the class visible to the DependencyService. See the following code for Android implementation
[assembly: Xamarin.Forms.Dependency(typeof(Your.Android.Namespace.KeyboardService))]
namespace Your.Android.Namespace
{
public class KeyboardService : IKeyboardService
{
public event EventHandler KeyboardIsShown;
public event EventHandler KeyboardIsHidden;
private InputMethodManager inputMethodManager;
private bool wasShown = false;
public KeyboardService()
{
GetInputMethodManager();
SubscribeEvents();
}
public void OnGlobalLayout(object sender, EventArgs args)
{
GetInputMethodManager();
if(!wasShown && IsCurrentlyShown())
{
KeyboardIsShown?.Invoke(this, EventArgs.Empty);
wasShown = true;
}
else if(wasShown && !IsCurrentlyShown())
{
KeyboardIsHidden?.Invoke(this, EventArgs.Empty);
wasShown = false;
}
}
private bool IsCurrentlyShown()
{
return inputMethodManager.IsAcceptingText;
}
private void GetInputMethodManager()
{
if (inputMethodManager == null || inputMethodManager.Handle == IntPtr.Zero)
{
inputMethodManager = (InputMethodManager)this.GetSystemService(Context.InputMethodService);
}
}
private void SubscribeEvents()
{
((Activity)Xamarin.Forms.Forms.Context).Window.DecorView.ViewTreeObserver.GlobalLayout += this.OnGlobalLayout;
}
}
}
In your Xamarin.Forms app you can now obtain an instance of the correct implementation of IKeyboardService with
var keyboardService = Xamarin.Forms.DependencyService.Get<IKeyboardService>();
In Xamarin Forms in ANDROID CODE change
(InputMethodManager)this.GetSystemService(Context.InputMethodService);
with
(InputMethodManager)Xamarin.Forms.Forms.Context.GetSystemService(Context.InputMethodService);
You need to change:
var inputMethodManager = (InputMethodManager)this.GetSystemService(Context.InputMethodService);
To:
InputMethodManager inputMethodManager = (InputMethodManager)((Activity)Android.App.Application.Context).GetSystemService(Context.InputMethodService);

Banner Advertisement with Xamarin.Forms

I just want to know about the banner advertisements supported with Xamarin.Forms without any patch or loophole. Is there any advertisement provider who are providing their SDKs with the Xamarin.Forms?
Thanks in advance.
There are both SDK and step-by-step examples for Google AdMob for Xamarin.Android. You are going to need the Xamarin.GooglePlaySerives.Ads nuget.
I use it to show ads in my Xamarin.Forms app published at Google Play.
Here is the sample code for the android part of your application:
using System;
using Android.App;
using Android.Content;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Android.OS;
using Android.Support.V7.App;
using Android.Gms.Ads;
using Android;
namespace AdMobExample
{
[Activity (Label = "#string/app_name", MainLauncher = true)]
public class MainActivity : AppCompatActivity
{
protected AdView mAdView;
protected InterstitialAd mInterstitialAd;
protected Button mLoadInterstitialButton;
protected override void OnCreate (Bundle savedInstanceState)
{
base.OnCreate (savedInstanceState);
SetContentView (Resource.Layout.activity_main);
mAdView = FindViewById<AdView> (Resource.Id.adView);
var adRequest = new AdRequest.Builder ().Build ();
mAdView.LoadAd (adRequest);
mInterstitialAd = new InterstitialAd (this);
mInterstitialAd.AdUnitId = GetString (Resource.String.test_interstitial_ad_unit_id);
mInterstitialAd.AdListener = new AdListener (this);
mLoadInterstitialButton = FindViewById<Button> (Resource.Id.load_interstitial_button);
mLoadInterstitialButton.SetOnClickListener (new OnClickListener (this));
}
protected void RequestNewInterstitial ()
{
var adRequest = new AdRequest.Builder ().Build ();
mInterstitialAd.LoadAd (adRequest);
}
protected void BeginSecondActivity ()
{
var intent = new Intent (this, typeof(SecondActivity));
StartActivity (intent);
}
protected override void OnPause ()
{
if (mAdView != null) {
mAdView.Pause ();
}
base.OnPause ();
}
protected override void OnResume ()
{
base.OnResume ();
if (mAdView != null) {
mAdView.Resume ();
}
if (!mInterstitialAd.IsLoaded) {
RequestNewInterstitial ();
}
}
protected override void OnDestroy ()
{
if (mAdView != null) {
mAdView.Destroy ();
}
base.OnDestroy ();
}
class AdListener : Android.Gms.Ads.AdListener
{
MainActivity that;
public AdListener (MainActivity t)
{
that = t;
}
public override void OnAdClosed ()
{
that.RequestNewInterstitial ();
that.BeginSecondActivity ();
}
}
class OnClickListener : Java.Lang.Object, View.IOnClickListener
{
MainActivity that;
public OnClickListener (MainActivity t)
{
that = t;
}
public void OnClick (View v)
{
if (that.mInterstitialAd.IsLoaded) {
that.mInterstitialAd.Show ();
} else {
that.BeginSecondActivity ();
}
}
}
}
}
There is also a ste-by-step guide for AdMob ads for Xamarin.iOS:
using Google.MobileAds;
...
const string intersitialId = "<Get your ID at google.com/ads/admob>";
Interstitial adInterstitial;
public override void ViewDidLoad ()
{
base.ViewDidLoad ();
CreateAndRequestInterstitial ();
}
public void AfterSomeTime ()
{
if (adInterstitial.IsReady)
adInterstitial.PresentFromRootViewController (navController);
}
void CreateAndRequestInterstitial ()
{
adInterstitial = new Interstitial (intersitialId);
adInterstitial.ScreenDismissed += (sender, e) => {
// Interstitial is a one time use object. That means once an interstitial is shown, HasBeenUsed
// returns true and the interstitial can't be used to load another ad.
// To request another interstitial, you'll need to create a new Interstitial object.
adInterstitial.Dispose ();
adInterstitial = null;
CreateAndRequestInterstitial ();
};
var request = Request.GetDefaultRequest ();
// Requests test ads on devices you specify. Your test device ID is printed to the console when
// an ad request is made. GADBannerView automatically returns test ads when running on a
// simulator. After you get your device ID, add it here
request.TestDevices = new [] { Request.SimulatorId.ToString () };
adInterstitial.LoadRequest (request);
}

Resources