How can I open another activity using spinner item selection in xamarin.forms only? - xamarin

I've tried this and it looks like majority of search results reference to Android studio. I'm using visual studio, xamarin forms.
protected override void OnCreate (Bundle bundle)
{
base.OnCreate (bundle);
SetContentView (Resource.Layout.Main);
Spinner spinner = FindViewById<Spinner> (Resource.Id.spinner);
spinner.ItemSelected += new EventHandler<AdapterView.ItemSelectedEventArgs> (spinner_ItemSelected);
var adapter = ArrayAdapter.CreateFromResource (
this, Resource.Array.my_array, Android.Resource.Layout.SimpleSpinnerItem);
adapter.SetDropDownViewResource (Android.Resource.Layout.SimpleSpinnerDropDownItem);
spinner.Adapter = adapter;
}
The spinner loads perfectly but the item selected method opens the activity on loading.
private void spinner_ItemSelected (object sender, AdapterView.ItemSelectedEventArgs e)
{
SetContentView (Resource.Layout.page1);
}
How best can I load the activity on specific item selection. Note: the items are referenced in the Strings.xml.

Because Spinner chooses the first item by default when initialized, it will fire spinner_ItemSelected
You can add a conditional judgment to your spinner_ItemSelected method:
private void spinner_ItemSelected (object sender, AdapterView.ItemSelectedEventArgs e)
{
var index = e.Parent.SelectedItemPosition; //base on the select position
var obj = e.Parent.SelectedItem; // base on the selectitem value(string)
// xxx is your conditions
if(index == xxx)
{
SetContentView (Resource.Layout.page1);
}
// or
if(obj.ToString().Equals("xxx"))
{
SetContentView (Resource.Layout.page1);
}
}

Related

Why won't my Xamarin forms custom editor renderer do anything

I am trying to make a custom renderer for an editor that changes the "return" key to a "done" button and fires the Completed event when you tap it instead of typing a newline. The code in OnElementChanged() is being hit, but it's not doing anything. The "return" key is still a "return" key and it still types newlines instead of making the editor go out of focus. What am I doing wrong?
Here is the class for the custom editor (in the .NET project):
using Xamarin.Forms;
namespace Partylist.Custom_Controls
{
public class ChecklistEditor : Editor
{
}
}
Here is the custom renderer for Android:
using Android.Content;
using Android.Runtime;
using Android.Views;
using Android.Views.InputMethods;
using Android.Widget;
using Partylist.Custom_Controls;
using Partylist.Droid.Custom_Renderers;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
[assembly: ExportRenderer(typeof(ChecklistEditor), typeof(ChecklistEditorRenderer))]
namespace Partylist.Droid.Custom_Renderers
{
class ChecklistEditorRenderer : EditorRenderer
{
// Constructor because it needs to exist.
public ChecklistEditorRenderer(Context context) : base(context)
{
}
// This gets overridden so I can change what I want to change.
protected override void OnElementChanged(ElementChangedEventArgs
<Editor> e)
{
// Make it do what is should normally do so it will exist.
base.OnElementChanged(e);
// Make the "Return" button on the keyboard be a "Done" button.
Control.ImeOptions = ImeAction.Done;
// Make the thing watch for when the user hits the "Return" button.
Control.EditorAction += OnEditorAction;
}
// This makes the "Return" button fire the "Completed" event
// instead of typing a newline.
private void OnEditorAction(object sender, TextView
.EditorActionEventArgs e)
{
e.Handled = false;
if (e.ActionId == ImeAction.Done)
{
Control.ClearFocus();
e.Handled = true;
}
}
}
}
Here is the custom renderer for iOS:
using Foundation;
using Partylist.Custom_Controls;
using Partylist.iOS.Custom_Renderers;
using UIKit;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
[assembly: ExportRenderer(typeof(ChecklistEditor), typeof(ChecklistEditorRenderer))]
namespace Partylist.iOS.Custom_Renderers
{
class ChecklistEditorRenderer : EditorRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs
<Editor> e)
{
base.OnElementChanged(e);
Control.ReturnKeyType = UIReturnKeyType.Done;
}
protected override bool ShouldChangeText(UITextView textView,
NSRange range, string text)
{
if (text == "\n")
{
textView.ResignFirstResponder();
return false;
}
return true;
}
}
}
The code-behind for the page where I'm using these custom renderers (there's nothing in the XAML that should conflict with it, I think, but I'll add it to the post if people want to make sure):
using Partylist.Custom_Controls;
using System;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace Partylist.Views
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class ChecklistPage : ContentPage
{
// Struct for items on the checklist.
struct Item
{
public ChecklistEditor ItemEditor { get; set; }
public CheckBox ItemCheckbox { get; set; }
}
// Create a list of contact structs to populate the ListView.
ObservableCollection<Item> items;
// Flag for when an item is added to the list.
bool itemAdded = false;
// Constructor.
public ChecklistPage()
{
// Whatever setup stuff it was going to do anyway.
InitializeComponent();
// Set the label's BindingContext to the
// App class so it can update its text.
tipLabel.BindingContext = (App)App.Current;
}
// Override for OnAppearing().
protected override void OnAppearing()
{
// Makes the page appear.
base.OnAppearing();
// Set the page's title to be the name of the selected list.
Title = App.selectedList.Name;
// Make a toolbar item appear to access the Main Checklist
// unless we are already there.
if (App.selectedList.ListFile.Name.EndsWith(".mchec"))
{
ToolbarItems.Remove(MainChecklistButton);
}
// Set the binding context of the page to itself.
BindingContext = this;
// Start the timer for the tips banner if it is stopped.
App.tipTimer.Start();
// Set the banner's text to the current tip's sumamry.
tipLabel.Text = ((App)App.Current).CurrentTip.Summary;
OnPropertyChanged("CurrentTip");
// Subscribe the OnTipUpdate function to the tipUpdate event in the app
// class.
App.TipUpdate += OnTipUpdate;
// Make the ObservableCOllection reference something.
items = new ObservableCollection<Item>();
// Open a stream to the list that we want to display.
using (StreamReader listReader = new StreamReader(App.selectedList
.ListFile.FullName))
{
// Loop through the file and read data into the list.
while (!listReader.EndOfStream)
{
// Create a blank item.
Item newItem = new Item()
{
ItemEditor = new ChecklistEditor()
{
Text = listReader.ReadLine(),
Placeholder = "New Item",
IsTabStop = true,
AutoSize = EditorAutoSizeOption.TextChanges,
WidthRequest = 310
},
ItemCheckbox = new CheckBox()
{
Color = App.selectedList.ListItemColor,
IsChecked = bool.Parse(listReader.ReadLine())
}
};
// Subscribe OnCompleted() to the new item's "Completed"
// event.
newItem.ItemEditor.Completed += OnCompleted;
// Subscribe OnTextChanged() to the new item's
// "TextChanged" event.
newItem.ItemEditor.TextChanged += OnTextChanged;
// Add the new item to the list.
items.Add(newItem);
// Make the ListView update.
ChecklistView.ItemsSource = items;
OnPropertyChanged("contacts");
}
// Once everything is loaded, close the file.
listReader.Close();
}
}
// Override for OnDisappearing().
protected override void OnDisappearing()
{
// Makes the page disappear.
base.OnDisappearing();
// Open a stream to the file for the list.
StreamWriter listWriter = new StreamWriter(App.selectedList
.ListFile.FullName);
// Loop through the contacts list to write the contacts to the
// file.
for (int i = 0; i < items.Count; i++)
{
// Write each item to the file.
listWriter.WriteLine(items.ElementAt(i).ItemEditor.Text);
listWriter.WriteLine(items.ElementAt(i).ItemCheckbox.IsChecked);
}
// Close the stream.
listWriter.Close();
}
// Function for when the "Add New Contact" button is clicked.
private void OnAddNewItemClicked(object sender, EventArgs e)
{
// Create a blank item.
Item newItem = new Item()
{
ItemEditor = new ChecklistEditor()
{
Placeholder = "New Item",
IsTabStop = true,
AutoSize = EditorAutoSizeOption.TextChanges,
WidthRequest = 310
},
ItemCheckbox = new CheckBox()
{
Color = App.selectedList.ListItemColor,
IsChecked = false
}
};
// Subscribe OnCompleted() to the new item's "Completed"
// event.
newItem.ItemEditor.Completed += OnCompleted;
// Subscribe OnTextChanged() to the new item's
// "TextChanged" event.
newItem.ItemEditor.TextChanged += OnTextChanged;
// Add the new contact to the list.
items.Add(newItem);
// Set the "itemAdded" flag to true.
itemAdded = true;
// Make the ListView update.
ChecklistView.ItemsSource = items;
OnPropertyChanged("contacts");
// Select the new item.
ChecklistView.SelectedItem = items.ElementAt(items.Count - 1);
}
// Function for when an item is selected, used to set the focus to
// a newly added item in the list.
private async void OnItemSelected(object sender, SelectedItemChangedEventArgs e)
{
// Only runs this if an item was added (as opposed to being
// read in from the file).
if (itemAdded)
{
if (e.SelectedItem == null) return;
await Task.Delay(100); // Change the delay time if Focus() doesn't work.
((Item)e.SelectedItem).ItemEditor.Focus();
ChecklistView.SelectedItem = null;
itemAdded = false;
}
}
// Function for when the user presses "Return" on the keyboard in
// an editor.
private void OnCompleted(object sender, EventArgs e)
{
// We just want to unfocus the editor.
((Editor)sender).Unfocus();
}
// Function for when the user types anything in the editor, used
// to make sure it resizes.
private void OnTextChanged(object sender, TextChangedEventArgs e)
{
// Makes the cell resize. The cell is the parent of the
// StackLayout which is the parent of the ContentView which is
// the parent of the Editor that fired the event.
((ViewCell)((Editor)sender).Parent.Parent.Parent)
.ForceUpdateSize();
}
}
}
In Android , you need to set Single Line for EditTextView , then it will works .
For example :
...
protected override void OnElementChanged(ElementChangedEventArgs<Editor> e)
{
base.OnElementChanged(e);
// set single line will works
Control.SetSingleLine();
Control.ImeOptions = ImeAction.Done;
Control.EditorAction += OnEditorAction;
}
private void OnEditorAction(object sender, TextView.EditorActionEventArgs e)
{
e.Handled = false;
if (e.ActionId == ImeAction.Done)
{
Control.ClearFocus();
e.Handled = true;
InputMethodManager imm = (InputMethodManager)Control.Context.GetSystemService(Context.InputMethodService);
imm.HideSoftInputFromWindow(Control.WindowToken, 0);
}
}
...
The effect :
About iOS to achieve that , you can refer to follow code :
[assembly: ExportRenderer(typeof(Editor), typeof(CustomEditorRenderer))]
namespace AppEntryTest.iOS
{
class CustomEditorRenderer : EditorRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<Editor> e)
{
base.OnElementChanged(e);
Control.ReturnKeyType = UIReturnKeyType.Done;
}
protected override bool ShouldChangeText(UITextView textView, NSRange range, string text)
{
if (text == "\n")
{
textView.ResignFirstResponder();
return false;
}
return true;
}
}
}
The effect :
====================================Update================================
If need to wrap text in Android , you can set background for EditTextView :
Adding bg_gray_border.xml in Resources/drawable folder :
<?xml version="1.0" encoding="utf-8" ?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#ffffff" />
<stroke
android:width="1dp"
android:color="#DEDEDE" />
<corners android:radius="6dp" />
</shape>
Used in Renderer class :
...
protected override void OnElementChanged(ElementChangedEventArgs<Editor> e)
{
base.OnElementChanged(e);
Control.SetSingleLine();
Control.SetBackgroundResource(Resource.Drawable.bg_gray_border);
Control.ImeOptions = ImeAction.Done;
Control.EditorAction += OnEditorAction;
}
...
The effect :
Add wapped text in iOS ,
...
protected override void OnElementChanged(ElementChangedEventArgs<Editor> e)
{
base.OnElementChanged(e);
Control.ReturnKeyType = UIReturnKeyType.Done;
Control.Layer.BorderColor =UIColor.Gray.CGColor;
Control.Layer.BorderWidth = 1;
Control.Layer.CornerRadius = 5;
}
...
The effect :
Here is the sample project .

Xamarin memory leakage

I'm a newbie to Xamarin. I have created an application that uses DrawerLayout(Android). But my problem is that every time i select a item in the menu(DrawerLayout menu), the memory increases, and this causes the app to become slow and crush. I've tried to use Xamarin profiler to analyze memory leaks - it suspects something called String.FastAllocationString, but it doesn't really show the line(code) that causes String.FastAllocationString issue. Please help ? Here is my code :
MainActivity
DrawerLayout drawerLayout;
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
// Set our view from the "main" layout resource
SetContentView(Resource.Layout.Main);
drawerLayout = FindViewById<DrawerLayout>(Resource.Id.drawer_layout);
// Init toolbar
var toolbar = FindViewById<Android.Support.V7.Widget.Toolbar>(Resource.Id.app_bar);
SetSupportActionBar(toolbar);
SupportActionBar.SetTitle(Resource.String.app_name);
SupportActionBar.SetDisplayHomeAsUpEnabled(true);
SupportActionBar.SetDisplayShowHomeEnabled(true);
// Attach item selected handler to navigation view
var navigationView = FindViewById<NavigationView>(Resource.Id.nav_view);
navigationView.NavigationItemSelected += NavigationView_NavigationItemSelected;
// Create ActionBarDrawerToggle button and add it to the toolbar
var drawerToggle = new ActionBarDrawerToggle(this, drawerLayout, toolbar, Resource.String.open_drawer, Resource.String.close_drawer);
drawerLayout.SetDrawerListener(drawerToggle);
drawerToggle.SyncState();
}
void NavigationView_NavigationItemSelected(object sender, NavigationView.NavigationItemSelectedEventArgs e)
{
var ft = FragmentManager.BeginTransaction();
ft.AddToBackStack(null);
switch (e.MenuItem.ItemId)
{
case (Resource.Id.nav_incidents):
SupportActionBar.SetTitle(Resource.String.toolbar_Test);
ft.Add(Resource.Id.HomeFrameLayout, new Test());
break;
}
ft.Commit();
ft.Dispose();
// Close drawer
drawerLayout.CloseDrawers();
}
Fragment
[Activity(Label = "Test")]
public class Test : Fragment
{
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
View view = inflater.Inflate(Resource.Layout.Test, container, false);
return view;
}
}
Xamarin Profiler
you have to check fragment is available before you add new one
switch (e.MenuItem.ItemId)
{
case (Resource.Id.nav_incidents):
SupportActionBar.SetTitle(Resource.String.toolbar_Test);
Fragment myFragment =
(Fragment)FragmentManager.FindFragmentByTag("FRAGMENT1");
if (myFragment.IsVisible){
ft.Replace(Resource.Id.HomeFrameLayout, new Test(),"FRAGMENT1");
}else{
ft.Add(Resource.Id.HomeFrameLayout, new Test(),"FRAGMENT1");
}
break;
}
Hope this help

InvalidCastException in xaml while navigate the page

This is code of click event of MainPage
void ItemView_ItemClick(object sender, ItemClickEventArgs e)
{
var item = ((EventItem)e.ClickedItem);
this.Frame.Navigate(typeof(EventPage), new Navigator() { Parent = "Dashboard", Event = item });
}
In image the code of next page
The sender parameter of the loaded event is the control that raised the event (in this case, the current page); so its type is the type of the page, not Navigator.
Apparently you're trying to access the argument that was passed to Frame.Navigate. To do that, you should override the OnNavigatedTo method:
protected override void OnNavigatedTo(NavigationEventArgs e)
{
var param = (Navigator)e.Parameter;
...
}

RecyclerView Click event

I have created a RecyclerView adapter and I'm trying to start an activity when a row is clicked:
public override OnBindViewHolder(RecyclerView.ViewHolder holder, int position)
{
MyViewHolder viewHolder = (MyViewHolder)holder;
viewHolder.MyView.Click += (sender, e) =>
{
var context = viewHolder.MyView.Context;
var intent = new Intent(context, typeof(DetailActivity));
context.StartActivity(intent);
}
}
When I click the first row it will take me to the activity like I want. If I scroll down so that the first row is rebound and then scroll back to the top again and then click the first row then my Click event fires twice. Once for the first row that was bound and then again for a row that was bound when I scrolled.
Is there an event you need to handle to unregister the click events?
I believe the standard pattern is to setup your clickhandlers in the constructor of the ViewHolder. Then in OnBindViewHolder, you update the Views/Data inside the ViewHolder.
Something like this (not compiled code):
Adapter:
public override OnBindViewHolder()
{
MyViewHolder viewHolder = (MyViewHolder)holder;
viewHolder.SetData(whatever data you care about);
}
MyViewHolder:
public MyViewHolder(View view) : base(view)
{
MainView = view;
MainView.Click += (sender, e) =>
{
var context = MainView.Context;
var intent = new Intent(context, typeof(DetailActivity));
context.StartActivity(intent);
}
}
Doing it this way keeps the Adapter cleaner by putting business logic in the ViewHolder, and also prevents your click handlers from being constantly setup and torn down as you scroll.

Silverlight TabItem template not working correctly

In a SL4 application i need to restyle my TabItems (actually add a button in the header).
So i took the TabItem's control template from here and added the functionality i wanted.
This seems to work fine, (i could dynamically add tabitems) with one exception:
i think this posted control template is behaving somehow "arbitrary": every time the mouse hoovers over a non selected TabItem header, this gets selected WHITHOUT clicking!! (afaik this is not the default behavior: the user user has to click a header to make this tabitem the selected one).
I tried to find why it is behaving like this, with no luck!
Is there someone who can enlighten my darkness???
Thanks in advance!
Well it turns out the error was not in the control template but in the class, the style was applied to.
In detail: the class the style was applied to is the following (in it you will see my comment about the "wrong behavior"):
public class WorkspaceViewModel : TabItem
{
public WorkspaceViewModel()
{
DefaultStyleKey = typeof(WorkspaceViewModel);
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
Button closeButtonSel = base.GetTemplateChild("PART_CloseTopSelected") as Button;
Button closeButtonUnsel = base.GetTemplateChild("PART_CloseTopUnSelected") as Button;
if (closeButtonSel != null)
closeButtonSel.Click += new RoutedEventHandler(closeButtonSel_Click);
if (closeButtonUnsel != null)
closeButtonUnsel.Click += new RoutedEventHandler(closeButtonSel_Click);
//this part is causing the effect i was complaining about!
//and has to be removed
this.MouseEnter += delegate(object sender, MouseEventArgs e)
{
IsSelected = true;
};
}
void closeButtonSel_Click(object sender, RoutedEventArgs e)
{
//this is the close request method used in the CloseTabItemCommand
OnRequestClose();
}
#region CloseTabItemCommand
private RelayCommand closeTabItemCommand;
public ICommand CloseTabItemCommand
{
get
{
if (this.closeTabItemCommand == null)
this.closeTabItemCommand = new RelayCommand(p => this.OnRequestClose(), p => this.CanCloseTabItem());
return this.closeTabItemCommand;
}
}
private bool CanCloseTabItem()
{
return true;
}
public event EventHandler RequestClose;
private void OnRequestClose()
{
if (RequestClose != null)
RequestClose(this, EventArgs.Empty);
}
#endregion
}

Resources