Xamarin.Forms 中的自定义对话框:插件 "Rg.Plugins.Popup" 页面异步





5.00/5 (4投票s)
本文提出了一种将 "Rg.Plugins.Popup" 异步集成到您的代码中的解决方案。
引言
本文提出了一种将 "Rg.Plugins.Popup
" 包集成到 Xamarin.Forms
应用程序中的解决方案。
背景
该第三方包是一个自定义对话框提供程序。下面,我将演示如何从 DialogService
异步调用其页面。解决方案在于使用 Task
通过 TaskCompletionSource
创建的任务的状态由 TaskCompletionSource
上的方法显式控制。这使得能够将外部异步操作的**完成****传播**到**底层 Task**。这种**分离**也确保了**消费者无法在没有**访问相应的 TaskCompletionSource
的情况下**转换状态**。[来自 MS 文档]
结果
确认对话框
确定对话框
IDialogService 实现
以下是来自 DialogService.cs 文件的代码片段。
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;
}
_popupNavigation
对象是 Rg.Plugins.Popup.Services.PopupNavigation.Instance
。
在 ViewModel 层中的用法
在这里,我调用该服务向最终用户提示对话框,并等待 UI 响应以采取级联操作。
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);
}));
代码结构
设计
对话框基本定义
DialogBase.xaml 包含一个 ContentView
作为操作占位符。操作将从具体的对话框定义中传递过来。
<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 包含一个 protected TaskCompletionSource
,以便具体的对话框可以使用它来 SetResult
。
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;
}
}
确认对话框定义
XAML
<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>
代码隐藏
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);
};
}
}
结论
目标是开发一个可重用的库,它封装了一个第三方 UI 包,并以基于任务的异步方式调用它,可以提供给消费者(ViewModel)。
历史
- 2019年12月8日:初始版本