Problem: I have followed different tutorials on how to implement a spinner in Android, such as this. However, when the app first opens, the toast is made immediately without the user selecting anything.
My Solution: I tried creating a boolean variable, that is it set to 'false' on start-up. The toast will show only once that boolean is set to 'true'. This solution works: no toast is made on start-up, but if the user had to select the first item (which is selected by default), then no toast is made. After the user selects a different option and thereafter re-selects the first option, then only will the toast for the first item be shown. This isn't a major issue though. My code is shown below.
In my OnCreate method, I have the following:
boolToast = false;
Spinner spinner = FindViewById<Spinner>(Resource.Id.spinner);
spinner.SetSelection(0, false);
spinner.ItemSelected += new EventHandler<AdapterView.ItemSelectedEventArgs>(Spinner_ItemSelected);
var adapter = ArrayAdapter.CreateFromResource(this, Resource.Array.array, Android.Resource.Layout.SimpleSpinnerItem);
adapter.SetDropDownViewResource(Android.Resource.Layout.SimpleSpinnerDropDownItem);
spinner.Adapter = adapter;
My spinner_ItemSelected method is as follows:
private void Spinner_ItemSelected(object sender, AdapterView.ItemSelectedEventArgs e)
{
Spinner spinner = (Spinner)sender;
string toast = string.Format("Selected item is {0}", spinner.GetItemAtPosition(e.Position));
if (boolToast == true)
{
Toast.MakeText(this, toast, ToastLength.Long).Show();
}
boolToast = true;
}
I am satisfied with it already, but wanted to know if there is maybe a more elegant or proper way of achieving this. I have seen a few other fairly similar questions, but those were mostly in Java which I am very unfamiliar with.
Related
I have one Fragment "AddNewIncomeFragment " with a TextView("#+id/lblAccountHead"). On click on textview it starts a new activity "AccountHeadListActivit" which shows a list of existing Account Head. On selection of account head from "AccountHeadListActivity" i want to update "lblAccountHead" of first activity with selected account name, other values need to be intact.
Earlier i did it using "messaging center" for xamarin form. Now i trying to do the same in xamarin native(android).
*****UPDATED WITH FIRST SOLUTION APPLIED****
Click event in AddNewIncomeFragment which start account head activity:
public void onAccounyHeadClick(object sender, EventArgs e)
{
var intend = new Intent(this.Activity, typeof(AccountHeadListActivity));
//this.StartActivity(intend);
this.StartActivityForResult(intend, 1000);
}
ListView Selection event of Account head activity
void OnSelection (object sender, SelectedItemChangedEventArgs e)
{
var result = new Intent();
result.PutExtra("name", "Salary Account");
result.PutExtra("id", 2);
SetResult(Result.Ok, result);
Finish();
}
On closing this activity i want to update textview of previous activity with selected account head name/id. Please share what option we have to do this in xamarin andriod, should use StartActivityForResult,Local Notifications or any best approach.
I have implemented above solution and it is working fine. But the issue is- 2nd activity "AccountHeadList" contains an add new account link which start a new activity "AddNewAccount"- Now if user create a new account and save it then this activity need to be closed and 1st activity need to updated with newly created account name. So basically "StartActivityForResult" failed when it involves three activity and need to updated 1st activity from 3rd activity- Please suggest.
Thanks,
#Paul
To receive a result, call
startActivityForResult() instead of startActivity()
then in your second activity, call the setResult(result) method to set the result before you finish the activity finish() after that in your original activity you can override the method
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
// set the text view with the data from the result
}
I have been fighting with this for a while...
I'm using Windows Phone 8.1 Runtime (not silverlight) and I have the following code:
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
if (!ExitWithoutSave().Result) return;
this.navigationHelper.OnNavigatedFrom(e);
}
private async Task<bool> ExitWithoutSave()
{
MessageDialog dialog = new MessageDialog("There are unsaved changes, are you sure you wish to leave?", "Unsaved changes");
dialog.Commands.Clear();
dialog.Commands.Add(new Windows.UI.Popups.UICommand("Yes") { Id = 0 });
dialog.Commands.Add(new Windows.UI.Popups.UICommand("No") { Id = 1 });
dialog.DefaultCommandIndex = 0;
dialog.CancelCommandIndex = 1;
var result = await dialog.ShowAsync();
if (result.Label == "No")
{
canceled = true;
}
return canceled;
}
Basically, I want to ask the user if he wishes to leave without saving, if he says no, then I want to block this functionality.
The problem is, if there's an await during the execution of the OnNavigatedFrom, Windows phone thinks the app has broken and the UI gets blocked.
Is there any way to correctly show a message box on pressing the back button?
If not, is it possible to disable the back button entirely for this page?
Thanks,
Keran
Edit 15-11-2015:
Just wanted to bump this post. I had no luck using HardwareButton events together with Navigation Helper, MessageBoxes still don't work. I can't even cancel the back button press.
So I wanted to renew my question: What is the best way to create a confirm message box on back button press on Windows Phone 8.1 Runtime? F. e. message: "You have unsaved changes, do you wish to exit?": Yes / No.
You can use following event .
HardwareButtons.BackPressed += HardwareButtons_BackPressed;
void HardwareButtons_BackPressed(object sender, BackPressedEventArgs e)
{
}
I want to capture 'Send' button event of outlook using UI Automation.
Right now i am able to get 'Focus Change Event' like whenever iam minimizing or maximizing the WINWORD window the the event is raised instead of that i want to get the event on Send button click.
private void SendButtonInvoke()
{
Process[] processes = Process.GetProcessesByName("WINWORD");
AutomationElement aeOutLook = null;
foreach (var item in processes)
{
aeOutLook = AutomationElement.FromHandle(item.MainWindowHandle);
}
//AutomationElement outlookelm = AutomationElement.FromHandle(processName.MainWindowHandle);
AutomationElement buttonAddInstance = aeOutLook.FindFirst(TreeScope.Descendants,
new PropertyCondition(AutomationElement.NameProperty, "Send"));
if (buttonAddInstance == null)
{
MessageBox.Show("Add button instance not found");
}
else
{
AutomationPropertyChangedEventHandler ButtonEvent =
new AutomationPropertyChangedEventHandler(ButtonChecked_EventHandler);
//Attaching the EventHandler
Automation.AddAutomationPropertyChangedEventHandler(buttonAddInstance, TreeScope.Children,
ButtonEvent, AutomationElement.NameProperty);
}
}
private void ButtonChecked_EventHandler(object sender, AutomationEventArgs e)
{
AutomationElement ar = sender as AutomationElement;
MessageBox.Show("Button Clicked Sucessfully.");
}
You have to specifiy the EventHandler for the involved UIA Pattern. (For your case it's likely to be the InvokePattern):
Automation.AddAutomationEventHandler(InvokePattern.InvokedEvent, AutomationElement buttonAddInstance ,TreeScope.Element, new AutomationEventHandler(OnStartInvoke));
private static void OnStartInvoke(object src, AutomationEventArgs e)
{
//logic
}
I wrote and tested the code below and it seems to work for me.
private void AddEmailSendEvent()
{
// Find the new email window
PropertyCondition newEmailWindowCondition = new PropertyCondition(AutomationElement.NameProperty, "Untitled - Message (HTML) ");
AutomationElement NewEmailWindow = AutomationElement.RootElement.FindFirst(TreeScope.Children, newEmailWindowCondition);
// Find the Send Button
PropertyCondition sendEmailButtonCondition = new PropertyCondition(AutomationElement.NameProperty, "Send");
AutomationElement sendButton = NewEmailWindow.FindFirst(TreeScope.Descendants, sendEmailButtonCondition);
// If supported, add the invoke event
if (sendButton.GetSupportedPatterns().Any(p => p.Equals(InvokePattern.Pattern)))
Automation.AddAutomationEventHandler(InvokePattern.InvokedEvent, sendButton, TreeScope.Element, handler);
}
private void handler(object sender, AutomationEventArgs e)
{
// Do whatever is needed, for testing this just adds a message to my forms Main UI
AddMessage("Invoke event occured");
}
I should note that I'm using the .Net 4.0 automation libs. I've found the older ones don't always work the way I want them. I also tested this with Outlook 2013, and both outlook and the new email message were already open when I tested this. It doesn't handle waiting for them to appear.
Just so your aware, these events don't always work for all controls. Some custom controls are made in such a way the invoke events are not reported to the UI in a way the event can register. With that said, from my testing you should be able to use this method on the send button.
Invoking vs mouse clicks: Just to add a little more detail, the standard control causes the invoke event to fire when a user clicks it. "Invoke" is just the standard event fired on clickable controls. The only time a click wouldn't fire the same invoke is if the developer decided to intercept the click somehow and redirect it elsewhere. I've seen this a lot when people build there own custom controls.
If your not sure about whether a control using/firing the invoke event or not you can get use the Accessible Event Watcher to watch a control as you click it. You can get more information on the tool here: https://msdn.microsoft.com/en-us/library/windows/desktop/dd317979(v=vs.85).aspx
I have some code that when I call my CustomMessageBox it displays the box with a user prompt for an amount of my object to add, once that is done I have it added to a list of objects. Once added, it then Displays a MessageBox.Show to just let the user know it was added.
My problem is that when I run the code it executes all the code, bypasses the display of the Custom message box, then displays the MessageBox.Show, and THEN displays the CMB.Show. I ran the code through the debugger and followed the trail and it hits the CMB.Show before the MessageBox.Show, but is displayed once the code is done. Sorry, I am still learning and might not be telling the problem well, please let me know if there is anything I can further explain upon.
Some code:
private int BasicLand(Card basicLand)
{
var countBox = new TextBox
{
Name = "count",
Width = 100,
};
var cmbCount = new CustomMessageBox
{
Caption = "Blah",
Content = countBox,
RightButtonContent = "ok",
};
cmbCount.Dismissed += (s1, e1) =>
{
switch (e1.Result)
{
case CustomMessageBoxResult.RightButton:
if (int.TryParse(countBox.Text, out tempInt) && Convert.ToInt32(countBox.Text) > 0)
{
countReturn = Convert.ToInt32(tempInt);
break;
}
else
{
//Some code for error....
}
}
};
cmbCount.Show();
return countReturn;
}
Then the other part that triggers first but is last in the code block.
MessageBox.Show("Object was added to List!");
I tried adding the ShowDialog to the custom box but it came up broken in VS. BasicLand is called within another method and when the object is added to the list it will display the MessageBox.Show.
The problem with your code is, it does not take into account that any user interaction is asynchronous. When you call Show() it will actually show the messagebox, but it will not block your currently running thread, the other statements after the call to Show() will be executed immediately and thus your method returns a returnvalue that has not been provided by the user but is just the default. To fix this you have to write your code in continuations.
private void PromtUserForFeeblefezerAmount(Action<int> continueFeeblefzing, Action cancel)
{
var messagebox = CreateFeeblefezerPromt();
messagebox.Dismissed += (sender, args) =>
{
if ( args.Result == CustomMessageBoxResult.RightButton )
continueFeeblefzing( GetFeeblefezerAmount(messagebox) );
else
cancel();
};
messagebox.Show();
}
The following code is from the C# portion of my Android Mono application. It is going to eventually be the GUI for a multimeter simulator, but right now just displays text. It is rather straight forward:
-Click one of the buttons to go to that meter (voltmeter, ammeter, ohmmeter)
-Click the "re-scan" button and a TextView tells you how many times you clicked that button.
-Click one of the other meter buttons or the home button to switch views
That much is working flawlessly. Unfortunately, once I switch views, the buttons cease to work. Below is the code for the Ohm button and the Amp button. The Ohm button is the 'complete' one that brings up views of all of the other screens. For testing purposes, I was going to the amp screen but when I go there, its re-scan button does nothing. None of the buttons do anything.
I am fairly certain that the issue is my use of the delegate commands, but none of my research has led me in any way towards a solution.
I can provide more of the main code and the XML code if needed.
ampButton.Click += delegate
{
SetContentView(Resource.Layout.AmpScreen);
Button ampButtonData = FindViewById<Button>(Resource.Id.CurrentButtonamp);
TextView ampData = FindViewById<TextView>(Resource.Id.ampdata);
ampButtonData.Click += delegate
{
ampData.Text = string.Format("{0} clicks!", count2++);
};
Button amp2volt = FindViewById<Button>(Resource.Id.Amp2VoltButton);
Button amp2ohm = FindViewById<Button>(Resource.Id.Amp2OhmButton);
Button amp2home = FindViewById<Button>(Resource.Id.Amp2HomeButton);
};
ohmButton.Click += delegate
{
SetContentView(Resource.Layout.OhmScreen);
Button ohmButtonData = FindViewById<Button>(Resource.Id.CurrentButtonohm);
TextView ohmData = FindViewById<TextView>(Resource.Id.ohmdata);
ohmButtonData.Click += delegate
{
ohmData.Text = string.Format("{0} clicks!", count3++);
};
Button ohm2amp = FindViewById<Button>(Resource.Id.Ohm2AmpButton);
Button ohm2volt = FindViewById<Button>(Resource.Id.Ohm2VoltButton);
Button ohm2home = FindViewById<Button>(Resource.Id.Ohm2HomeButton);
ohm2amp.Click += delegate
{
SetContentView(Resource.Layout.AmpScreen);
};
ohm2volt.Click += delegate
{
SetContentView(Resource.Layout.VoltScreen);
};
ohm2home.Click += delegate
{
SetContentView(Resource.Layout.Main);
};
};
I think your problem is that you are replacing the entire view each time - so the button instances are changing.
What happens inside SetContentView is that the InflatorService gets asked to create a brand new set of UI objects based on the passed in XML, the existing UI is wiped clean and then those new UI objects are put in their place.
It doesn't matter if the new UI objects happen to have the same resource identifiers as the old objects - they are still separate instances.
If you want to continue using your current approach, then you need to rewire all your events after each SetContentView - e.g.
ohm2amp.Click += delegate
{
SetContentView(Resource.Layout.AmpScreen);
RewireEvents();
};
with
private void RewireEvents()
{
var ohm2home = FindViewById<Button>(Resource.Id.ohm2home);
ohm2home.Click += { /* todo */ };
// etc
}
alternatively, maybe consider a different UI:
e.g. you could change the Visibility on different child layouts rather than calling SetContentView to replace everything
e.g. or you could use multiple activities (or tabs) instead of a single activity
Hope that helps