How can I save multiple user input to a spinner? - c#

I am working with c# on Xamarin, Visual studio 2015 to develop android app. I have a box where user have to input a string value then when a button clicked, I want this value to be added to a spinner and to be saved and reloaded for the next open so he can choose the value he entered without the need to re-input it. Till now I don"t have a problem, I got my idea worked. But what I am struggling in is: if a user have input a value then clicked the button, then entered another value, only the last value is saved and showed in the spinner when the APP is re-opened. What I want is: each value entered by the user need to be saved and showed in the spinner. Then if the user want to delete the value he entered before, a button for delete item.
Here's what I have done so far:
string user;
ISharedPreferences prefs = PreferenceManager.GetDefaultSharedPreferences(this);
var items = new List<string>() { "1", "2", "3" };
Button button4 = FindViewById<Button>(Resource.Id.button4);
Spinner spinner = FindViewById<Spinner>(Resource.Id.spinner1);
EditText input = FindViewById<EditText>(Resource.Id.input);
user = input.Text;
button4.Click += delegate
{
user = input.Text;
items.Add(user);
ISharedPreferencesEditor editor = prefs.Edit();
editor.PutString("try", user);
editor.Apply();
};
user = prefs.GetString("try", "no");
items.Add(user);
var adapter3 = new ArrayAdapter<string>(this, Android.Resource.Layout.SimpleSpinnerItem, items);
spinner.Adapter = adapter3;
Those code are adding and saving the user input to the spinner when I reopen the app but if the user entered two values then only the last one is saved. What I want is each value to be saved and displayed in the spinner.
Thank you in advance..

Try this:
// a constant to avoid magic strings
const string KEY_FOR_TRY = "TRY";
ArrayAdapter<string> _spinnerAdapter;
protected override void OnCreate (Bundle savedInstanceState)
{
base.OnCreate (savedInstanceState);
// Set our view from the "main" layout resource
SetContentView (Resource.Layout.Main);
string user;
ISharedPreferences prefs = PreferenceManager.GetDefaultSharedPreferences (this);
// Load the items from pref and fill the spinner
// if pref is empty just set an empty list.
// GetStringSet because you are loading the collection you saved.
var savedItems = prefs.GetStringSet (KEY_FOR_TRY, new List<string> ());
var items = new List<string> (savedItems);
Button button4 = FindViewById<Button> (Resource.Id.button4);
Spinner spinner = FindViewById<Spinner> (Resource.Id.spinner1);
EditText input = FindViewById<EditText> (Resource.Id.input);
user = input.Text;
button4.Click += delegate
{
//you might want validate for empty strings and if entry is already saved to prevent duplicates.
user = input.Text;
items.Add (user);
ISharedPreferencesEditor editor = prefs.Edit ();
//PutStringSet because you are saving a collection.
editor.PutStringSet (KEY_FOR_TRY, items);
editor.Apply ();
//do this only if you want to refresh the spinner values.
_spinnerAdapter.Insert (user, 0);
_spinnerAdapter.NotifyDataSetChanged ();
};
//Get the first item if there is any, don't know why you need this.
user = items.FirstOrDefault ();
_spinnerAdapter = new ArrayAdapter<string> (this, Android.Resource.Layout.SimpleSpinnerItem, items);
spinner.Adapter = _spinnerAdapter;
}
}
Hope this helps!

Related

Show listview in middle of method but wait until SelectionChanged event fires before method continues

Windows UWP app in C#. I have a method that checks a condition and depending on the condition, it may need to show a listview to the user so they can select an item from the list. I have more code in the method, after I potentially show the list view that needs to run after. However, because the listview shows and I have to wait for the SelectionChanged event handler to fire, I cannot figure out how to pause the calling method on that line until the event handler is completed for SelectionChanged. I don't have code written yet, so here is some pseduo code to illustrate:
private void LookupEmployee(string searchCriteria)
{
List<string> matches = GetEmployeeNameMatchesFromCriteria(searchCriteria);
if(matches.Count == null || matches.Count == 0)
{
//No matches found, warn user
return;
}
if(matches.Count == 1)
{
//We are good, we have just one match which is desirable.
}
if(matches.Count > 1)
{
//This means we have more than one match and we need to popup list view to have user select one
ShowListView(matches);
}
//Process Employee data here.
}
I know one option is to "daisy chain" calls by breaking out the final processing of employee data to another method and call that from the event handler for SelectionChanged of the listview. However, this has two issues. First, if I just have one match, then I will not be showing the listview or getting the SelectionChanged anyway. Second, if I had a bunch of variables and other things at the beginning of the method to be used at the end of the method, I don't want to (nor do I know how to) pass all of that through and back from the event handler in the event I need to show it.
I guess what I am looking for in a way is how the MessageDialog is handled.
var md = new MessageDialog("My Message");
md.Commands.Add(new UICommand("Okay")
{
});
var result = await md.ShowAsync();
if (result.Label == "Okay")
{
DoStuff;
}
Where using this will wait on the line:
await md.ShowAsync();
Until the user clicks the button, at which point the method can continue from there.
I guess I am looking for something similar to that where I can hold on the line of the method in the case that I need to show the listview until the user selects and item and grab the item that was selected.
Thoughts?
Thanks!
Okay, I think I found what I was looking for so I wanted to post the code. This is similar to how a modal window worked in the old days. Basically, you can use a ContentDialog which will allow you to "wrap" any controls you want in it. In my case, I want to show a ListView, so I wrap that in the ContentDialog. Here is what I have:
First we can do our tests and based on the tests, we can create the ContentDialog/ListView if needed. If we do create the ContentDialog, we can also setup the Display parameters so it fits the way we want it to.
private async void checkProductMatches()
{
var selectedItem = string.Empty;
//Check our results from DB.
if (productResults.Count == 0)
{
//This means we didn't find any matches, show message dialog
}
if (productResults.Count == 1)
{
//We found one match, this is ideal. Continue processing.
selectedItem = productResults.FirstOrDefault().Name;
}
if (productResults.Count > 1)
{
//Multiple matches, need to show ListView so they can select one.
var myList = new ListView
{
ItemTemplate = Create(),
ItemsSource =
productResults,
HorizontalAlignment = HorizontalAlignment.Stretch,
VerticalAlignment = VerticalAlignment.Stretch
};
var bounds = Window.Current.Bounds;
var height = bounds.Height;
var scroll = new ScrollViewer() { HorizontalAlignment = HorizontalAlignment.Stretch, VerticalAlignment = VerticalAlignment.Stretch, Height = height - 100 };
var grid = new StackPanel();
grid.Children.Add(myList);
scroll.Content = grid;
var dialog = new ContentDialog { Title = "Title", Content = scroll };
Now, we wire up the event handler for the ListView SelectionChanged event and grab the selectedItem should this event raise.
myList.SelectionChanged += delegate (object o, SelectionChangedEventArgs args)
{
if (args.AddedItems.Count > 0)
{
MyProducts selection = args.AddedItems[0] as MyProducts;
if (selection != null)
{
selectedItem = selection.Name;
}
}
dialog.Hide();
};
Finally, we await the showing of the ContentDialog.
var s = await dialog.ShowAsync();
What this will do is, if we have one item, there is no need to popup the content dialog. So, we can assign the one result to our selectedItem variable and proceed. However, if we have multiple matches, we want to display a list for the user to select the one item. In this case, we create the ContentDialog, ListView and display parameters. They key is to wire up the event handler before we call to show the dialog and inside of the event handler, we make sure to cancel or close the dialog. Then we call to await the dialog showing. This will pause execution of this method on that line while the dialog is showing. Once the user selects an item, the event handler will raise, get the selected item and then close the dialog, which will then allow the method to continue execution from the awaited line.
Here is the full method:
private async void checkProductMatches()
{
var selectedItem = string.Empty;
//Check our results from DB.
if (productResults.Count == 0)
{
//This means we didn't find any matches, show message dialog
}
if (productResults.Count == 1)
{
//We found one match, this is ideal. Continue processing.
selectedItem = productResults.FirstOrDefault().Name;
}
if (productResults.Count > 1)
{
//Multiple matches, need to show ListView so they can select one.
var myList = new ListView
{
ItemTemplate = Create(),
ItemsSource =
productResults,
HorizontalAlignment = HorizontalAlignment.Stretch,
VerticalAlignment = VerticalAlignment.Stretch
};
var bounds = Window.Current.Bounds;
var height = bounds.Height;
var scroll = new ScrollViewer() { HorizontalAlignment = HorizontalAlignment.Stretch, VerticalAlignment = VerticalAlignment.Stretch, Height = height - 100 };
var grid = new StackPanel();
grid.Children.Add(myList);
scroll.Content = grid;
var dialog = new ContentDialog { Title = "Title", Content = scroll };
myList.SelectionChanged += delegate (object o, SelectionChangedEventArgs args)
{
if (args.AddedItems.Count > 0)
{
MyProducts selection = args.AddedItems[0] as MyProducts;
if (selection != null)
{
selectedItem = selection.Name;
}
}
dialog.Hide();
};
var s = await dialog.ShowAsync();
}
//Test furter execution. Ideally, selected item will either be the one record or we will
//get here after the list view allows user to select one.
var stringTest = string.Format("Selected Item: {0}", selectedItem);
}
Hope this helps someone.

How to load the correct image from my database to the correct item when I click on my pin?

So I have a map in my code with multiple pins. When I click on a pin I get to a newpage with the pintitle. That works but If I want to add an image/and or description to that same page (that I also store on my database, parse) it doesnt work as I only get the topimage stored in the database on every different pin i click.
string picture;
string theDescription;
var getItems = await parseAPI.getInfo (Application.Current.Properties ["sessionToken"].ToString ()); //I load my data.
foreach (var currentItem in getItems["results"])
{
var prodPic = "";
if(currentItem["image"] != null)
{
prodPic = (string)currentItem ["image"] ["url"];
}
picture = prodPic; //i add the picture.
theDescription = currentItem ["description"].ToString (); // i add the descrption
dName = currentItem ["name"].ToString (); //and the title
var pin = new Pin ();
pin.Position = new Position (16,13);
pin.Label = dName; //and then i connect my title here so it works, but how should I do it with my picture + description?
pin.Address = "click for info";
pin.Clicked += onButtonClicked1;
theMap.Pins.Add (pin); //adding my pins to my map.
}
void onButtonClicked1 (object sender, EventArgs e)
{
Pin pin = (Pin)sender;
Navigation.PushAsync (new DetailPage (pin.Label, picture, theDescription )); //label works, so every pin get a unique label, but picture + the description remains the same inside the item i enter.
}
so It works with the title, and that is because I have connected the pin to my onbuttonclicked1 (pin.label) function I assume? so I how should I do it with my image + description so the pin does not get the same picture + description on every pin i enter
UPDATED IDEA:
new List <String> ourItems = new List<String> ();
ourItems.Add (theDescription);
ourItems.Add (picture);
Like this? and then somehow connect them into my OnButtonClicked1 function?
You are running a foreach loop which repeatedly sets the same "picture" variable. That is, every time you iterate, you are setting the "picture" and "description" variables to whatever value is relevant for the current iteration without actually persisting any of the previous values anywhere.
Your loop would look something like this:
Iteration one: picture = "pictureOne.png";
Iteration two: picture = "pictureTwo.png";
Iteration three: picture = "PictureThree.png";
...etc
What this means is that by the time your loop ends, you will have reset your imagine multiple times, with the variable retaining its last set value (in the above example that would be "pictureThree.png"
One way (not necessarily the best, mind you) would be to have an empty list, which you then populate from within the loop.

Share data (string) between activities, save and add it to ListView C# Xamarin

first I have to mention that I'm new to Android app development. I'm using Xamarin (C#) for developing apps. Here's the problem. I want to write simple ToDoList application to learn how to store/share data between activities.
Button "Add" is placed in Main layout. When user clicks it, another screen shows up where user can name(EditText) task and add its description(EditText). When user clicks button "Complete" in that screen, app redirects user to Main screen and add user's task to a listview.
I did some research and found out that I could do that with SharedPreferences, saving data to file on device, or sql.
I decided to go with SharedPreferences. Problem is, everything I found about SharedPrefences (and I searched a lot, believe me) is written in Java which wouldn't be such a big problem, but nobody ever mentioned sharing data between different activites using shared preferences. My code is below, I'm really struggling with this over a day, so please help me.
My MainActivity (where button "Add" is):
public class MainActivity : Activity
{
Button add;
ListView list;
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
///set layout
SetContentView(Resource.Layout.Main);
///define variables, buttons, lists
add = FindViewById<Button>(Resource.Id.add);
list = FindViewById<ListView>(Resource.Id.list);
///get string from secondActivity
var prefs = Application.Context.GetSharedPreferences("todolist", FileCreationMode.Private);
var preference = prefs.GetString("task", null);
///add string (preference) to list
List<string> lista = new List<string>();
lista.Add(preference);
///"convert" list to listview
ArrayAdapter<string> adapter = new ArrayAdapter<string>(this, Resource.Layout.Main, lista);
list.Adapter = adapter;
add.Click += delegate
{
StartActivity(new Intent(this, typeof(addActivity)));
};
}
}
My second screen (second activity):
public class addActivity : Activity
{
/// define variables and create list
Button complete;
EditText name, description;
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
SetContentView(Resource.Layout.add);
///variables
complete = FindViewById<Button>(Resource.Id.complete);
name = FindViewById<EditText>(Resource.Id.name);
description = FindViewById<EditText>(Resource.Id.description);
///converting to string
string title = name.Text.ToString();
string content = description.Text.ToString();
///pass string to MainActivity
complete.Click += delegate
{
if (String.IsNullOrEmpty(title))
{
Toast.MakeText(this, "Please enter task name", ToastLength.Short).Show();
}
else
{
var prefs = Application.Context.GetSharedPreferences("todolist", FileCreationMode.Private);
var prefEditor = prefs.Edit();
prefEditor.PutString("task", title);
prefEditor.Commit();
Toast.MakeText(this, "Task added", ToastLength.Short).Show();
StartActivity(new Intent(this, typeof(MainActivity)));
}
};
}
}
You could use an SQLite database here. I would recommend doing so if you want to build a ToDo list. It will make it easier for you as well.
Xamarin has plenty of documentation on how to set this up here:
https://developer.xamarin.com/guides/cross-platform/application_fundamentals/data/
They also have an example app that does pretty much what you want to do here:
https://github.com/xamarin/mobile-samples/tree/master/Tasky
You can pass the string to the next activity through an Intent
Instead of defining that intent in the arg of your Start activivty you should define it before that and then add the string using the putExtra method
examples: How to use putExtra() and getExtra() for string data

Done Button For PickerView ToolBar Not Working

Done button added to pickerview toolbar,
But on click done button click event is not working.
public override void ViewDidLoad(){
myPickerView = new UIPickerView (RectangleF.Empty){
AutoresizingMask = UIViewAutoresizing.FlexibleWidth,
ShowSelectionIndicator = true,
Model = model,
Hidden = true,
BackgroundColor = UIColor.Clear
};
toolbar = new UIToolbar();
toolbar.BarStyle = UIBarStyle.Default;
toolbar.Translucent = true;
toolbar.SizeToFit();
// Create a 'done' button for the toolbar and add it to the toolbar
UIBarButtonItem doneButton = new UIBarButtonItem("Done", UIBarButtonItemStyle.Done,
(s, e) => {
Console.WriteLine ("Calling Done!");
txt_RegistrationType.Text = selectedType;
txt_Email.ResignFirstResponder();
txt_UserName.ResignFirstResponder();
txt_Password.ResignFirstResponder();
txt_PhoneNumber.ResignFirstResponder();
txt_ConformPassword.ResignFirstResponder();
});
toolbar.SetItems(new UIBarButtonItem[]{doneButton}, true);
model.PickerChanged += (sender, e) => {
Console.WriteLine("selected vlaue {0}",e.SelectedValue);
txt_RegistrationType.Text = e.SelectedValue;
};
this.myPickerView.AddSubview (toolbar);
myPickerView.Frame = PickerFrameWithSize (myPickerView.SizeThatFits (SizeF.Empty));
View.AddSubview (myPickerView);
myPickerView.AddSubview (toolbar);
}
On click selected items
its shows pickerView "PickerView.Hidden = false" which appears picker view and toolbar with done button. When click the done button on toolbar its click event is not working.
Please let me know for getting an event on click done button.
Firstly, I want to point out that you're adding your "toolbar" to your current "UIPickerView" twice. Once with:
this.myPickerView.AddSubview (toolbar);
and another time with:
myPickerView.AddSubview (toolbar);
Secondly, why the toolbar? It only adds complexity and dependencies to your project.
What I suggest, is scrap the whole block of code, and make two separate controls on this view:
A button
A UIPickerView
public override void ViewDidLoad()
{
UIButton button = new UIButton (new RectangleF (5, 5, frame_width, 30));
button.TouchUpInside += Method;
UIPickerView pickerView = new UIPickerView (new RectangleF (5, 45, frame_width, 180));
pickerView.Model = model
this.View.AddSubviews (new UIView[]{button, pickerView});
}
void Method (object sender, eventargs e)
{
Console.WriteLine ("Done");
/*If you want to make the pickerView disappear,
you can add a pickerView.Hide() here, and then call a method
to redraw the controls on the current view.*/
}
I hope this helps. Good luck!

NullReferenceException was unhandled and I don't know why?

I'm creating a WPF C# application that has a datagrid hooked up to a SQL Server database. The user has the option of editing data in the datagrid by highlighting a row and clicking an edit button which then populates several textboxes with the data from the highlighted row. At this point, the user can then edit the data, click save and the datagrid reflects the changes. Until recently that feature was working fine. However, I was asked to add a feature that displays a highlighted row of data somewhere else on the screen (as looking at a datagrid for too long can become tiresome). So when a user clicks on a row a series of textblocks to the right of the datagrid change to show the data of the highlighted row in an easier to view format. That feature also works fine. The issue I'm having now, is that when a row is highlighted and automatically displays the data in the textblocks, if the user also tries to edit that row, the application crashes. The data displays just fine in the textboxes after the user clicks edit (while simultaneously displaying the same highlighted row in the textblocks); it's just when save is clicked that I run into an issue.
Debugging the program shows that everything is running smoothly. However after clicking save, the debugger jumps back up to my myGridSelectionChanged event and say's "NullReferenceException was unhandled -- Object reference not set to instance of an object" When I reload the program however, the datagrid reflects the changes that I tried to make before the application crashed. I'm assuming that means that the issue doesn't have to do with actually editing the database, rather the problem is with the textblocks not being able to reflect those edits. Below is some of my code:
Here is the code for the save button:
private void saveBtn_Click(object sender, RoutedEventArgs e)
{
var stqmDC = new SqtmLinqDataContext();
var selectedRow = EditGrid.GetSelectedRow(myGrid);
var ID = EditGrid.GetCell(myGrid, selectedRow, 0);
string selectedID = ((TextBlock)ID.Content).Text;
int convertedID = Convert.ToInt32(selectedID);
int newQuantity = int.Parse(quantityTxt.Text);
var query = from info in stqmDC.General_Infos
where info.Quote_ID == convertedID
select info;
foreach (General_Info info in query)
{
info.Customer_Name = customerNameTxt.Text;
info.OEM_Name = oemNameTxt.Text;
info.Qty = newQuantity;
info.Quote_Num = quoteNumberTxt.Text;
info.Fab_Drawing_Num = fabDrawingNumTxt.Text;
info.Rfq_Num = rfqNumberTxt.Text;
info.Rev_Num = revNumberTxt.Text;
}
try
{
stqmDC.SubmitChanges();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
var mainTable = from generalInfo in stqmDC.GetTable<General_Info>()
select new
{
generalInfo.Quote_ID,
generalInfo.Open_Quote,
generalInfo.Customer_Name,
generalInfo.OEM_Name,
generalInfo.Qty,
generalInfo.Quote_Num,
generalInfo.Fab_Drawing_Num,
generalInfo.Rfq_Num,
generalInfo.Rev_Num
};
myGrid.ItemsSource = mainTable;
leftSP.Visibility = Visibility.Hidden;
rightSP.Visibility = Visibility.Hidden;
cancelBtn.Visibility = Visibility.Hidden;
submitBtn.Visibility = Visibility.Hidden;
saveBtn.Visibility = Visibility.Hidden;
sendBtn.Visibility = Visibility.Hidden;
}
And the code for displaying the highlighted row in textblocks:
private void myGridSelectionChanged(object sender, SelectionChangedEventArgs e)
{
var rowSelection = EditGrid.GetSelectedRow(myGrid);
var quoteID = EditGrid.GetCell(myGrid, rowSelection, 0);
string quoteIDEdit = ((TextBlock)quoteID.Content).Text;
QuoteIDtxtblk.Text = quoteIDEdit;
var date = EditGrid.GetCell(myGrid, rowSelection, 1);
string dateEdit = ((TextBlock)date.Content).Text;
Datetxtblk.Text = dateEdit;
var custName = EditGrid.GetCell(myGrid, rowSelection, 2);
string custNameEdit = ((TextBlock)custName.Content).Text;
CustomerNametxtblk.Text = custNameEdit;
var OemName = EditGrid.GetCell(myGrid, rowSelection, 3);
string OemNameEdit = ((TextBlock)OemName.Content).Text;
OemNametxtblk.Text = OemNameEdit;
var Quantity = EditGrid.GetCell(myGrid, rowSelection, 4);
string QuantityEdit = ((TextBlock)Quantity.Content).Text;
Quantitytxtblk.Text = QuantityEdit;
var quoteNum = EditGrid.GetCell(myGrid, rowSelection, 5);
string quoteNumEdit = ((TextBlock)quoteNum.Content).Text;
QuoteNumbertxtblk.Text = quoteNumEdit;
var fabDrawing = EditGrid.GetCell(myGrid, rowSelection, 6);
string fabDrawingEdit = ((TextBlock)fabDrawing.Content).Text;
FabDrawingNumbertxtblk.Text = fabDrawingEdit;
var rfqNum = EditGrid.GetCell(myGrid, rowSelection, 7);
string rfqNumEdit = ((TextBlock)rfqNum.Content).Text;
RfqNumbertxtblk.Text = rfqNumEdit;
var revNum = EditGrid.GetCell(myGrid, rowSelection, 8);
string revNumEdit = ((TextBlock)revNum.Content).Text;
RevNumbertxtblk.Text = revNumEdit;
}
Thanks in advance to anyone who can help.
Where exactly would you think it's handled?
To handle exceptions that cause app crashes you need to exception-proof every "entry point", defined as every spot in your app where unmanaged code can call in or a the code starts running on a different thread.
This includes button click handlers. Add a try/catch in your button handler and show some UI message that an error happened and write a log message or at least a 'Debug.WriteLine(exceptionObjectHere);' so you can see where did the exception come from
You change ItemsSource of a DataGrid in saveBtn_Click which means that your selection will disapear and SelectionChanged will be fired.
So you need to handle this case (myGrid.SelectedItem == null) somewhere, and just do nothing (return) if so.

Categories