Xamarin.Forms - Tooltip as menu - xamarin.forms

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 .

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.

Include tabbedpage in a Xamarin forms popup

I have a popup on click of a list view.
Is it possible to include Tabbed page in the popup with two tabs?
Not sure what effect is your want.Follow is one way :
async void OnAlertYesNoClicked (object sender, EventArgs e)
{
bool answer = await DisplayAlert ("Question?", "Would you like to play a game", "Yes", "No");
Debug.WriteLine ("Answer: " + answer);
}
If you want tabs without having a TabbedPage in Xamarin.Forms, you can use those custom tabs:
https://github.com/roubachof/Sharpnado.Presentation.Forms#pure-xamarinforms-tabs-no-renderers
These are simple xamarin forms views, you can put them everywhere you like.

Navigation issue in Xamarin Forms with same page type multiple time

Relatively new to Xamarin, hitting an issue with PushAsync and navigation I can't figure out.
I have a main navigation page, and then a "MyContentPage" that is responsible for rendering a dynamic list based on a supplied id. When the user clicks on a list item they go to a next (newed up) "MyContentPage" (same class) with a different id. Basically a recursive page hierarchy based on a local db.
Problem is that navigation seems to quickly get messed up in some way I can't work out. The pages get swapped around, or get lost. Navigating back to root, if I click back down again, it skips to a page that is further down etc.
So basically the one page apart from the main page (which has multiple navigationpages in tabs - though I only use one tab at this point) binds its controls to this function:
public async Task NavigateToContent(int contentId)
{
await ((Application.Current.MainPage) as TabbedPage)?.CurrentPage.Navigation.PushAsync(new MyContentPage(contentId));
}
The above is then used recursively. Ie. Similar controls bind to the same function until there are no further pages to click down to.
The MyContentPage constructor loads the model:
public MyContentPage(int id)
{
InitializeComponent();
_id = id;
BindingContext = viewModel = new ContentPageViewModel(id);
}
What is the issue here?
From what you mentioned in comments, the issue is caused by the navigation code called in the 'service' class. When you call the service method multiple times, it actually changes the current navigation stack in xamarin forms. Move the page navigation code from service class to viewmodel class.
Or try to put the page navigation source code into something like 'NavigationService' (one example is the one in https://learn.microsoft.com/en-us/xamarin/xamarin-forms/enterprise-application-patterns/ ) and inject this service into your view model class.
OK so this all turned out to be an issue with concurrency.
The original button click was like this:
private void Button_Clicked(object sender, EventArgs e)
{
Task.Run(async () => await (BindingContext as ContentPageViewModel).ExecuteNavCommand(sender));
}
But this resulted in a UI operation happening on a different task
The event handler can be declared as async
The correction is
private async void Button_Clicked(object sender, EventArgs e)
{
await viewModel.ExecuteNavCommand(sender);
}

How to configure ExpressionTextBox bindings / OwnerActivity when used in a dialog?

Our group is working on a Custom Activity Designer around our Email activity. It's a pretty straight forward designer, allow the user to enter settings / creds, but instead of cluttering the activity designer with all the settable options, we thought about putting some settings in a dialog window. (Which opens when you click the button beside the server address box).
Some of our email activity properties are InArguments so we are trying to make use of the ExpressionTextBox to display these values without much luck. The main problem is we aren't sure how to properly set up the binding and the OwnerActivity on the ExpressionTextBox. In the Activity Designer's xaml this is simply done by setting Expression=ModelItem.Property using a converter for the InArgument and setting the OwnerActivity=ModelItem, like this:
<view:ExpressionTextBox HintText="Enter a VB Expression" Expression="{Binding ModelItem.ServerAddress, ConverterParameter=In, Converter={StaticResource ArgumentToExpressionConverter}, Mode=TwoWay}" ExpressionType="{x:Type system:String}" OwnerActivity="{Binding ModelItem}" Margin="2" MaxLines="1" />
If anyone has any ideas on how we could accomplish this in a dialog, please advise.
Well, this is more a WPF\MVVM question than WF4, really.
When developing custom activities designers you just have to keep one thing in mind: any change made on designer\dialog should be reflected on ModelItem. Either through XAML binding expressions or through code on ModelItem.Properties property.
Now, when and how you do it, there are several answers to that but that's really an implementation detail and depends on how you want to do it.
Lets assume you're showing the dialog on button-beside-the-server-address-box click. And lets also assume you've access to dialog textboxes through their name. At that point, you've access to ModelItem so just set its properties as needed:
private void ButtonNextToServerAddressBox_OnClick(object sender, RoutedEventArgs e)
{
var dialog = new ServerAddressEditor();
var result = dialog.ShowDialog();
if (result ?? false)
{
ModelItem.Properties["Server"].SetValue(new InArgument<string>(dialog.ServerTextBox.Text));
ModelItem.Properties["Port"].SetValue(new InArgument<string>(dialog.PortTextBox.Text));
// ... set all other properties
}
}
Now, if you are using any other pattern, or you want pure MVVM, it can be a little more tricky because of how ModelItem works. But this is a totally fine approach.
I resolved this by creating a property in the dialog's ViewModel to hold the Activity Designer's ModelItem.
public ModelItem OwnerActivity {
get { return _OwnerActivity; }
set { _OwnerActivity = value; }
}
vm.OwnerActivity = this.DataContext.ModelItem;
I then set the Xaml for the Expression Text Box in my dialog to binding to this:
<view:ExpressionTextBox HintText="Enter a VB Expression" Expression="
{Binding Path=OwnerActivity.ServerAddress, ConverterParameter=In, Converter=
{StaticResource ArgumentToExpressionConverter}, Mode=TwoWay}" ExpressionType="
{x:Type system:String}" OwnerActivity="{Binding OwnerActivity}" Margin="2"
MaxLines="1" />
Because I'm now binding directly to the ModelItem from the Activity Designer, any change made to the ModelItem property from the dialog is ALWAYS committed, even if you choose to Cancel from the dialog. To wire up the Ok/Cancel buttons so they work accordingly, I did the following in the dialog:
// declare a ModelEditingScope to make changes transactional
private ModelEditingScope _editScope;
// add this to the constructor of the dialog to begin transactional edits on the ModelItem
_editScope = editorViewModel.OwnerActivity.BeginEdit();
// ok & cancel button click event to commit or revert the changes.
private void OK_Click(object sender, RoutedEventArgs e)
{
_editScope.Complete();
this.DialogResult = DialogResult.OK;
this.Close();
}
private void Cancel_Click(object sender, RoutedEventArgs e)
{
_editScope.Revert();
this.DialogResult = DialogResult.Cancel;
this.Close()
}

Is there a way to disable the background on the DisplayActionSheet using 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";
}

Resources