Click here to Skip to main content
15,860,844 members
Articles / Mobile Apps / Xamarin

Custom Dialogs in Xamarin.Forms: Plugin "Rg.Plugins.Popup" Pages Asynchronous

Rate me:
Please Sign up or sign in to vote.
5.00/5 (4 votes)
8 Dec 2019CPOL1 min read 15.2K   3   2
This article proposes a solution for plugging "Rg.Plugins.Popup" asynchronous into your code.

Introduction

This article proposes a solution for plugging "Rg.Plugins.Popup" package into a Xamarin.Forms application.

Background

The 3rd party package is a custom dialog provider. Below, I will demonstrate how to call its pages from a DialogService asynchronous. The solution is in using TaskCompletionSource for external asynchronous operation.

The state of a task created by a TaskCompletionSource is controlled explicitly by the methods on TaskCompletionSource. This enables the completion of the external asynchronous operation to be propagated to the underlying Task. The separation also ensures that consumers are not able to transition the state without access to the corresponding TaskCompletionSource. [From MS doc]

Result

Image 1

Confirmation Dialog

Image 2

Ok Dialog

IDialogService Implementation

The following is a code snippet from DialogService.cs file.

C#
public async Task<bool> ShowConfirmationDialogAsync
    (string message, string yesButtonText = "Yes", string noButtonText = "No")
{
     var confirmationDialog = new ConfirmationDialog(message)
     {
         YesButtonText = yesButtonText,
         NoButtonText = noButtonText
     };
     await _popupNavigation.PushAsync(confirmationDialog);
     var result = await confirmationDialog.GetResult();
     await _popupNavigation.PopAllAsync();
     return (bool)result;
}

The _popupNavigation object is Rg.Plugins.Popup.Services.PopupNavigation.Instance.

Usage in View Model Layer

Here, I call the service to prompt the dialog to the end user and wait for the UI response to take a cascaded action.

C#
private ICommand _selectedProductCommand;
public ICommand SelectedProductCommand => _selectedProductCommand ?? 
    (_selectedProductCommand = new Command<Product>(async (selectedProduct) =>
{
   var confirmed = await _dialogService.ShowConfirmationDialogAsync
                   ($"Are you sure you want to delete Item: {selectedProduct.Name} ?");
   if (!confirmed)
       return;

   ProductList.Remove(selectedProduct);
}));

Code Structure

Image 3

Design

Image 4

Dialog Base Definition

DialogBase.xaml contains a ContentView as actions place holder. The actions will be passed from the concrete dialog definition.

XML
<popup:PopupPage xmlns:popup="clr-namespace:Rg.Plugins.Popup.Pages;
assembly=Rg.Plugins.Popup" xmlns="http://xamarin.com/schemas/2014/forms" 
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
        x:Class="XamF.Controls.CustomDialogs.Base.DialogBase"
        x:Name="dialogBasePage">
    <ContentPage.Content>
        <Frame WidthRequest="250" BackgroundColor="White" 
         HorizontalOptions="Center" VerticalOptions="Center" 
         Padding="30" CornerRadius="0">
            <Grid RowSpacing="20">
                <Grid.RowDefinitions>
                    <RowDefinition Height="*"></RowDefinition>
                    <RowDefinition Height="Auto"></RowDefinition>
                </Grid.RowDefinitions>
                <Label x:Name="lblMessage" 
                Grid.Row="0" VerticalTextAlignment="Center" 
                 HorizontalTextAlignment="Center" HorizontalOptions="Center" 
                 VerticalOptions="StartAndExpand" FontSize="Medium"></Label>
                <ContentView Grid.Row="1" 
                 Content="{Binding Source={x:Reference dialogBasePage}, 
                 Path=ActionsPlaceHolder}"  
                 HorizontalOptions="Center" VerticalOptions="End">
                </ContentView>
            </Grid>
        </Frame>
    </ContentPage.Content>
</popup:PopupPage>

DialogBase.cs contains a protected TaskCompletionSource so that the concrete dialog can use it to SetResult.

C#
public abstract partial class DialogBase : PopupPage
{
    #region Variables
    protected Dictionary<DialogAction, Task> DialogActions;
    protected Action OnApearing;
    protected TaskCompletionSource<object> Proccess;
    //protected ILocator _locator;
    protected IPopupNavigation _popupNavigation;
    #endregion Variables
    #region Properties
    protected string _message;
    protected string Message { get => _message; set => _message = value; }
    #endregion Properties
    public DialogBase()
    {
        InitializeComponent();
        //_locator = App.Locator;
        _popupNavigation = PopupNavigation.Instance;
        this.CloseWhenBackgroundIsClicked = false;
    }
    #region Bindable Properties
    public static readonly BindableProperty ActionsPlaceHolderProperty
        = BindableProperty.Create
          (nameof(ActionsPlaceHolder), typeof(View), typeof(DialogBase));
    public View ActionsPlaceHolder
    {
        get { return (View)GetValue(ActionsPlaceHolderProperty); }
        set { SetValue(ActionsPlaceHolderProperty, value); }
    }
    #endregion Bindable Properties
    protected override void OnAppearing()
    {
        base.OnAppearing();
        this.lblMessage.Text = _message;
        OnApearing?.Invoke();
        Proccess = new TaskCompletionSource<object>();
    }
    public virtual Task<object> GetResult()
    {
        return Proccess.Task;
    }
}

Confirmation Dialog Definition

XAML:

XML
<dialogBase:DialogBase
    xmlns:dialogBase="clr-namespace:XamF.Controls.CustomDialogs.Base" 
    xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="XamF.Controls.CustomDialogs.ConfirmationDialog">
    <dialogBase:DialogBase.ActionsPlaceHolder>
        <StackLayout Orientation="Horizontal" Grid.Row="1">
            <Button x:Name="btnYes" Text="Yes" WidthRequest="80"></Button>
            <Button x:Name="btnNo" Text="No" WidthRequest="80"></Button>
        </StackLayout>
    </dialogBase:DialogBase.ActionsPlaceHolder>
</dialogBase:DialogBase>

Code behind:

C#
public partial class ConfirmationDialog : DialogBase
{
    public string YesButtonText { get; set; }
    public string NoButtonText { get; set; }
    public ConfirmationDialog(string message)
    {
        InitializeComponent();
        _message = message;
        OnApearing = () =>
        {
            this.btnNo.Text = NoButtonText;
            this.btnYes.Text = YesButtonText;
        };
        this.btnNo.Clicked += (sender, args) =>
        {
            Proccess.SetResult(false);
        };

        this.btnYes.Clicked += (sender, args) =>
        {
            Proccess.SetResult(true);
        };
    }
}

Conclusion

The objective is developing a reusable library that wraps a 3rd party UI package and calls it asynchronous task-based that can be handed out to consumers (view model).

History

  • 8th December, 2019: Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Technical Lead
Egypt Egypt
I have 10 years of experiences in developing Web & Desktop Applications.

Specialties:
System Analysis
Software Design & Architecture
Web / Desktop Development
Data Modeling & Database Development

Comments and Discussions

 
QuestionSource Code Pin
out_of_law21-Mar-20 21:50
out_of_law21-Mar-20 21:50 
AnswerRe: Source Code Pin
Abdulrahman Emad30-Mar-20 11:44
professionalAbdulrahman Emad30-Mar-20 11:44 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.