我想在xamarin表单应用程序(iOS / Android PCL)中显示一个小弹出窗口
我实际上正在使用ContentPage(XAML C#代码)
我正在显示这个弹出窗口:
await Navigation.PushModalAsync(mypopupinstance)
它工作正常,但弹出窗口是全屏幕 . 我只想要一个小弹出窗口,我想看看背后是什么 .
谢谢
你'll need to look elsewhere for this kind of functionality. One such library is Rotorgames'弹出插件:https://github.com/rotorgames/Rg.Plugins.Popup
模态页面不能像那样呈现 .
对于小弹出窗口,您可以使用
DisplayAlert()
在页面内 .
如果您想要更可自定义的内容,只需将页面内容包装在相对布局或网格中,并在普通内容的基础上添加弹出窗口 .
我正在处理同样的问题,到目前为止我能够创建一个可以容纳内容页面的弹出窗口 . 我很乐意分享我目前的状态 . 请注意,我将尽可能缩短代码示例,因此将其简化为简单的加载和文本提示对话框 .
Approach
在使用Acr.UserDialogs库一段时间之后,我觉得需要有可以根据我的个人要求定制的对话框 . 此外,我想尽量减少必须依赖插件的必要性 . 理想情况下,应该通过简单的调用来调用这样的对话框,例如:
Dialogs.ShowLoading();
要么
string result = Dialogs.ShowPrompt();
与Xamarin.Forms一样,很明显我们需要一个依赖服务实现才能工作 .
Shared Code Library
我们创建了一个基本接口“IDialogs.cs”:
public interface IDialogs { bool IsDialogOpen(); void ShowLoading(LoadingDialog dialog); void ShowPrompt(PromptDialog dialog); void HideDialog(); }
接下来要做的是拥有一个静态对话框类,可以从需要对话框的每个页面调用它 . “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(); } }
您会注意到,我们在ShowPromptText方法中使用TaskCompletionSource和自定义事件处理程序 . 这使我们能够显示对话框并等待用户按下okay或取消按钮并使用返回的结果 .
截至目前,对话框非常简单 . 我使用了一些额外的代码来进行样式设置和主题化,但我会将其留下来,以使这个答案简短而简单 .
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" BackgroundColor="Transparent"> <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>
(不需要为此发布xaml.cs代码,因为没有与加载屏幕的交互)
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" BackgroundColor="Transparent"> <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
首先,我们将创建之前在共享代码中创建的IDialogs接口的android实现:
[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; } } } }
请注意,在调用实际方法以显示对话框之前,必须设置对话框服务的活动,因此在MainActivity.cs中确保调用
DialogService.Init(this);
在初始化Xamarin.Forms之后 .
最后,这里有一些黑魔法:
通常,人们可以通过将片段容器放入主布局并在其中放入片段来在Android中实现这样的对话框 . 不幸的是,由于使用了Xamarin.Forms,默认情况下这样的主要布局是不可用的 .
尽管Xamarin.Forms提供了一个视图扩展,它允许将ContentPage转换为片段(ContentPage.CreateFragment),但我们不会成功使用它,因为它需要放置一个目标片段容器 .
但是,android提供了一种称为DialogFragment的东西,它可以在屏幕上抛出,而不需要定义的片段容器 .
遗憾的是,没有任何开箱即用的解决方案可以从Xamarin Forms创建DialogFragment . 好消息是,使用System.Reflection(;)可以克服这个问题,因此我们创建了自己的扩展方法和内部类的一些修改版本,xamarin.forms在引擎盖下使用 . 为了实现这一点,我从Xamarin.Platform.Android中获取了Xamarin.Forms中的代码并修改它以从ContentPage创建一个DialogFragment:
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); } } }
iOS implementation
幸运的是,对于iOS实现,没有必要深入研究Xamarin.Forms代码:
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 = 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); } } }
Et voila,现在每当我们使用“方法”的调用时 - 这篇文章的部分,一个包含我们的自定义Xamarin.Forms ContentPage的漂亮的弹出对话框将会显示出来 .
3 回答
你'll need to look elsewhere for this kind of functionality. One such library is Rotorgames'弹出插件:https://github.com/rotorgames/Rg.Plugins.Popup
模态页面不能像那样呈现 .
对于小弹出窗口,您可以使用
在页面内 .
如果您想要更可自定义的内容,只需将页面内容包装在相对布局或网格中,并在普通内容的基础上添加弹出窗口 .
我正在处理同样的问题,到目前为止我能够创建一个可以容纳内容页面的弹出窗口 . 我很乐意分享我目前的状态 . 请注意,我将尽可能缩短代码示例,因此将其简化为简单的加载和文本提示对话框 .
Approach
在使用Acr.UserDialogs库一段时间之后,我觉得需要有可以根据我的个人要求定制的对话框 . 此外,我想尽量减少必须依赖插件的必要性 . 理想情况下,应该通过简单的调用来调用这样的对话框,例如:
要么
与Xamarin.Forms一样,很明显我们需要一个依赖服务实现才能工作 .
Shared Code Library
我们创建了一个基本接口“IDialogs.cs”:
接下来要做的是拥有一个静态对话框类,可以从需要对话框的每个页面调用它 . “Dialogs.cs”:
您会注意到,我们在ShowPromptText方法中使用TaskCompletionSource和自定义事件处理程序 . 这使我们能够显示对话框并等待用户按下okay或取消按钮并使用返回的结果 .
截至目前,对话框非常简单 . 我使用了一些额外的代码来进行样式设置和主题化,但我会将其留下来,以使这个答案简短而简单 .
The loading dialog:
(不需要为此发布xaml.cs代码,因为没有与加载屏幕的交互)
The prompt dialog
PromptDialog.Xaml:
PromptDialog.xaml.cs:
Android implementation
首先,我们将创建之前在共享代码中创建的IDialogs接口的android实现:
请注意,在调用实际方法以显示对话框之前,必须设置对话框服务的活动,因此在MainActivity.cs中确保调用
在初始化Xamarin.Forms之后 .
最后,这里有一些黑魔法:
通常,人们可以通过将片段容器放入主布局并在其中放入片段来在Android中实现这样的对话框 . 不幸的是,由于使用了Xamarin.Forms,默认情况下这样的主要布局是不可用的 .
尽管Xamarin.Forms提供了一个视图扩展,它允许将ContentPage转换为片段(ContentPage.CreateFragment),但我们不会成功使用它,因为它需要放置一个目标片段容器 .
但是,android提供了一种称为DialogFragment的东西,它可以在屏幕上抛出,而不需要定义的片段容器 .
遗憾的是,没有任何开箱即用的解决方案可以从Xamarin Forms创建DialogFragment . 好消息是,使用System.Reflection(;)可以克服这个问题,因此我们创建了自己的扩展方法和内部类的一些修改版本,xamarin.forms在引擎盖下使用 . 为了实现这一点,我从Xamarin.Platform.Android中获取了Xamarin.Forms中的代码并修改它以从ContentPage创建一个DialogFragment:
iOS implementation
幸运的是,对于iOS实现,没有必要深入研究Xamarin.Forms代码:
Et voila,现在每当我们使用“方法”的调用时 - 这篇文章的部分,一个包含我们的自定义Xamarin.Forms ContentPage的漂亮的弹出对话框将会显示出来 .