UISearchBar SearchButtonClicked never called? - c#

I am new to iOS, and I am using Xamarin Studio on MacOS (I already have my Xamarin.Droid app and now making it on iOS).
I am struggling using an UISearchBar, I want a really basic usage tho .. I tried to follow this recipes (https://developer.xamarin.com/recipes/ios/content_controls/search-controller/) but still can't manage to make it work.
What I am trying to get is a SearchBar, in which the user should type text then press the "search" button on the keyboard. Pressing the search button should call code that will populate an UITableView with data retrieved on my webservice.
Here's my Storyboard structure, a ViewController which contains an UITableView with a name
I tried the following code, everything seems to work fine, but when I press search on the keyboard, SearchButtonClicked event is never fired ... And I can't figure out why
public partial class SearchViewController : UIViewController
{
public SearchViewController(IntPtr handle) : base(handle)
{
}
public override void ViewDidLoad()
{
var searchController = new UISearchController(this);
searchController.SearchBar.SizeToFit();
searchController.SearchBar.ShowsCancelButton = true;
searchController.SearchBar.SearchButtonClicked += delegate {
Console.WriteLine("Hello ????");
this.Title = "This code is never called";
};
RunnersSearchTableView.TableHeaderView = searchController.SearchBar;
base.ViewDidLoad();
}
}
Also tried
public partial class SearchViewController : UIViewController
{
public SearchViewController(IntPtr handle) : base(handle)
{
}
public override void ViewDidLoad()
{
var searchController = new UISearchController(this);
searchController.SearchBar.SizeToFit();
searchController.HidesNavigationBarDuringPresentation = true;
searchController.SearchBar.ShowsCancelButton = true;
searchController.SearchBar.Delegate = new MySearchBarDelegate();
RunnersSearchTableView.TableHeaderView = searchController.SearchBar;
base.ViewDidLoad();
}
}
public class MySearchBarDelegate : UISearchBarDelegate
{
public MySearchBarDelegate()
{
}
public override void SearchButtonClicked(UISearchBar searchBar)
{
Console.WriteLine("TEST");
searchBar.ResignFirstResponder();
base.SearchButtonClicked(searchBar);
}
}
And also tried
public partial class SearchViewController : UIViewController
{
public SearchViewController(IntPtr handle) : base(handle)
{
}
public override void ViewDidLoad()
{
var searchController = new UISearchController(this);
searchController.SearchBar.SizeToFit();
searchController.HidesNavigationBarDuringPresentation = true;
searchController.SearchBar.ShowsCancelButton = true;
searchController.SearchBar.WeakDelegate = this;
RunnersSearchTableView.TableHeaderView = searchController.SearchBar;
base.ViewDidLoad();
}
[Export("searchBarSearchButtonClicked:")]
public virtual void SearchButtonClicked(UISearchBar searchBar)
{
Console.WriteLine("hello??");
searchBar.ResignFirstResponder();
}
}
None of them seems to work

You need to register delegate for searchbar .
[Foundation.Register("UISearchBar", true)]
for more : https://developer.xamarin.com/api/type/UIKit.UISearchBar/

Related

How to prevent Xamarin forms, a custom menu and WebView renderer with EvaluateJavascript from freezing?

I have created a custom menu item which appears in the default menu which pops up when selecting text on my custom WebView.
On clicking on the menu item it calls EvaluateJavascript to get the selected WebView text, and then passes the text to another page.
However after performing this action once or twice, some text on certain areas of the screen start to become unresponsive to clicks eg. text on the parts of the WebView become unselectable, clicks on that part of the screen on other pages becomes unresponsive and even the soft keyboard becomes unclickable in some spots. If this continues for a while sometimes my app will then suddenly freeze the entire operating system and I have to soft reset my phone. It appears that there maybe some serious memory leakage going on.
I create my custom menu item in the MainActivity class:
public override void OnActionModeStarted(ActionMode mode)
{
if (Root.IsCurrentPageType<DictPage>() && DictP.IsWebViewFocused())
{
IMenu menu = mode.Menu;
menu.Add("To Notes");
menu.GetItem(0).SetOnMenuItemClickListener(new MyMenuItemOnMenuItemClickListener(this, mode));
}
base.OnActionModeStarted(mode);
}
It is then handled in the Listener class...
public class MyMenuItemOnMenuItemClickListener : Java.Lang.Object, IMenuItemOnMenuItemClickListener
{
private MainActivity mContext;
ActionMode _mode;
public MyMenuItemOnMenuItemClickListener(MainActivity activity, ActionMode mode)
{
this.mContext = activity;
_mode = mode;
}
public bool OnMenuItemClick(IMenuItem item)
{
WEB.CopyToMainNotes();
Device.BeginInvokeOnMainThread(() =>
{
//close menu if clicked
_mode?.Finish();
});
return true;
}
}
...which calls CopyToMainNotes on my derived WebView class and its associated Renderer and EventHandler classes:
public class WebViewEx : Xamarin.Forms.WebView
{
public static WebViewEx WEB;
//Namespace
//YourClass
public event WebViewExEventHandler CallNativeMethodEvent;
public void CallNativeMethod(WebViewExEventType type)
{
WebViewExEventArgs e = new WebViewExEventArgs();
e.EventType = type;
CallNativeMethodEvent?.Invoke(this, e);
}
public WebViewEx()
{
WEB = this;
}
public void CopyToMainNotes()
{
Device.BeginInvokeOnMainThread(() =>
{
CallNativeMethod(WebViewExEventType.copyToMainNotes);
});
}
}
public delegate void WebViewExEventHandler(object sender, WebViewExEventArgs e);
public class WebViewExEventArgs : EventArgs
{
public enum WebViewExEventType { copyToMainNotes };
public WebViewExEventType EventType = WebViewExEventType.copyToMainNotes;
public WebViewExEventArgs() : base()
{
}
}
public class WebViewExRenderer : WebViewRenderer
{
public WebViewExRenderer(Android.Content.Context context) : base(context)
{
}
protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.WebView> e)
{
base.OnElementChanged(e);
if (Control != null)
{
WebViewEx ex = e.NewElement as WebViewEx;
ex.CallNativeMethodEvent += WebViewEx_CallNativeMethodEvent;
}
}
internal class JavascriptCallback : Java.Lang.Object, IValueCallback
{
public JavascriptCallback(Action<string> callback)
{
_callback = callback;
}
private Action<string> _callback;
public void OnReceiveValue(Java.Lang.Object value)
{
_callback?.Invoke(Convert.ToString(value));
}
}
private void WebViewEx_CallNativeMethodEvent(object sender, WebViewExEventArgs e)
{
switch (e.EventType)
{
case WebViewExEventType.copyToMainNotes:
{
CopyToMainNotes();
break;
}
}
}
public void CopyToMainNotes()
{
string script = "(function(){ return window.getSelection().toString()})()";
var response = string.Empty;
Control?.EvaluateJavascript(script, new JavascriptCallback((r) =>
{
response = r;
Device.BeginInvokeOnMainThread(() =>
{
DPage.CopyThisTextToAnotherPage(response.ToString().Trim('\"'));
});
}));
}
}
The CopyToMainNotes method above is where the EvaluateJavascript takes place and the selected text finally gets sent to another page.
Any ideas where I might be going wrong here? Thanks in advance!

System.Reflection.TargetInvocationException on MessagingCenter.Send

I'm trying to build simple navigation using MessagingCenter but I'm receiving System.Reflection.TargetInvocationException when I pressed the back button (hardware button).
Here is how I get the error;
After app load,
I hit the back button (hardware button)
Then after the app got minimized, I open it in recent app
After that, I click on Login then I got this error:
Unhandled Exception:
System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation.
pointing on
MessagingCenter.Send<object>(this, App.EVENT_LAUNCH_MAIN_PAGE);
in Login Method in LoginPage.xaml.cs
PS: The code works well if I don't hit the back button (hardware button)
Here is the code:
App.xaml.cs
public partial class App : Application
{
public static string EVENT_LAUNCH_LOGIN_PAGE = "EVENT_LAUNCH_LOGIN_PAGE";
public static string EVENT_LAUNCH_MAIN_PAGE = "EVENT_LAUNCH_MAIN_PAGE";
public App()
{
InitializeComponent();
MainPage = new App3.LoginPage();
MessagingCenter.Subscribe<object>(this, EVENT_LAUNCH_LOGIN_PAGE, SetLoginPageAsRootPage);
MessagingCenter.Subscribe<object>(this, EVENT_LAUNCH_MAIN_PAGE, SetMainPageAsRootPage);
}
private void SetLoginPageAsRootPage(object sender)
{
MainPage = new NavigationPage(new LoginPage());
}
private void SetMainPageAsRootPage(object sender)
{
MainPage = new NavigationPage(new App3.MainPage());
}
protected override void OnStart()
{
// Handle when your app starts
}
protected override void OnSleep()
{
// Handle when your app sleeps
}
protected override void OnResume()
{
// Handle when your app resumes
}
}
LoginPage.xaml.cs
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class LoginPage : ContentPage
{
public Command LoginCommand { get; }
public LoginPage()
{
InitializeComponent();
LoginCommand = new Command(() => Login());
Button btn = new Button { Text = "Login", Command = LoginCommand };
Content = new StackLayout
{
Children =
{
btn
}
};
}
public void Login()
{
MessagingCenter.Send<object>(this, App.EVENT_LAUNCH_MAIN_PAGE);
}
}
MainPage.xaml.cs
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
ToolbarItems.Add(new ToolbarItem("Logout", "", () => Logout()));
}
public void Logout()
{
MessagingCenter.Send<object>(this, App.EVENT_LAUNCH_LOGIN_PAGE);
}
}
That was a bug in Xamarin.Forms Version 2.3.3.175. To fix this bug install an earlier version of Xamarin.Forms. I get my app running with version 2.3.0.107.
The bug in version 2.3.3.175 should be fixed in version 2.3.4-pre1.

RecyclerView.ViewHolder On Click Items

I'm working with RecyclerView (Xamarin.Android) on Visual Studio 2017, this is my first Xamarin project since I'm a native Android developer, and I'm kinda confused respect to implementing the On Items Clicks events on the Recycler View. I just created the RecyclerView.Adapter using the default template that the IDE provides (RecyclerAdapter class), it looks a lot like the native implementation:
Here my Code:
using System;
using Android.Views;
using Android.Widget;
using Android.Support.V7.Widget;
namespace Airlink
{
class PdfAdapter : RecyclerView.Adapter
{
public event EventHandler<PdfAdapterClickEventArgs> ItemClick;
public event EventHandler<PdfAdapterClickEventArgs> ItemLongClick;
Pdf[] items;
public PdfAdapter(Pdf[] data)
{
items = data;
}
// Create new views (invoked by the layout manager)
public override RecyclerView.ViewHolder OnCreateViewHolder(ViewGroup
parent, int viewType)
{
//Setup your layout here
View itemView = null;
//var id = Resource.Layout.__YOUR_ITEM_HERE;
itemView=LayoutInflater.From(parent.Context).
Inflate(Resource.Layout.pdf_item, parent, false);
var vh = new PdfAdapterViewHolder(itemView, OnClick,
OnLongClick);
return vh;
}
// Replace the contents of a view (invoked by the layout manager)
public override void OnBindViewHolder(RecyclerView.ViewHolder
viewHolder, int position)
{
var item = items[position];
// Replace the contents of the view with that element
PdfAdapterViewHolder holder = viewHolder as
PdfAdapterViewHolder;
holder.pdf_name.Text = items[position].Name;
}
public override int ItemCount => items.Length;
void OnClick(PdfAdapterClickEventArgs args) =>
ItemClick?.Invoke(this, args);
void OnLongClick(PdfAdapterClickEventArgs args) =>
ItemLongClick?.Invoke(this, args);
}
public class PdfAdapterViewHolder : RecyclerView.ViewHolder
{
public TextView pdf_name { get; set; }
public PdfAdapterViewHolder(View itemView,
Action<PdfAdapterClickEventArgs> clickListener,
Action<PdfAdapterClickEventArgs> longClickListener) :
base(itemView)
{
pdf_name = itemView.FindViewById<TextView>
(Resource.Id.pdf_name);
itemView.Click += (sender, e) => clickListener(new
PdfAdapterClickEventArgs { View = itemView, Position =
AdapterPosition });
itemView.LongClick += (sender, e) => longClickListener(new
PdfAdapterClickEventArgs { View = itemView, Position =
AdapterPosition });
}
}
public class PdfAdapterClickEventArgs : EventArgs
{
public View View { get; set; }
public int Position { get; set; }
}
}
So, given my code, I just want to know where I can handle the clicked View, I'm not sure if I have to implement some code on the PdfAdapterClickEventArgs Class or on the OnClick and OnLongClick voids that receive the PdfAdapterClickEventArgs object, let's say I want to show a Toast showing the name of the TextView inside the View clicked. I use to handle this action on native Android using:
view.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//my code here
}
}
inside the ViewHolder constructor method.
I mean, I can change the default C# implementation in order to get something similar to the snippet above, but I would like to keep the original C# code.
You will implement your OnClick event handler in the Activity/Fragment where instance of your Adapter is created.
This line means you are making the ItemClick event public in your adapter class.
public event EventHandler<PdfAdapterClickEventArgs> ItemClick;
So you are now able to do this:
var adapter = new PdfAdapter(data);
adapter.ItemClick += OnItemClick;
....
....
myRecyclerView. SetAdapter(adapter);
And you will have a method:
public void OnItemClick(object sender, PdfAdapterClickEventArgs e)
{
var view = args.View; //this is your view
Toast.MakeText(this, $"Item Position: {args?.Position}", ToastLength.Short).Show();
}
Hope this helps.

Creating Smart Tag for Form (not other control) using c#

As the Form of System.Windows.Forms inherits from Control, I was wondering if there is a way to create a Custom Form and its Designer with some options (shortcuts) to create a title or somthings like that.
I tried this, but nothings happend, the Form I calles ManagedForm
[Designer(typeof(ManagedFormDesigner))]
public class ManagedForm : Form{
//code here
}
[PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name = "FullTrust")]
public class ManagedFormDesigner : ControlDesigner {
private DesignerActionListCollection actionLists;
public override DesignerActionListCollection ActionLists {
get {
if (actionLists == null) {
actionLists = new DesignerActionListCollection();
actionLists.Add(new ManagedFormDesignerActionList(this.Component));
}
return actionLists;
}
}
}
public class ManagedFormDesignerActionList : DesignerActionList {
private ManagedForm managedForm = null;
private DesignerActionUIService designerActionUISvc = null;
public ManagedFormDesignerActionList(IComponent component) : base(component) {
this.managedForm = component as ManagedForm;
this.designerActionUISvc =
GetService(typeof(DesignerActionUIService))
as DesignerActionUIService;
}
public override DesignerActionItemCollection GetSortedActionItems() {
DesignerActionItemCollection items = new DesignerActionItemCollection();
items.Add(new DesignerActionMethodItem(this, "CreateTitle", "Create Title", "Appearence", true));
return items;
}
public void CreateTitle() {
Panel pTitulo = new Panel();
pTitulo.Size= new Size(100,25);
pTitulo.Dock = DockStyle.Top;
(this.Component as ManagedForm).Controls.Add(pTitulo);
}
}
Action list are show when you click on the little arrow on the control inside a form (or on a component on the bottom of the designer if the object is a component).
Other things you can do is to manage verbs.
Verbs Handling is implemented on the ControlDesigner class (ManagedFormDesigner in your case).
You can see verbs clicking right mouse button or on the bottom of the properties (i.e. TabControl ha 2 verbs, add tab and remove tab).
You can implement verbs adding to ControlDesigner (or ComponentDesigner) class something like this
private DesignerVerbCollection _verbs;
public override DesignerVerbCollection Verbs
{
get
{
if (_verbs == null)
{
_verbs = new DesignerVerbCollection();
_verbs.Add(new DesignerVerb("Create Title", new EventHandler(MyCreateTitleHandler)));
}
return _verbs;
}
}
private void MyCreateTitleHandler(object sender, EventArgs e)
{
// Do here something but take care to show things via IUIService service
IUIService uiService = GetService(typeof(IUIService)) as IUIService;
}

How do I return from a SetWebViewClient call?

I have a simple monodroid problem
Activity A calls Activity B
Activity B has a webview and looks like this
SetContentView(Resource.Layout.webView);
WebView webView = FindViewById<WebView>(Resource.Id.webView1);
webView.Settings.JavaScriptEnabled = true;
webView.LoadUrl(url);
webView.SetWebViewClient(new dealWithWebView());
In dealWithWebView(), I have this
class dealWithWebView : WebViewClient
{
WebView webView;
public override void OnPageFinished(WebView view, string url)
{
webView = view;
Context c = webView.Context;
// it goes away and does something
// calls another method in the class which returns back here
}
}
Problem is that I don't seem to be able to get it to return back from the WebViewClient and then from "B" return back to "A".
How do I do this?
You could do something like
SetContentView(Resource.Layout.webView);
WebView webView = FindViewById<WebView>(Resource.Id.webView1);
webView.Settings.JavaScriptEnabled = true;
webView.LoadUrl(url);
webView.SetWebViewClient(new dealWithWebView(this));
which uses:
class dealWithWebView : WebViewClient
{
Activity parent;
public dealWithWebView(Activity parent)
{
this.parent = parent;
}
public override void OnPageFinished(WebView view, string url)
{
var webView = view;
Context c = webView.Context;
// it goes away and does something
// calls another method in the class which returns back here
// all done - so finish the parent now
parent.Finish();
}
}
You can also use:
class WebViewClientCallback : WebViewClient
{
public event EventHandler PageLoaded = delegate { };
public override void OnPageFinished(WebView view, string url)
{
base.OnPageFinished(view, url);
PageLoaded(this, EventArgs.Empty);
}
}
Usage:
clientCallback = new WebViewClientCallback();
clientCallback.PageLoaded += delegate(object sender, EventArgs e) {
// What to do next?
// Do the things you want to do after PageIsLoaded
};
webview.SetWebViewClient(clientCallback);
webview.LoadData("<p>HTML-CONTENT</p>", "text/html; charset=UTF-8", null);

Categories

Resources