Include a text field inside an alert dialog - xaml

When a user clicks an "Add" button, I would like an alert dialog to be shown to the user, allowing them to enter in a store number into an entry field. I have researched into this but have not found any clear solution regarding this for Xamarin.Forms.
View my code below on what I have tried. I added an entry field in my Xaml and made it invisible. Then I added a tap gesture so that once my FAB is tapped, the entry field now becomes visible in the alert dialog. This does not work though.
Xaml:
<Image
Grid.Column="0" Grid.Row="0"
Source="add.png"
HorizontalOptions="EndAndExpand"
VerticalOptions="EndAndExpand"
Margin="0,0,30,30"
HeightRequest="45"
WidthRequest="45">
<Image.GestureRecognizers>
<TapGestureRecognizer
Command="{Binding clickNewCard}"
CommandParameter="3"
Tapped="TapGestureRecognizer_Tapped"/>
</Image.GestureRecognizers>
</Image>
<Entry x:Name="StoreField" InputTransparent="True" IsVisible="False"/>
Xaml.cs
private async void TapGestureRecognizer_Tapped(object sender, EventArgs e)
{
if (e != null)
{
StoreField.IsVisible = true;
await DisplayAlert("Enter Store Number", StoreField.Text, "Add");
}
}

As #Cole_Xia mentioned there are a couple of NuGet packages you can use to create custom Alert dialogs with Xamarin Forms for example:
1.- https://github.com/rotorgames/Rg.Plugins.Popup
2.- https://github.com/michaeled/FormsPopup
3.- https://github.com/aritchie/userdialogs
I have tried Forms Popup and it definitely works.

Related

How to make my contentview in the center of the screen, above my grid?

Right now my ContentView (acts like a pop up window) stacks on top of my grid, see code below:
<StackLayout>
<ContentView>...</>
<StackLayout>
<Grid>...</>
</StackLayout>
</StackLayout>
The grid is a form, so when I press a button in my form, I want a new pop up window (the contentview) to be above the grid, in the center of the screen. The idea is that you can still see some of the form behind the contentview.
How do I put contentview in the front and not stacked on top of my grid?
Here's my contentview
<ContentView x:Name="popupAddDetails" AbsoluteLayout.LayoutBounds="0,0,1,1"
VerticalOptions="Center" HorizontalOptions="Center">
PS: I'm new to this environment and still learning. Thankful for any help
I tried to make a stacklayout above my first stacklayout with my grid in it and then set vertical and horizontal attributes in my contentview to center. But it just stacked upon my inner stacklayout.
You could simply use Xamarin Community Toolkit Popup to make it.
First of all, add a new Nuget named Xamarin.CommunityToolkit to your project.
Then, create a popup page which will be shown when you click a button. This is much similar to creating a ContentPage.
<?xml version="1.0" encoding="UTF-8" ?>
<xct:Popup
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="ContentDemo.CommunityPopupPage"
xmlns:xct="clr- namespace:Xamarin.CommunityToolkit.UI.Views;assembly=Xamarin.CommunityToolkit">
<StackLayout WidthRequest="200" HeightRequest="200"BackgroundColor="Red"HorizontalOptions="Center" VerticalOptions="Center">
<Label Text="hello"/>
</StackLayout>
</xct:Popup>
The .cs may be something like this:
using Xamarin.CommunityToolkit.UI.Views;
namespace ContentDemo
{
public partial class CommunityPopupPage : Popup
{
public CommunityPopupPage()
{
this.Size = new Size(300, 300); //you could change the size of the popup
InitializeComponent();
}
}
}
Finally, you may want to open this popup page via a button clicked event. Don't forget to add this line:
using Xamarin.CommunityToolkit.Extensions;
void Button_Clicked(System.Object sender, System.EventArgs e)
{
App.Current.MainPage.Navigation.ShowPopup(new CommunityPopupPage());
}
I created this demo and it worked well. You could refer to Xamarin Community Toolkit Popup and Getting Started with the Xamarin Community Toolkit for more information.
====================update =====
2.Using outer layout as a Grid instead of a StackLayout might be an alternative. Just something like this:
<Grid x:Name="outerGrid" VerticalOptions="Center" > //this grid includes the grid and the contentview to be shown
<Grid x:Name="innerGrid">
<Grid.RowDefinitions>
...
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
...
</Grid.ColumnDefinitions>
</Grid>
<ContentView>
... // design the content
<ContentView />
</Grid>
In this way, the ContentView will be placed above the innerGrid. But I prefer using a popup page.
Hope it works for you.

Cant reselect previous selected item in CollectionView in Xamarin.Forms on iOS

TLDR
CollectionView broken in iOS, cant find any solution online. Weak Google fu or just not seeing the answer.
The problem
I have a CollectionView (Lets call it A) that contains items that are only strings values for filtering on a different CollectionView (Lets call it B) containing results from selecting a filter option in CollectionView A.
I have 3 different filtering options: "All", "Close to you", "Online" in CollectionView A.
The problem is that on iOS when I select for example filter option "All" in CollectionView A for the first time on the page it does the filtering and shows the results in CollectionView B, when I choose for example filtering option "Online" in CollectionView A it filters and shows the results in CollectionView B.
But when I choose filtering option "All" for the second time in CollectionView A its not responding, no event triggers, no commands runs and its not disabled. It is only showing, but I cant do anything with it.
Expected result
Can reselect the previous item on iOS, in Android no problem.
Actual result
Cant reselect previous item on iOS, need to back to previous page in stack and then navigate back to page to reset filtering.
The xaml markup
This is the xaml markup for the CollectionView A as explained above, the only holding string values to filter on.
<CollectionView ItemsSource="{Binding FilterLocations}"
Grid.Row="2"
SelectedItem="{Binding SelectedFilterLocation}"
SelectionMode="Single"
HeightRequest="50">
<CollectionView.ItemsLayout>
<LinearItemsLayout Orientation="Horizontal"
ItemSpacing="10" />
</CollectionView.ItemsLayout>
<CollectionView.ItemTemplate>
<DataTemplate x:DataType="x:String">
<StackLayout xct:TouchEffect.NativeAnimation="True">
<Frame BorderColor="{StaticResource BorderColor}"
x:Name="subCategoryFrame"
Padding="14, 10">
<Label Text="{Binding .}"
x:Name="subCategoryName"
FontFamily="{StaticResource FontPoppinsLight}"
TextColor="{StaticResource PrimaryAlt}" />
</Frame>
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
<CollectionView.Header>
<BoxView WidthRequest="0"
HeightRequest="1"
BackgroundColor="Transparent" />
</CollectionView.Header>
<CollectionView.Footer>
<BoxView WidthRequest="{StaticResource NormalSpacingDouble}"
HeightRequest="1"
BackgroundColor="Transparent" />
</CollectionView.Footer>
</CollectionView>
The CollectionView A is in SelectionMode:Single, and the SelectedItem is bound to a ICommand on its bound ViewModel. And in the ViewModel the selection of a item in CollectionView A will trigger a filtering in CollectionView B
What I done so far
I set up so if a item in the CollectionView A is disabled it become Red, but it dont become Red.
I have tried to add a event in the Code behind, to try and set SelectedItem to null, but that back fired and just made all events go twice, one for selecting the item on the screen and second for altering the SelectedItem in the code behind.
Code:
private void CollectionView_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (Device.RuntimePlatform == Device.iOS)
{
var collectionView = sender as CollectionView;
if (collectionView == null) return;
if (collectionView.SelectedItem != null)
{
collectionView.SelectedItem = null;
}
}
}
(I know it is a big no no to do logic stuff in the Code Behind that is not design logic, but I need to get this solved or have a quick and dirty fix because of time pressure.)
Sorry for the wall of text
Use a TapGestureRecognizer.
Below, MyItemCommand is defined in MyViewModel and {Binding .} refers to the item selected in ItemsSource.
<DataTemplate ...>
<StackLayout>
<StackLayout.GestureRecognizers>
<TapGestureRecognizer Command="{Binding Source={RelativeSource AncestorType={x:Type vm:MyViewModel}},
Path=MyItemCommand}"
CommandParameter="{Binding .}"/>
</StackLayout.GestureRecognizers>
...
First Bind the collection view into the event selectionchange.
<CollectionView ItemsSource="{Binding FilterLocations}"
Grid.Row="2"
SelectedItem="{Binding SelectedFilterLocation}"
SelectionMode="Single"
HeightRequest="50"
SelectionChanged="collection_SelectionChanged">
then inside the event put selected item to null.
private async void collection_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
try
{
var Sender = sender as CollectionView;
if (Sender.SelectedItem == null)
return;
Sender.SelectedItem = null;
}
catch (Exception ex)
{
}
}

WebView GestureRecognition not working in Xamarin forms

I have a webview When this webview is tapped I need to make visible of a button the problem Is Gesture recognition is not working
my Xaml
<customRenderer:CustomWebView Uri="{Binding SelectedJournal.Uri}" VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand" x:Name="CustomWebView" AbsoluteLayout.LayoutBounds="0,50,1,1" AbsoluteLayout.LayoutFlags="SizeProportional" >
<customRenderer:CustomWebView.GestureRecognizers>
<TapGestureRecognizer Tapped="TapGestureRecognizer_OnTapped2" NumberOfTapsRequired="1" ></TapGestureRecognizer>
</customRenderer:CustomWebView.GestureRecognizers>
</customRenderer:CustomWebView>
<customRenderer:NavigationImageButton ItemTapped="FullScreenOnTapped" Source="full.jpg" AbsoluteLayout.LayoutBounds="0.5,1,-1,-1" AbsoluteLayout.LayoutFlags="PositionProportional" HeightRequest="60" WidthRequest="60" x:Name="FullScreenBtn" IsVisible="False" >
In the code behind i called like this
private void TapGestureRecognizer_OnTapped2(object sender, EventArgs e)
{
FullScreenBtn.IsVisible = true;
}
This should work but this is not working
also my custom rendering web view class
public class CustomWebView : WebView
{
public static readonly BindableProperty UriProperty = BindableProperty.Create<CustomWebView, string>(p => p.Uri, default(string));
public string Uri
{
get { return (string)GetValue(UriProperty); }
set { SetValue(UriProperty, value); }
}
}
How to Achive this
Instead of using the gesture recognizer on your webview, you can use the 'Focused' event of your view to display your button. You can do something like this:
var wb = new WebView
{
HorizontalOptions = LayoutOptions.FillAndExpand,
VerticalOptions = LayoutOptions.FillAndExpand,
Source = "https://stackoverflow.com/questions/56320611/webview-gesturerecognition-not-working-in-xamarin-forms",
};
wb.Focused += (s, e) =>
{
//Handle your logic here!
wb.Unfocus();
};
Here, Unfocus() is used if you wish to implement your logic everytime the webview is tapped.
A WebView is used in my application to display formatted text. Tapping the text should open an editor to modify the text. The suggested Focused event works on Android but not iOS (as of iOS 15.0 simulator at least). I found the solution in the comments for https://bugzilla.xamarin.com/42/42596/bug.html#c4. Adding an empty label to the xaml as a transparent overlay of the WebView registers the tap event and enables the gesture recogniser.
<Grid HeightRequest="132">
<WebView Grid.Column="0" Grid.Row="0"
Source="{Binding FormattedDescription, Converter={tr:StringToWebViewSourceConverter}}"
HeightRequest="132"/>
<Label Grid.Column="0" Grid.Row="0" Text=""
HeightRequest="132" HorizontalOptions="Fill">
<Label.GestureRecognizers>
<TapGestureRecognizer Command="{Binding EditDescriptionCommand}"/>
</Label.GestureRecognizers>
</Label>
</Grid>

Binding shared flyout to 2 controls in Listview's DataTemplate in UWP Windows 10

I've got a shared Flyout defined in my <Page.Resources> as follows:
<Flyout x:Name="InfoFlyout" Opened="{Binding IsOpen,
ElementName=MyListView, Mode=TwoWay}">
<Grid>
<Button Foreground="White" Margin="5">
<StackPanel Orientation="Horizontal">
<TextBlock Text="Help"/>
</StackPanel>
</Button>
</Grid>
</Flyout>
But I get An object reference not set error when compiling, so I used the code from this article (Using Windows 8.1 Flyout control with MVVM) instead.
This seems to circumvent the problem I was having with the above code. Now my shared Flyout code looks like this:
<Flyout x:Name="InfoFlyout"
helpers:FlyoutHelpers.Parent="{Binding ElementName=MyListView}"
helpers:FlyoutHelpers.IsOpen="{Binding IsOpen, Mode=TwoWay}">
<Grid>
<Button Foreground="White" Margin="5">
<StackPanel Orientation="Horizontal">
<TextBlock Text="Help"/>
</StackPanel>
</Button>
</Grid>
</Flyout>
My ListView control (i.e. x:Name="MyListView") is binded to the page's ViewModel i.e. MainPageViewModel. The IsOpen property is defined in the MainViewModel.
Now in my ListView DataTemplate, I want my Flyout to open when I press and hold the ListViewItem or when pressing a button within the ListViewItem:
<DataTemplate>
<Grid FlyoutBase.AttachedFlyout="{StaticResource InfoFlyout}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Image Grid.Column="0" Source={Binding MyImage} />
<Grid Grid.Column="1" Margin="5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Button Width="30" Height="30"
Flyout="{StaticResource InfoFlyout}"
content="i">
</Button>
</Grid>
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Holding">
<actions:OpenFlyoutAction />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</Grid>
</DataTemplate>
As you can see, I've got the Flyout "attached" to the Grid via:
FlyoutBase.AttachedFlyout="{StaticResource InfoFlyout}"
and I've got the same Flyout attached to the button within the ListViewItem itself via:
Flyout="{StaticResource InfoFlyout}"
I've put breakpoints on both my setter and getter for the IsOpen property and when page gets loaded, it does go into the getter but whenever I open or close my Flyout either via Holding or by pressing the 'i' button, it doesn't trigger the method below and therefore it doesn't change the IsOpen property.
private static void OnIsOpenPropertyChanged(DependencyObject d,
DependencyPropertyChangedEventArgs e) as defined in the FlyoutHelper class.
The reason I've set my ElementName to MyListView is that I want all my ListViewItem to be binded to the one property i.e. IsOpen as I need to detect whenever a flyout menu is opened irrelevant of which ListViewItem it belongs to.
How can I achieve or resolve this?
UPDATE - 1
The problem of accessing the shared menu has been resolved by using the following:
<Flyout x:Name="InfoFlyout"
helpers:FlyoutHelpers.Parent="{Binding ElementName=MyListView}"
helpers:FlyoutHelpers.IsOpen="{Binding IsOpen, Mode=TwoWay}">
and setting the button to
<Button Width="30" Height="30"
Command="{Binding InformationCommand}"
CommandParameter="{Binding}"
Flyout="{StaticResource InfoFlyout}">
Which is fine and as #ElvisXia mentioned, you can comment out the code in the OnIsOpenPropertyChanged as the positioning is already determined by the button located inside my ListViewItem.
There is however one outstanding problem. A small one btw, but nice if it can be solved. The shared flyout which is attached to the grid itself in the DataTemplate i.e.
<DataTemplate>
<Grid FlyoutBase.AttachedFlyout="{StaticResource InfoFlyout}">
It is being positioning based on the ListViewItem which technically is correct as I'm calling a different piece of code for that one i.e.
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Holding">
<actions:OpenFlyoutAction />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
And the OpenFlyoutAction is defined as follows:
public class OpenFlyoutAction : DependencyObject, IAction
{
public object Execute(object sender, object parameter)
{
FrameworkElement senderElement = sender as FrameworkElement;
FlyoutBase flyoutBase = FlyoutBase.GetAttachedFlyout(senderElement);
flyoutBase.ShowAt(senderElement);
return null;
}
}
Can I somehow stop using the OpenFlyoutAction and use the same code as provided in the article to open my Flyout wherever the user is holding his/her finger on the relevant ListViewItem rather than on top or below the actual ListViewItem?
I understand it's a little bit side track from the original issue which was to share a Flyout by to controls but may as well finish it as it is somehow relevant to the issue.
Thanks.
Change the type of Parent from Button to ListView. To open flyout in particular X,Y position is not possible in WP. You can choose PopUp control instead. Here is a link which i got open the pop up in tapped position. You can use VisualTreeHelper to get PopUp control of tapped ListViewItem
By Using Windows 8.1 Flyout control with MVVM , the author use parent to control where the flyout shows up.
So the author have codes like below(FlyoutHelpers.cs):
private static void OnIsOpenPropertyChanged(DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
var flyout = d as Flyout;
var parent = (ListView)d.GetValue(ParentProperty);
if (flyout != null && parent != null)
{
var newValue = (bool)e.NewValue;
if (newValue)
flyout.ShowAt(parent);
else
flyout.Hide();
}
}
He use flyout.ShowAt(parent) to let flyout show at parent element. But in your codes you have binded the flyout to the button using:
<Button Width="30" Height="30"
Flyout="{StaticResource InfoFlyout}" content="i">
</Button>
So it is not necessary to let it show at it's parent any more. To fix the problem, you can comment out the statements like below:
private static void OnIsOpenPropertyChanged(DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
//var flyout = d as Flyout;
//var parent = (ListView)d.GetValue(ParentProperty);
//if (flyout != null && parent != null)
//{
// var newValue = (bool)e.NewValue;
// if (newValue)
// flyout.ShowAt(parent);
// else
// flyout.Hide();
//}
}
Then you will see the flyout shows at the right place.

How to change open animation of flyout menu

I have a menu flyout which is opened on ListView Items. The default open animation is quite slow is there a way to change the default animation of flyout menu?
EDIT
I am using following dependency property for showing context menu on list view item it works fine but when context menu is shown it squeezes the the whole view a bit. I do not want to squeeze the page when context menu is opened.
public class OpenMenuFlyoutAction:DependencyObject,IAction
{
public object Execute(object sender, object parameter)
{
if (!Global.IsDisabledShowContextMenuOnListView)
{
FrameworkElement senderElement = sender as FrameworkElement;
FlyoutBase flyoutBase = FlyoutBase.GetAttachedFlyout(senderElement);
flyoutBase.ShowAt(senderElement);
}
return null;
}
}
List Item data template
<DataTemplate x:Key="MemberListItemDataTemplate">
<Grid Width="{Binding ElementName=searchView,Path=ActualWidth}" Background="{Binding ItemBackground}"
Margin="0,0,0,20" Height="auto">
<i:Interaction.Behaviors>
<icore:EventTriggerBehavior EventName="Holding">
<helpers:OpenMenuFlyoutAction />
</icore:EventTriggerBehavior>
</i:Interaction.Behaviors>
<FlyoutBase.AttachedFlyout>
<MenuFlyout>
<MenuFlyoutItem Text="share" RequestedTheme="Dark" Command="{Binding ElementName=pageMyProfile, Path=DataContext.ShareMemberDetails}" CommandParameter="{Binding item99}" />
<MenuFlyoutItem Text="reomve from members" RequestedTheme="Dark" Command="{Binding ElementName=pageMyProfile, Path=DataContext.RemoveMember}" CommandParameter="{Binding item99}" />
</MenuFlyout>
</FlyoutBase.AttachedFlyout>
...
</DataTemplate>
Yeah, you need to create a new Style targeting MenuFlyoutPresenter in your resources
<Style TargetType="MenuFlyoutPresenter">
If you copy it from Program Files (x86)\Windows Phone Kits\8.1\Include\abi\Xaml\Design\generic.xaml you'll notice there are already some storyboards inside for various visual states that you need to change in order to get a different animation.
I wrote about something very similar in my blog post MenuFlyout flip animation on Windows Phone WinRT