Is there a way to disable the background on the DisplayActionSheet using Xamarin Forms? - xamarin.forms

Issue:
I'm dealing with an issue on how to disable the background on a DisplayActionSheet or DisplayAlert in Xamarin Forms, so the only way the user can "get out" of the popup windows is via the options that the window has prompted.
Here is the screenshot of the problem:
http://postimg.org/image/s16cp66wf/
Do you know if there's a simple way to do this? I've been looking but I couldn't found....
I tried:
Using the methods that came with the control but nothing else came up. What I'm doing right now is to call it till I have an answer.
Code:
From my code behind I call the event
async void OnNextClicked(object sender, EventArgs args)
{
await userValidations.RMLocationServiceWindow();
}
And then I call the popup window
public async Task RMLocationServiceWindow ()
{
string rta = null;
while (rta == null)
{
rta = await App.Current.MainPage.DisplayActionSheet("Do you accept to use the service?", "Cancel", "Continue");
}
}
I couldn't find any that keep the focus on the windows.

Unfortunately this could not be done in displayactionsheet, for you to be able to achieve this, you must create a Modal Page with list where you could be able to disable back button until there is a selection on the list. hope it helps

I think I got your problem. If your problem is about when user not click 'Cancel' or 'Continue' and he click empty space, you wont get value for rta. Think about if user click white space it mean is canceling. This way may help you.
while (rta == null)
{
rta = await App.Current.MainPage.DisplayActionSheet("Do you accept to use the service?", "Cancel", "Continue");
if (rta==null)
rta=="Cancel";
}

Related

DispatchKeyEvent stops firing after Xamarin Forms Entry control IsFocused

I am building a Xamarin Forms mobile app that runs in Android on a Zebra scanner. I flip 2 different StackLayouts to IsVisble true/false to display different stuff in the UI. (StackLayout1 and StackLayout2)
The customer wants the user to be able to use the app entirely from the hardware keyboard on the scanner. So I have used the device Settings so that it never displays the virtual keyboard (I don’t think that matters for the issue I am having.)
I am overriding DispatchKeyEvent in a PageRenderer in the Android project and everything is working great … except.
The problem case:
StackLayout1 is displayed
the user taps an Entry control, putting the focus there
the user taps a button in the UI
the app displays StackLayout2
at this point the DispatchKeyEvent never fires no matter what key I press on the device keyboard
If an Entry box does NOT get the focus (step #2 above) the DispatchKeyEvent always fires in StackLayout2 and the StackLayouts display as expected.
If I programatically put the focus in an Entry box in StackLayout2 at step #3 above the DispatchKeyEvent fires fine.
That is not an OK solution. I have tried to progamatically put the focus on StackLayout2, and that code seems to do what is expected but DispatchKeyEvent does not fire.
Maybe I need to do something in the Android-project PageRenderer so that it is aware of StackLayout2 when it is made IsVisible = true.
Update 2: I found that I did NOT need custom StackLayouts. The solution which I posted below does not include any of this stuff I am describing in Update 1 (sorry, if that's confusing).
Update 1:
I added a ViewRenderer for both StackLayouts, and the code is hitting the OnElementChanged event when StackLayout2's IsVisible property flips to true, just great. Although the problem case is the same: DispatchKeyEvent does not fire once StackLayout2 is displayed, if an EntryBox had the focus in StackLayout1
Here is the OnElementChanged part of the new StackLayout ViewRenders
async void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
switch (e.PropertyName)
{
case "IsVisible":
if (Element.IsVisible)
{
if (sender is StackLayout)
{
this.FocusableViewAvailable(this); // if I comment these 2 lines out I get the same bad result
this.Focusable = true; // if I comment these 2 lines out I get the same bad result
this.FocusableInTouchMode = true;
var dd = this.RequestFocus(); // this is always false
var ee = this.IsFocused; // this is always false
}
}
break;
}
}
Also, as I am pointing out in the comments ^ there, IsFocused is always false.
Ideas?
My hunch, "Maybe I need to do something in the Android project PageRenderer" was correct. In the DispatchKeyEvent I had to make the MainPage have the focus when the keypress was handled.
Here is what the DispatchKeyEvent looks like now (notice the comments):
public override bool DispatchKeyEvent(KeyEvent ke)
{
// MainPage.ReceiveKeyPress(e); is the method that this method returns to
bool KeyPressWasHandled = false;
KeyPressWasHandled = (Element as MainPage).ReceiveKeyPress(ke);
if (KeyPressWasHandled)
{
// this next block seems to be needed so that this class
// continues to receive the keypress event after an Entry box has had the focus
this.Focusable = true;
this.FocusableInTouchMode = true;
this.RequestFocus();
return true; // returning true tells the parent class that the keypress has been handled
} else
{
try
{
return base.DispatchKeyEvent(ke);
}
Now the "problem case" in my initial post is no longer a problem.
NOTE: I found that I did NOT need the custom ViewRenderers that I had made for the StackLayouts.

Xamarin.Forms - Tooltip as menu

I couldn't find it anywhere, so I'm trying this option.
I have an app with frames and when I click on the frame I want a tooltip-like window to come up where the user can select an option. Something like this:
I took a look at Xamarin.Android.Tooltips, but that doesn't seem to be able to handle multiple options to choose from. Are there other packages available which work better for this case?
I hope my question is clear.
Something like this does not exist in vanilla Xamarin.Forms unless you're ready to create your own custom view and platform renderers. You may consider using a NuGet package like ACR User Dialogs which provides many pre-programmed views that would probably satisfy your requirements.
There are many ways to achieve this.
1.Xamarin Community Toolkit PopUp.
There is a sample included in above link,you can check it here: https://github.com/ahoefling/XCT-Popups-Samples?WT.mc_id=xamarin-c9-jamont .
The main code is:
private async void Button_Clicked(object sender, EventArgs e)
{
// uncomment the different types of popups
HelloPopup();
//PositionPopup();
await ReturnValuePopup();
await GenericTypePopup();
}
void HelloPopup() => Navigation.ShowPopup(new HelloPopup());
void PositionPopup() => Navigation.ShowPopup(new PositionPopup());
async Task ReturnValuePopup()
{
object result = await Navigation.ShowPopupAsync(new ReturnValuePopup());
await DisplayAlert("Popup Response", $"{result}", "OK");
}
async Task GenericTypePopup()
{
var result = await Navigation.ShowPopupAsync(new GenericValuePopup());
await DisplayAlert(result.Title, result.Message, result.Ok);
}
2.Display Pop-ups
To display an action sheet, await DisplayActionSheet in any Page, passing the message and button labels as strings. The method returns the string label of the button that was clicked by the user.
A simple example is shown here:
private async void Button_Clicked(object sender, EventArgs e)
{
string action = await DisplayActionSheet("Choose an option", "Cancel", "Delete", "Option 1", "Option 2", "Option 3");
Debug.WriteLine("Action: " + action);
}
3.nuget Rg.Plugins.Popup .
It is a cross platform plugin for Xamarin.Forms which allows you to open Xamarin.Forms pages as a popup that can be shared across iOS, Android, UWP, and macOS. Also the plugin allows you to use very simple and flexible animations for showing popup pages.
For more , check: https://github.com/rotorgames/Rg.Plugins.Popup .

Xamarin Forms - Duplicate Screens

I'm writing an app using Xamarin Forms and I have an issue I was hoping someone can help with.
My app contains a screen which has multiple icons that can be pressed which would then open a new screen.
My issue is that if you press the icon twice really fast, the app opens up 2 instances of the same screen (it's not just related to a double press, if you press the icon 6 times very fast it will open up 6 duplicate screens). Pressing the Back button, closes the top screen to reveal the duplicate screen underneath. Pressing the Back button again navigates you back to the original screen.
This issue seems to occur on any screen within my app so I'm hoping other people will have experienced it and know of a solution to prevent duplicate screens being displayed.
This is a known issue in TapEvents.
My hack is, in the code-behind, have a bool variable _canTap.
Inside the method you are calling to push new page, first you check if canTap, then set to false, and only set to true after navigating to another page. This way all taps will be disregarded.
Example:
private bool _canTap = true;
public void YourMethod()
{
if(_canTap)
{
_canTap = false;
YourMethodToNavigate();
_canTap = true;
}
}
In the Icon_Pressed method add this,
this.IsEnabled = false;
await Navigation.PushAsync(new MyPage());
this.IsEnabled = true;
It disables the page until the current Icon pressed event is finished
This is known problem with Xamarin apps. I've used a private variable combined with a try-finally pattern to solve this. Ex:
bool allowTap = true;
public void ButtonTapped()
{
try
{
if(allowTap)
{
allowTap = false;
// Do whatever...
}
}
finally
{
allowTap = true;
}
}
The finally makes sure allowTap gets set back to true no matter what happens, short of a complete crash. Note that you can also use a catch block between the try and finally blocks to grab any errors if needed.

How to restrict closing of display alert while clicking on outside of alert

I am using xamarin.forms for an Android application, where I need to display an alert with YES on left side and NO on the right side. I can manage it swapping YES to NO and NO to YES, but when outside of the alert is clicked by default it is taking as YES.
How can I achieve that?
I use something like this and it works quite well, even though it has come to my attention that it might not be future-proof. If the user doesn't click on YES, the display just disappears without doing anything else.
bool continue_logout = false;
try
{
continue_logout = await DisplayAlert("Logout", "If you logout, you also clear the cache of all data. Are you sure you want to continue?", "Yes", "No");
if (continue_logout)
{
// Clean up stuff and clear out user settings
}
}
catch {
}
finally
{
if (continue_logout)
App.Current.MainPage = new NavigationPage(new LoginPage());
}

How does one cancel/unaccept a drag-and-drop operation in Flex 3?

Goal:
Allow the user to delete a record by dragging a row from an AdvancedDataGrid, dropping it onto a trash-can icon and verify the user meant to do that via a popup alert with "OK" and "Cancel" buttons.
What is working:
Dragging/Dropping a row onto the trash icon.
If the user clicks the "OK" button, the record is deleted.
If the user clicks the "Cancel" button, the operation is canceled.
Problem:
After the user clicks the "Cancel" button and the popup alert closes, no rows in the ADG can be dragged. I've discovered that after sorting the ADG, by clicking on a column header, the user can begin dragging rows again.
Code: (changed from original post)
<mx:Image source="{trashImage}" buttonMode="true"
toolTip="drag a participant here to delete them from the project"
dragDrop="deleteParticipantDrop(event)" dragEnter="deleteParticipantEnter(event)"
dragExit="deleteParticipantDragExit(event)" top="4" right="122" id="image2" />
// trashImage Event Handlers:
private function deleteParticipantEnter(event:DragEvent):void
{
var component:IUIComponent = IUIComponent(event.currentTarget);
dragComponent = component;
DragManager.acceptDragDrop(component);
DragManager.showFeedback(DragManager.MOVE);
deleteParticipantDragEvent = event;
}
private function deleteParticipantDrop(event:DragEvent):void
{
var selectedKitNum:String = memberRpt.selectedItem.KitNum;
var selectedName:String = memberRpt.selectedItem.ParticipantName;
var component:IUIComponent = IUIComponent(event.currentTarget);
dragComponent = component;
DragManager.acceptDragDrop(component);
isEditingParticipantInfo = false;
isDeletingParticipant = true;
deleteParticipantDropEvent = event;
event.stopImmediatePropagation(); // Added as per mrm
alert.confirm("Are you sure you want to delete this participant, Kit #" + memberRpt.selectedItem.KitNum + " (" +
memberRpt.selectedItem.ParticipantName + ") from the project? This cannot be reversed!! An email will be " +
"sent to notify this participant and you will receive a copy of it for your records.", confirmRemoveParticipant);
}
private function deleteParticipantDragExit(event:DragEvent):void
{
var component:IUIComponent = IUIComponent(event.currentTarget);
dragComponent = component;
DragManager.acceptDragDrop(component);
DragManager.showFeedback(DragManager.NONE);
}
private function confirmRemoveParticipant(event:CloseEvent):void
{
if (event.detail == Alert.YES)
{
deleteReason = DeleteParticipantTitleWindow(PopUpManager.createPopUp( this, DeleteParticipantTitleWindow , true));
dispatchEvent(deleteParticipantDropEvent); // Added as per mrm
PopUpManager.centerPopUp(deleteReason);
deleteReason.showCloseButton = true;
deleteReason.title = "Reason for removal from project";
deleteReason.addEventListener("close", cleanupRemoveParticipant);
deleteReason["cancelButton"].addEventListener("click", cleanupRemoveParticipant);
deleteReason["okButton"].addEventListener("click", finalizeDeleteParticipant);
isDeletingParticipant = false;
}
else
{
cleanupRemoveParticipant();
}
}
private function cleanupRemoveParticipant(event:Event = null):void
{
memberRpt.invalidateDisplayList();
memberRpt.executeBindings();
if (deleteReason != null)
{
PopUpManager.removePopUp(deleteReason);
deleteReason = null;
}
}
public function finalizeDeleteParticipant(event:Event):void
{
if (deleteReason.reason.text != null)
{
selectedReportItem = memberRpt.selectedItem;
selectedReportItemIndex = memberRpt.selectedIndex;
memberReportData.removeItemAt(selectedReportItemIndex);
}
else
{
alert.info("You must provide a reason for removing a participant from your project!!");
}
cleanupRemoveParticipant();
}
Thanks in advance for all helpful suggestions.
Have you tried running the validateNow() method on the ADG after the cancel event?
Here is some more information on the validateNow() method.
Why you need to know about validateNow...
I really do think this is what you're looking for! Please let us know if that is the case...
Try refreshing the data bindings on the datagrid using executeBindings and/or invalidateDisplayList in the enclosing control.
To be honest this sounds a bit like a bug. Have you posted this on flexcoders? The Adobe guys hang out on there (probably here too, but definitely there)
Hang on... just spotted that between the drop event and the cancel button of the popup there is an asynchronous web service call which appears to be handled by GetParticipantOrderInformation. Is that correct?
If yes, then have you tried offering a simpler dialog for Cancel before you do that? I wonder whether the combination of layers of events is causing a problem.
I didn't have any success with refreshing the data bindings on the datagrid via the executeBindings and invalidateDisplayList methods. I also didn't have any luck by showing the confirmation alert before making the webservice call. In fact, I discovered that making the webservice call was completely unnecessary and removed it. So now the code flows like this:
Drag/drop ADG row onto trash icon.
Display confirmation Alert box.
If user clicked "Cancel" button, redisplay the ADG.
But the same problem persists. I'll update the Code section with the latest code.
Here's an idea:
- Just before you create the alert window, stop the DragEvent
event.stopImmediatePropagation();
store the event so we can resume if the user clicks the Yes button
queuedEvent = event as DragEvent;
show the alert window
if the user clicks the yes button, resume the queued event
dispatchEvent(queuedEvent);
DragManager.showFeedback(DragManager.NONE);

Resources