首页 文章

显示带有xamarin表单的弹出窗口

提问于
浏览
1

我想在xamarin表单应用程序(iOS / Android PCL)中显示一个小弹出窗口

我实际上正在使用ContentPage(XAML C#代码)

我正在显示这个弹出窗口:

await Navigation.PushModalAsync(mypopupinstance)

它工作正常,但弹出窗口是全屏幕 . 我只想要一个小弹出窗口,我想看看背后是什么 .

谢谢

3 回答

  • 2

    你'll need to look elsewhere for this kind of functionality. One such library is Rotorgames'弹出插件:https://github.com/rotorgames/Rg.Plugins.Popup

  • 4

    模态页面不能像那样呈现 .

    对于小弹出窗口,您可以使用

    DisplayAlert()
    

    在页面内 .

    如果您想要更可自定义的内容,只需将页面内容包装在相对布局或网格中,并在普通内容的基础上添加弹出窗口 .

  • 0

    我正在处理同样的问题,到目前为止我能够创建一个可以容纳内容页面的弹出窗口 . 我很乐意分享我目前的状态 . 请注意,我将尽可能缩短代码示例,因此将其简化为简单的加载和文本提示对话框 .

    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的漂亮的弹出对话框将会显示出来 .

相关问题