How to do a simple InputBox dialog?

ThomasFlemmingThomasFlemming ✭✭DEMember ✭✭

I'm struggeling with a simple thing.

How can I display a prompt in a dialog box, thats waits for the user to input text or click a button, and then returns a string containing the contents of the text box with Xamarin.Forms?

With Android or WP8 I was able to create a popup with some controls, but I didn't find any similar in Xamarin.Forms.

Thx, Tom

Posts

  • AlessandroCaliaroAlessandroCaliaro ✭✭✭✭✭ ITMember ✭✭✭✭✭

    @ThomasFlemming I've never tried but I think these can help you (dialog...) https://github.com/aritchie/acr-xamarin-forms

  • FredyWengerFredyWenger ✭✭✭✭✭ CHInsider ✭✭✭✭✭

    @ThomasFlemming:
    You can take the PopUp-Control from XLabs.
    I have posted a description, how to use it - see link below:

    forums.xamarin.com/discussion/33587/how-to-use-a-listview-in-a-scrollview-with-xlabs-popup-control#latest

  • ThomasFlemmingThomasFlemming ✭✭ DEMember ✭✭

    thx guys, but thats all pure overkill for a simple InputBox.

    In XamariniOS it would be not more then:

            UIAlertView alert = new UIAlertView();
            alert.Title = "Title";
            alert.AddButton("OK");
            alert.AddButton("Cancel");
            alert.Message = "Please Enter a Value.";
            alert.AlertViewStyle = UIAlertViewStyle.PlainTextInput;
            alert.Clicked += (object s, UIButtonEventArgs ev) =>
            {
                if(ev.ButtonIndex ==0)
                {
                    string input = alert.GetTextField(0).Text;
                }
            };
            alert.Show();
    

    Nothing similar in Xamarin.Forms ??

  • I know I'm late. But if someone is searching for the most simple way to show simple dialogs in Xamarin.Forms: On any Page you can call the following methods:
    public Task<string> DisplayActionSheet(string title, string cancel, string destruction, params string[] buttons); public Task DisplayAlert(string title, string message, string cancel); public Task<bool> DisplayAlert(string title, string message, string accept, string cancel);
    For more details: https://developer.xamarin.com/guides/xamarin-forms/user-interface/navigation/pop-ups/

  • BhaurajBiradarBhaurajBiradar ✭✭ USUniversity ✭✭

    @ThomasFlemming - Thank you, It worked for me :)

  • MetaKMetaK ESMember
    edited June 2016

    @ChristophRosenberger.6105 - What does this have to do with the discussion? Not Input Dialogs on ur answer...

    @ThomasFlemming - U are awesome!
    But in the end , I ended up doing a layout on the xaml as if it were a displayalert

  • BrianAustinBrianAustin ✭✭ AUMember ✭✭

    Peter thinks he's late lol!
    @ThomasFlemming put the stacklayout in an absolutelayout and position it.
    background image for the absolute layout looks like this. (png with 70% transparent areas)

  • BrianAustinBrianAustin ✭✭ AUMember ✭✭
    edited August 2016
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using Xamarin.Forms;
    
    namespace acrossWords.Pages
    {
        public class ConfirmationPage : ContentPage
        {
            public bool result;
            Image ConfirmButton;
            Image CancelButton;
            Label QuestionLabel;
            public TaskCompletionSource<string> tcs;
    
            public ConfirmationPage()
            {
                BackgroundImage = "ConfirmationPage.png";
                QuestionLabel = new Label
                {
                    FontFamily = "BookAntiqua",
                    //Margin = Margin = new Thickness(0, 5, 0, 0),
                    FontSize = Device.GetNamedSize(NamedSize.Large, typeof(Label)) * 1.2,
                    Text = "",
                    TextColor = Xamarin.Forms.Color.White,
                    //HorizontalOptions = LayoutOptions.StartAndExpand,
                    //VerticalTextAlignment = Xamarin.Forms.TextAlignment.Center
                };
                ConfirmButton = new Image { Source = "ConfirmButton.png"};
                CancelButton = new Image { Source = "CancelButton.png"};
    
                AbsoluteLayout ConfirmLayout = new AbsoluteLayout { };
                Content = ConfirmLayout;
                AbsoluteLayout.SetLayoutFlags(ConfirmLayout, Xamarin.Forms.AbsoluteLayoutFlags.All);
                AbsoluteLayout.SetLayoutBounds(ConfirmLayout, new Xamarin.Forms.Rectangle(0, 0, 1, 1));
                ConfirmLayout.Children.Add(QuestionLabel, new Rectangle(0.3, 0.3, AbsoluteLayout.AutoSize, AbsoluteLayout.AutoSize), AbsoluteLayoutFlags.PositionProportional);
                ConfirmLayout.Children.Add(ConfirmButton, new Rectangle(0.2, 0.7, AbsoluteLayout.AutoSize, AbsoluteLayout.AutoSize), AbsoluteLayoutFlags.PositionProportional);
                ConfirmLayout.Children.Add(CancelButton, new Rectangle(0.8, 0.7, AbsoluteLayout.AutoSize, AbsoluteLayout.AutoSize), AbsoluteLayoutFlags.PositionProportional);
    
                ConfirmButton.GestureRecognizers.Add(new TapGestureRecognizer
                {
                    NumberOfTapsRequired = 1,
                    Command = new Command(OnConfirm)
                });
                CancelButton.GestureRecognizers.Add(new TapGestureRecognizer
                {
                    NumberOfTapsRequired = 1,
                    Command = new Command(OnCancel)
                });
            }
            public void clearTCS()
            {
                tcs = new TaskCompletionSource<string>();
            }
            public  async Task ShowConfirmation(string question)
            {
                QuestionLabel.Text = question;
                await App.PushPage(this);
            }
            void OnConfirm()
            {
                tcs.SetResult("true");
                App.PopPage();
            }
            void OnCancel()
            {
                tcs.SetResult("false");
                App.PopPage();
            }
        }
    }
    
  • BrianAustinBrianAustin ✭✭ AUMember ✭✭
    edited August 2016

    use it like this..

          public Task<string> ConfirmResignGame()
                {
                    App.confirmPage.clearTCS();
                    App.confirmPage.ShowConfirmation("Resign this game?");
                    return App.confirmPage.tcs.Task;
    
    
    
                }
    

    example

                FancyButton.CancelButton.GestureRecognizers.Add(new TapGestureRecognizer
                {
                    NumberOfTapsRequired = 1,
                    Command = new Command(o =>
                       Device.BeginInvokeOnMainThread(async () => {
                           App.mainMenuPage.activityIndicator.IsVisible = true;
                           App.mainMenuPage.activityIndicator.IsRunning = true;
    
                           if (TurnsRemaining > 0)
                           {
    **                           string confirm = await App.gamePage.ConfirmResignGame();
                               if (confirm.Equals("true"))
                               {
                                   App.debug("MainMenugame: Gamepage ConfirmResignGame came back true");
                               }
                               else
                               {
                                   App.debug("MainMenugame: Gamepage ConfirmResignGame came back false");
                               }**
                           }
                           App.mainMenuPage.activityIndicator.IsVisible = false;
                           App.mainMenuPage.activityIndicator.IsRunning = false;
                       })
                    )
                });
    
  • BrianAustinBrianAustin ✭✭ AUMember ✭✭

    result

  • SivaShankarArumugamSivaShankarArumugam ✭✭✭ USMember ✭✭✭

    @ThomasFlemming popup is working on button click. when i try to open popup using timer not working.

    My requirment - successfull login (login page is poped out), after 10 mins I want to get the password from user and validate.

    I have written the code in login method of my page

    TimeSpan ts = new TimeSpan(0, 10, 0); Device.StartTimer(ts, CheckUserSession); private bool CheckUserSession() { bool IsUsersession = false; string myinput = await InputBox(this.Navigation); //DisplayAlert("Session", "User session over...", "Ok"); return IsUsersession; }

    Need help..

  • abeketovabeketov ✭✭ USMember ✭✭

    @SivaShankarArumugam try to use

    Device.BeginInvokeOnMainThread(() =>{ string myinput = await InputBox(this.Navigation); });

  • GeetSGeetS ✭✭✭ USMember ✭✭✭

    @ThomasFlemming said:
    Here is a solution without any nugets. In case its useful for somebody. It doesn't look so nice, because it opens fullscreen. Would be nicer, if the calling page would still be visible darkened underneath, but I don't now how to get this one.

     public static Task<string> InputBox(INavigation navigation) 
       {
                // wait in this proc, until user did his input 
                var tcs = new TaskCompletionSource<string>();
    
                var lblTitle = new Label { Text = "Title", HorizontalOptions = LayoutOptions.Center, FontAttributes = FontAttributes.Bold};
                var lblMessage = new Label { Text = "Enter new text:" };
                var txtInput = new Entry { Text = "" };
    
                var btnOk = new Button
                {
                    Text = "Ok",
                    WidthRequest =100,
                    BackgroundColor = Color.FromRgb(0.8, 0.8, 0.8),
                };
                btnOk.Clicked += async (s, e) =>
                {
                    // close page
                    var result = txtInput.Text;
                    await navigation.PopModalAsync();
                    // pass result
                    tcs.SetResult(result);
                };
    
                var btnCancel = new Button
                {
                    Text = "Cancel",
                    WidthRequest = 100,
                    BackgroundColor = Color.FromRgb(0.8, 0.8, 0.8)
                };
                btnCancel.Clicked += async  (s, e) =>
                {
                    // close page
                    await navigation.PopModalAsync();
                    // pass empty result
                    tcs.SetResult(null);
                };
    
                var slButtons = new StackLayout 
                {
                    Orientation = StackOrientation.Horizontal ,
                    Children = { btnOk, btnCancel},
                };
    
                var layout = new StackLayout
                {
                    Padding =new Thickness (0,40,0,0),
                    VerticalOptions= LayoutOptions .StartAndExpand   ,
                    HorizontalOptions =LayoutOptions.CenterAndExpand  ,
                    Orientation = StackOrientation.Vertical,
                    Children = { lblTitle, lblMessage, txtInput, slButtons },
                };
    
                // create and show page
                var page = new ContentPage();
                page.Content = layout;
                navigation.PushModalAsync(page);
                // open keyboard
                txtInput.Focus();
    
                // code is waiting her, until result is passed with tcs.SetResult() in btn-Clicked
                // then proc returns the result
                return tcs.Task;
            }  
    

    Call it from any page with:
    string myinput = await InputBox(this.Navigation);

    Awesome. B)

  • rocharocharocharocha ✭✭ USMember ✭✭

    @ThomasFlemming said:
    Here is a solution without any nugets. In case its useful for somebody. It doesn't look so nice, because it opens fullscreen. Would be nicer, if the calling page would still be visible darkened underneath, but I don't now how to get this one.

     public static Task<string> InputBox(INavigation navigation) 
       {
                // wait in this proc, until user did his input 
                var tcs = new TaskCompletionSource<string>();
    
                var lblTitle = new Label { Text = "Title", HorizontalOptions = LayoutOptions.Center, FontAttributes = FontAttributes.Bold};
                var lblMessage = new Label { Text = "Enter new text:" };
                var txtInput = new Entry { Text = "" };
    
                var btnOk = new Button
                {
                    Text = "Ok",
                    WidthRequest =100,
                    BackgroundColor = Color.FromRgb(0.8, 0.8, 0.8),
                };
                btnOk.Clicked += async (s, e) =>
                {
                    // close page
                    var result = txtInput.Text;
                    await navigation.PopModalAsync();
                    // pass result
                    tcs.SetResult(result);
                };
    
                var btnCancel = new Button
                {
                    Text = "Cancel",
                    WidthRequest = 100,
                    BackgroundColor = Color.FromRgb(0.8, 0.8, 0.8)
                };
                btnCancel.Clicked += async  (s, e) =>
                {
                    // close page
                    await navigation.PopModalAsync();
                    // pass empty result
                    tcs.SetResult(null);
                };
    
                var slButtons = new StackLayout 
                {
                    Orientation = StackOrientation.Horizontal ,
                    Children = { btnOk, btnCancel},
                };
    
                var layout = new StackLayout
                {
                    Padding =new Thickness (0,40,0,0),
                    VerticalOptions= LayoutOptions .StartAndExpand   ,
                    HorizontalOptions =LayoutOptions.CenterAndExpand  ,
                    Orientation = StackOrientation.Vertical,
                    Children = { lblTitle, lblMessage, txtInput, slButtons },
                };
    
                // create and show page
                var page = new ContentPage();
                page.Content = layout;
                navigation.PushModalAsync(page);
                // open keyboard
                txtInput.Focus();
    
                // code is waiting her, until result is passed with tcs.SetResult() in btn-Clicked
                // then proc returns the result
                return tcs.Task;
            }  
    

    Call it from any page with:
    string myinput = await InputBox(this.Navigation);

    It should be on Xamarin.Forms. awesome!!! tksm

  • TheMaeggesTheMaegges ✭✭ DEMember ✭✭

    I have found another way to create a popup dialog from a ContentPage, though i haven't implemented iOS yet.
    However, I am gladly willing to share my current state. Please note that I will keep the code examples as short as possible, therefore reducing it to a simple loading and a text prompt dialog.

    Approach

    After having used the Acr.UserDialogs library for a while, i felt the need of having dialogs I could customize due to my personal requirements. Also I wanted to minimize the necessity of having to rely on plugins.
    Ideally such a dialog should be invoked with a simple call, for instance:

    Dialogs.ShowLoading();
    

    or

    string result = Dialogs.ShowPrompt();
    

    It is quite obvious that we will require a dependency service implementation for that to work.

    Shared Code Library

    We create a basic interface "IDialogs.cs":

    public interface IDialogs
    {
        bool IsDialogOpen();
        void ShowLoading(LoadingDialog dialog);
    
        void ShowPrompt(PromptDialog dialog);
    
        void HideDialog();
    }
    

    Next thing to do is to have a static dialog class, which can be called from every page where a dialog is needed.
    "Dialogs.cs":

    public static class Dialogs
    {
        private static IDialogs dialogService = DependencyService.Get<IDialogs>();
    
        public static bool IsDialogOpen()
        {
            return dialogService.IsDialogOpen();
        }
    
        public static void ShowLoading()
        {
            LoadingDialog dlg = new LoadingDialog();
            dialogService.ShowLoading(dlg);
        }
    
        public static Task<string> ShowPromptText()
        {
            TaskCompletionSource<string> dialogCompletion = new TaskCompletionSource<string>();
            PromptDialog dialog = new PromptDialog();
    
            dialog.Canceled += (object sender, object result) => { dialogService.HideDialog(); dialogCompletion.SetResult((string)result); };
            dialog.Confirmed += (object sender, object result) => { dialogService.HideDialog(); dialogCompletion.SetResult((string)result); };
            dialogService.ShowPrompt(dialog);
            return dialogCompletion.Task;
        }
    
        public static void HideDialog()
        {
            dialogService.HideDialog();
        }
    }
    

    You will notice, that we are using a TaskCompletionSource together with custom event handlers in the ShowPromptText method. This enables us to display the dialog and await the user pressing the okay or cancel button and consuming the returned result.

    As of now, the dialogs are as simple as they can get. I use some additional code for styling and themeing, but i will leave that out in order to keep this answer short and simple.

    The loading dialog:

    <ContentPage xmlns="http://xamarin.com/schemas/2014/forms" 
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="MyApp.Dialogs.LoadingDialog">
    <ContentPage.Content>
        <Grid BackgroundColor="#bb000000">
            <Grid.RowDefinitions>
                <RowDefinition Height="*"/>
                <RowDefinition Height="*"/>
                <RowDefinition Height="*"/>
            </Grid.RowDefinitions>
            <Frame BackgroundColor="Black" CornerRadius="15" Grid.Row="1" x:Name="ContentGrid" Margin="100,0,100,0">
            <Grid>
                <Grid.RowDefinitions>
                    <RowDefinition Height="2*"/>
                    <RowDefinition Height="*"/>
                </Grid.RowDefinitions>
                <ActivityIndicator Grid.Row="0" Color="White" IsRunning="True" HorizontalOptions="Center" VerticalOptions="Center"/>
                <Label x:Name="LoadingLabel" Text="Loading ..." VerticalOptions="End"  HorizontalOptions="Center" Grid.Row="1" TextColor="White" />
            </Grid>
            </Frame>
        </Grid>
    </ContentPage.Content>
    

    (no need to post xaml.cs code for this, since there is no interaction with a loading screen)

    The prompt dialog

    PromptDialog.Xaml:

    <ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:dialogs="clr-namespace:BetterUI.Dialogs"
             x:Class="MyApp.Dialogs.PromptDialog">
    <ContentPage.Content>
        <ScrollView>
        <Grid BackgroundColor="#bb000000">
                <Grid.RowDefinitions>
                    <RowDefinition Height="*"/>
                    <RowDefinition Height="Auto"/>
                    <RowDefinition Height="*"/>
                </Grid.RowDefinitions>
                <Frame x:Name="ContentGrid" Grid.Row="1" CornerRadius="15" BackgroundColor="White" Margin="50,0,50,0" Padding="0">
                    <Grid  Grid.Row="1" Margin="0">
                        <Grid.RowDefinitions>
                            <RowDefinition Height="Auto"/>
                            <RowDefinition Height="Auto"/>
                            <RowDefinition Height="Auto"/>
                            <RowDefinition Height="50"/>
                        </Grid.RowDefinitions>
                        <Grid x:Name="HeadingGrid" Padding="10, 5, 10, 5" Margin="-15,0,-15,0" Grid.Row="0" BackgroundColor="Black">
                            <Label x:Name="HeadingLabel" Text="Enter text" TextColor="White" Margin="20,0,20,0"/>
                        </Grid>
                        <Label l x:Name="DescriptionLabel" Text="Enter your text" Grid.Row="1" Margin="15,0,15,0"/>
                        <Entry x:Name="DialogResultText" Placeholder="Text" PlaceholderColor="LightGray" TextColor="Black" Grid.Row="2" Margin="15,0,15,0"/>
                        <Grid Grid.Row="3" ColumnSpacing="0">
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="*"/>
                                <ColumnDefinition Width="*"/>
                            </Grid.ColumnDefinitions>
                            <Button x:Name="CancelButton" Text="Cancel" Clicked="OnCancelClick" Grid.Column="0" CornerRadius="0"/>
                            <Button x:Name="ConfirmButton" Text="Okay" Clicked="OnOkayClick" Grid.Column="1" CornerRadius="0"/>
                        </Grid>
                    </Grid>
                </Frame>
            </Grid>
        </ScrollView>
    </ContentPage.Content>
    

    PromptDialog.xaml.cs:

    public partial class PromptDialog : ContentPage
    {
        public event EventHandler<object> Confirmed;
        public event EventHandler<object> Canceled;
    
        public PromptDialog()
        {
            InitializeComponent();
        }
        private void OnCancelClick(object sender, EventArgs e)
        {
            Canceled?.Invoke(this, string.Empty);
        }
    
        private void OnOkayClick(object sender, EventArgs e)
        {
            Confirmed?.Invoke(this, DialogResultText.Text);
        }
    }
    

    Android implementation

    First of all we will need the android implementation of our IDialogs interface created in the shared code earlier:

    [assembly: Dependency(typeof(DialogService))]
    namespace MyApp.Droid.Services
    {
    /// <summary>
    /// Handles displaying dialog items on screen
    /// </summary>
    public class DialogService : IDialogs
    {
        private static DialogFragment currentDialog;
    
        /// <summary>
        /// returns if a dialog is already open
        /// </summary>
        /// <returns></returns>
        public bool IsDialogOpen()
        {
            return (currentDialog != null && currentDialog.IsVisible);
        }
    
        /// <summary>
        /// Initialize Dialog Service with activity
        /// </summary>
        /// <param name="activity">activity</param>
        public static void Init(Activity activity)
        {
            Activity = activity;
        }
    
        public static Activity Activity { get; set; }
    
        /// <summary>
        /// Displays a loading dialog
        /// </summary>
        /// <param name="dialog">Instance of progress dialog (xamarin.forms)</param>
        public void ShowLoading(Dialogs.LoadingDialog dialog)
        {
            if (Activity == null)
                return;
    
            DialogFragment frag = dialog.CreateDialogFragment(Activity);
    
            frag.SetStyle(DialogFragmentStyle.NoTitle, Resource.Style.DialogFrame);
            frag.Show(Activity.FragmentManager, "dialog");
            currentDialog = frag;
        }
    
        /// <summary>
        /// Displays a prompt dialog
        /// </summary>
        /// <param name="dialog"></param>
        public void ShowPrompt(Dialogs.PromptDialog dialog)
        {
            if (Activity == null)
                return;
    
            DialogFragment frag = dialog.CreateDialogFragment(Activity);
    
            frag.SetStyle(DialogFragmentStyle.NoTitle, Resource.Style.DialogFrame);
            frag.Show(Activity.FragmentManager, "dialog");
            currentDialog = frag;
        }
    
        /// <summary>
        /// Hides loading dialog
        /// </summary>
        public void HideDialog()
        {
            if (Activity == null)
                return;
    
            if (currentDialog != null)
            {
                currentDialog.Dismiss();
                currentDialog = null;
            }
        }
    }
    }
    

    Note that you have to set the activity for the dialog service prior to calling the actual methods to show a dialog, so in your MainActivity.cs make sure to call

    DialogService.Init(this);
    

    after having initialized Xamarin.Forms.

    Finally, here comes some black magic:

    Usually, one would implement such a dialog in Android by putting a fragment container into the main layout and throwing a fragment inside. Unfortunately, due to using Xamarin.Forms, such a main layout isn't available by default.

    Even though Xamarin.Forms offers a view extension, which allows converting a ContentPage to a fragment (ContentPage.CreateFragment), we won't have any success of using this because it needs a target fragment container to be placed in.

    However, android provides us with a DialogFragment, which can be thrown at the screen without having the need of a defined fragment container.

    Unfortunately there isn't any out-of-the-box solution for creating a DialogFragment from Xamarin Forms. Good news is that this can be overcome with the power of using System.Reflection(;), so we create our own extension method and some modified versions of internal classes, xamarin.forms uses under the hood. In order to achieve this, I took the PageExtensions code from Xamarin.Forms in Xamarin.Platform.Android and modified it to create a DialogFragment from a ContentPage:

    public static class PageExtensions
    {
        public static DialogFragment CreateDialogFragment(this ContentPage view, Context context)
        {
            if (!Forms.IsInitialized)
                throw new InvalidOperationException("call Forms.Init() before this");
    
            // Get Platform constructor via reflection and call it to create new platform object
            Platform platform = (Platform)typeof(Platform).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[] { typeof(Context), typeof(bool) }, null)
                ?.Invoke(new object[] { context, true });
    
            // Set the page to the platform
            if (platform != null)
            {
                platform.GetType().GetMethod("SetPage", BindingFlags.NonPublic | BindingFlags.Instance)?.Invoke(platform, new object[] { view });
    
                // Finally get the view group
                ViewGroup vg = (Android.Views.ViewGroup)platform.GetType().GetMethod("GetViewGroup", BindingFlags.NonPublic | BindingFlags.Instance)?.Invoke(platform, null);
    
                return new EmbeddedDialogFragment(vg, platform);
            }
    
            return null;
        }
    
        public class DefaultApplication : Xamarin.Forms.Application
        {
        }
    
        class EmbeddedDialogFragment : DialogFragment
        {
            readonly ViewGroup _content;
            readonly Platform _platform;
            bool _disposed;
    
            public EmbeddedDialogFragment()
            {
            }
    
            public EmbeddedDialogFragment(ViewGroup content, Platform platform)
            {
                _content = content;
                _platform = platform;
            }
    
            public override global::Android.Views.View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
            {
                this.Dialog.Window.SetSoftInputMode(SoftInput.AdjustResize);
                return _content;
            }
    
            public override void OnDestroy()
            {
                this.Dialog?.Window.SetSoftInputMode(SoftInput.AdjustPan);
                base.OnDestroy();
            }
    
            protected override void Dispose(bool disposing)
            {
                if (_disposed)
                {
                    return;
                }
    
                _disposed = true;
    
                if (disposing)
                {
                    (_platform as IDisposable)?.Dispose();
                }
    
                base.Dispose(disposing);
            }
        }
    }
    

    Et voila, now whenever we use the calls from the "Approach"-Section of this post, a nice popup dialog containing our custom Xamarin.Forms ContentPage will show up.

    I have to apologize for not having an iOS implementation ready at the moment, but I assume that a similar approach can be implemented for iOS. When I have finished the iOS implementation, i will gladly add it to this answer.

  • TheMaeggesTheMaegges ✭✭ DEMember ✭✭
    edited August 2018

    Here is the iOS implementation of DialogService:

    [assembly: Dependency(typeof(DialogService))]
    namespace BetterUI.iOS.Services
    {
    public class DialogService : IDialogs
    {
        private UIViewController currentDialog;
        private UIWindow popupWindow = null;
    
        public void HideLoading()
        {
            if (currentDialog != null)
            {
                UIApplication.SharedApplication.KeyWindow.RootViewController.DismissModalViewController(false);
                currentDialog.Dispose();
                currentDialog = null;
            }
        }
    
        public bool IsDialogOpen()
        {
            return (currentDialog != null && currentDialog.IsBeingPresented);
        }
    
        public void ShowLoading(LoadingDialog dialog)
        {
            UIViewController dialogController = dialog.CreateViewController();
            ShowDialog(dialogController);
            currentDialog = dialogController;
    
        }
    
        public void ShowPrompt(PromptDialog dialog)
        {
            UIViewController dialogController = dialog.CreateViewController();
            ShowDialog(dialogController);
            currentDialog = dialogController;
        }
    
        private void ShowDialog(UIViewController dialogController)
        {
            var bounds = UIScreen.MainScreen.Bounds;
            dialogController.View.Frame = new CGRect(bounds.X, bounds.Y, bounds.Width, bounds.Height);
            dialogController.ProvidesPresentationContextTransitionStyle = true;
            dialogController.View.Alpha = new nfloat(0.8);
            dialogController.View.BackgroundColor = null;
            dialogController.ModalPresentationStyle = UIModalPresentationStyle.Custom;
            UIApplication.SharedApplication.KeyWindow.RootViewController.DefinesPresentationContext = true;
            UIApplication.SharedApplication.KeyWindow.RootViewController.ModalPresentationStyle =
                UIModalPresentationStyle.CurrentContext;
            UIApplication.SharedApplication.KeyWindow.RootViewController.PresentViewController(dialogController, false, null);
        }
    }
    }
    
  • Ashish_sharmaAshish_sharma ✭✭✭ INMember ✭✭✭

    ChristophRosenberger.6105:-

    DisplayActionSheet(string title, string cancel, string destruction, params string[] buttons)
    how to use this alert in contentview page instead of contetpage .

  • NMackayNMackay mod GBInsider, University mod

    We use Rg.Plugins for all this.

    • Handles popups for input
    • Busy popup
    • Alert
    • Activity Sheet
    • Toast notifications

    All wrapped into a dialog service so it's nice and easy to consume using the MVVM pattern.

  • wuselSuselwuselSusel ✭✭ Member ✭✭

    @TheMaegges
    Thank you for your solution!
    Do you happen to have an idea how to make the iOS dialog opaque? No matter how I do it, either the dialog and the background are transparent or both are opaque.

  • TheMaeggesTheMaegges ✭✭ DEMember ✭✭
    edited December 2018

    @wuselSusel said:
    @TheMaegges
    Thank you for your solution!
    Do you happen to have an idea how to make the iOS dialog opaque? No matter how I do it, either the dialog and the background are transparent or both are opaque.

    Actually yes, i got it running by changing the ShowDialog method to the following:

    private void ShowDialog(UIViewController dialogController)
    {
        var bounds = UIScreen.MainScreen.Bounds;
        dialogController.View.Frame = bounds;
        UIApplication.SharedApplication.KeyWindow.RootViewController.ModalPresentationStyle = UIModalPresentationStyle.CurrentContext;
        UIApplication.SharedApplication.KeyWindow.RootViewController.AddChildViewController(dialogController);
        UIApplication.SharedApplication.KeyWindow.RootViewController.View.Opaque = false;
        UIApplication.SharedApplication.KeyWindow.RootViewController.View.Layer.AllowsGroupOpacity = true;
        UIApplication.SharedApplication.KeyWindow.RootViewController.View.Layer.BackgroundColor = new CGColor(Color.White.ToCGColor(), 0.0f);
        UIApplication.SharedApplication.KeyWindow.RootViewController.View.BackgroundColor = UIColor.Clear;
        UIApplication.SharedApplication.KeyWindow.RootViewController.View.AddSubview(dialogController.View);
        dialogController.ModalPresentationStyle = UIModalPresentationStyle.OverCurrentContext;
        dialogController.View.Opaque = false;
        dialogController.View.BackgroundColor = UIColor.Clear.ColorWithAlpha(0.0f);
    }
    
  • wuselSuselwuselSusel ✭✭ Member ✭✭

    @TheMaegges said:
    private void ShowDialog(UIViewController dialogController)
    {
    var bounds = UIScreen.MainScreen.Bounds;
    dialogController.View.Frame = bounds;
    UIApplication.SharedApplication.KeyWindow.RootViewController.ModalPresentationStyle = UIModalPresentationStyle.CurrentContext;
    UIApplication.SharedApplication.KeyWindow.RootViewController.AddChildViewController(dialogController);
    UIApplication.SharedApplication.KeyWindow.RootViewController.View.Opaque = false;
    UIApplication.SharedApplication.KeyWindow.RootViewController.View.Layer.AllowsGroupOpacity = true;
    UIApplication.SharedApplication.KeyWindow.RootViewController.View.Layer.BackgroundColor = new CGColor(Color.White.ToCGColor(), 0.0f);
    UIApplication.SharedApplication.KeyWindow.RootViewController.View.BackgroundColor = UIColor.Clear;
    UIApplication.SharedApplication.KeyWindow.RootViewController.View.AddSubview(dialogController.View);
    dialogController.ModalPresentationStyle = UIModalPresentationStyle.OverCurrentContext;
    dialogController.View.Opaque = false;
    dialogController.View.BackgroundColor = UIColor.Clear.ColorWithAlpha(0.0f);
    }

    Thank you very much but it didn't work for me.
    Have you tried it? My dialogues are completely opaque now.

  • TheMaeggesTheMaegges ✭✭ DEMember ✭✭

    @wuselSusel said:

    @TheMaegges said:
    private void ShowDialog(UIViewController dialogController)
    {
    var bounds = UIScreen.MainScreen.Bounds;
    dialogController.View.Frame = bounds;
    UIApplication.SharedApplication.KeyWindow.RootViewController.ModalPresentationStyle = UIModalPresentationStyle.CurrentContext;
    UIApplication.SharedApplication.KeyWindow.RootViewController.AddChildViewController(dialogController);
    UIApplication.SharedApplication.KeyWindow.RootViewController.View.Opaque = false;
    UIApplication.SharedApplication.KeyWindow.RootViewController.View.Layer.AllowsGroupOpacity = true;
    UIApplication.SharedApplication.KeyWindow.RootViewController.View.Layer.BackgroundColor = new CGColor(Color.White.ToCGColor(), 0.0f);
    UIApplication.SharedApplication.KeyWindow.RootViewController.View.BackgroundColor = UIColor.Clear;
    UIApplication.SharedApplication.KeyWindow.RootViewController.View.AddSubview(dialogController.View);
    dialogController.ModalPresentationStyle = UIModalPresentationStyle.OverCurrentContext;
    dialogController.View.Opaque = false;
    dialogController.View.BackgroundColor = UIColor.Clear.ColorWithAlpha(0.0f);
    }

    Thank you very much but it didn't work for me.
    Have you tried it? My dialogues are completely opaque now.

    This is strange, I copied it right out of my solution, which works perfectly fine for me. However getting that to run took me a hell of debugging time. Did you do a full clean and rebuild of your project? Also what is your deployment target? I am using 8.0. Last but not least, does your dialog provide transparent space as the dialog view in my example does?

  • wuselSuselwuselSusel ✭✭ Member ✭✭

    @TheMaegges said:
    This is strange, I copied it right out of my solution, which works perfectly fine for me. However getting that to run took me a hell of debugging time. Did you do a full clean and rebuild of your project? Also what is your deployment target? I am using 8.0. Last but not least, does your dialog provide transparent space as the dialog view in my example does?

    To be on the safe side, I tested the whole thing with your loading dialog. I changed the deployment target to 8 (previously 9) + full clean and rebuild.

    dialogController.View takes up the whole screen so any changes to the background color have no effect whatsoever.

  • TheMaeggesTheMaegges ✭✭ DEMember ✭✭

    Can you post the xaml of your dialog and maybe a screenshot?

  • wuselSuselwuselSusel ✭✭ Member ✭✭
    edited December 2018
    <?xml version="1.0" encoding="utf-8" ?>
    <ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
                 xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                 x:Class="MyApp.Views.UserControls.PopUps.BaseDialog"
                 x:Name="BaseDialogPage">
        <ContentPage.Content>
            <Grid BackgroundColor="#bb000000">
                <Grid.RowDefinitions>
                    <RowDefinition Height="*"/>
                    <RowDefinition Height="*"/>
                    <RowDefinition Height="*"/>
                </Grid.RowDefinitions>
                <Frame BackgroundColor="Black" CornerRadius="15" Grid.Row="1" x:Name="ContentGrid" Margin="100,0,100,0">
                    <Grid>
                        <Grid.RowDefinitions>
                            <RowDefinition Height="2*"/>
                            <RowDefinition Height="*"/>
                        </Grid.RowDefinitions>
                        <ActivityIndicator Grid.Row="0" Color="White" IsRunning="True" HorizontalOptions="Center" VerticalOptions="Center"/>
                        <Label x:Name="LoadingLabel" Text="Loading ..." VerticalOptions="End"  HorizontalOptions="Center" Grid.Row="1" TextColor="White" />
                    </Grid>
                </Frame>
            </Grid>
        </ContentPage.Content>
    </ContentPage>
    

    Like I said, for testing purposes I used your dialog :smile: .

    This actually has a background with content that is covered by the dialog (and its background?). Changing the transparency (dialogController.View.Alpha) makes it visible but also changes the opacity of the dialog itself.
    Thank you for your efforts.

  • TheMaeggesTheMaegges ✭✭ DEMember ✭✭

    I just tried it in a simulator, also no errors over there. Maybe there is a slight mismatch in the code we are using. Could you attach your project, so I could look into it?

  • wuselSuselwuselSusel ✭✭ Member ✭✭

    There you go, a minimal (unfortunately) not working example.
    Thank you so much!!

  • TheMaeggesTheMaegges ✭✭ DEMember ✭✭
    edited December 2018

    Thanks a lot!
    Actually there was a slightly, yet important difference between my current implementation and the one i posted here:

    In the BaseDialog.xaml BackgroundColor="Transparent" has to be set.

    <ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="App1.BaseDialog" x:Name="BaseDialogPage" BackgroundColor="Transparent">

    I must have forgotten to add that when posting the solution. Sorry for that.

    App1.zip 356.9K
  • wuselSuselwuselSusel ✭✭ Member ✭✭

    THANK YOU SO MUCH, That did it.
    After it worked perfectly for android, I wouldn't have thought of that.

  • ativodevativodev Member

    Hello, @TheMaegges your implementation was very useful for my development, but unfortunately I can not set transparency background. Could you help me?

  • KarenCateKarenCate ✭✭ USMember ✭✭

    @TheMaegges Thank you so much for your solution! It's exactly what I was looking for.

    Unfortunately, I managed to get lost in the black magic step. Does the static PageExtensions class go in the shared project or the Android project (I was guessing the latter since it's all... Android). It's getting Platform from System, and sure enough, it's marked "internal". Am I doing something wrong?

  • TheMaeggesTheMaegges ✭✭ DEMember ✭✭
    edited July 1

    @KarenCate said:
    @TheMaegges Thank you so much for your solution! It's exactly what I was looking for.

    Unfortunately, I managed to get lost in the black magic step. Does the static PageExtensions class go in the shared project or the Android project (I was guessing the latter since it's all... Android). It's getting Platform from System, and sure enough, it's marked "internal". Am I doing something wrong?

    Yes, you got that right. PageExtensions has to go into your Android project.
    Unfortunately, there was a change in Xamarin Forms: Until Version 3.5 Platform was accessible from outside.
    That was changed to internal, so the platform constructor has to be acquired and invoked with Reflection.

    I will look into this and adapt my code example, however i cannot promise if i will be able to do this on short term.
    Maybe I'll also set up an example repository, which should make it easier to use the code.

    Update: I just checked my implementation against the latest version of Xamarin.Forms ( 4.0.0.540366 ) and it compiles and works.

  • EndarEndar Member

    I implemented this on iOS and it worked great. Then after I made the Android version, I noticed a bug with Navigation.PushModalAsync. If I had opened the custom dialog during a session, Navigation.PushModalAsync will stop part way through its execution. The app continues to work and there is no error message. I copied the code exactly from what you posted, and it still does this when I run the app on Android. Does anyone have any idea what might be causing this?

Sign In or Register to comment.