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

时间:2017-06-22 21:20:29

标签: xamarin.ios xamarin.forms

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

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

我正在使用以下内容显示此弹出窗口:

await Navigation.PushModalAsync(mypopupinstance)

它运行正常,但弹出窗口是全屏的。我只想要一个小弹出窗口,我想看看背后是什么。

由于

5 个答案:

答案 0 :(得分:4)

您需要在其他地方寻找此类功能。一个这样的库是Rotorgames的Popup插件:https://github.com/rotorgames/Rg.Plugins.Popup

答案 1 :(得分:2)

模态页面无法像这样呈现。

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

DisplayAlert() 

在页面内。

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

答案 2 :(得分:0)

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

方法

使用Acr.UserDialogs库一段时间后,由于个人需求,我感到需要自定义对话框。我也想尽量减少必须依赖插件的必要性。 理想情况下,这样的对话框应该通过简单的调用来调用,例如:

Dialogs.ShowLoading();

string result = Dialogs.ShowPrompt();

与Xamarin.Forms一样,很明显,我们将需要依赖服务实现才能使它工作。

共享代码库

我们创建一个基本界面“ 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与自定义事件处理程序一起使用。这样一来,我们就可以显示对话框,并等待用户按下“确定”或“取消”按钮并使用返回的结果。

截至目前,对话框尽可能简单。我使用了一些其他的样式和主题代码,但是为了使答案简短而简单,我将省略这些代码。

正在加载对话框:

<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代码,因为与加载屏幕没有交互)

提示对话框

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实现

首先,我们将在前面的共享代码中创建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),但由于需要将目标片段容器放入其中,因此使用它不会成功。 / p>

但是,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实现

幸运的是,对于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);
    }
}
}

等等,现在,每当我们使用本文中“方法”部分的调用时,都会出现一个包含自定义Xamarin.Forms ContentPage的漂亮弹出对话框。

答案 3 :(得分:0)

使用Plugins.Popup之类的包来实现此目的比较容易,没有自定义渲染器,就不可能将图像添加到默认的AlertDialog中,这将限制您的工作。

使用弹出插件,您只需将其添加到解决方案中,即可在iOS和Android中初始化:

[Register("AppDelegate")]
public partial class AppDelegate : global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate
{
    public override bool FinishedLaunching(UIApplication app, NSDictionary options)
    {
      Rg.Plugins.Popup.Popup.Init();

      global::Xamarin.Forms.Forms.Init ();
      LoadApplication (new App ());
      return base.FinishedLaunching (app, options);
    }
}

Android:

namespace HelloXamarinFormsWorld.Android
{
    [Activity(Label = "HelloXamarinFormsWorld", MainLauncher = true,
        ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
    public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsApplicationActivity
    {
        protected override void OnCreate(Bundle bundle)
        {
            base.OnCreate(bundle);

            Rg.Plugins.Popup.Popup.Init(this, bundle);

            Xamarin.Forms.Forms.Init(this, bundle);
            LoadApplication (new App ());
        }
    }
}

创建弹出页面

<pages:PopupPage.Animation>
    <animations:ScaleAnimation 
        PositionIn="Center"
        PositionOut="Center"
        ScaleIn="1.2"
        ScaleOut="0.8"
        DurationIn="400"
        DurationOut="300"
        EasingIn="SinOut"
        EasingOut="SinIn"
        HasBackgroundAnimation="True"/>
</pages:PopupPage.Animation>
<!--You can use any elements here which are extended from Xamarin.Forms.View-->
<StackLayout 
    VerticalOptions="Center" 
    HorizontalOptions="Center" 
    Padding="20, 20, 20, 20">
    <Label
        Text="Test"/>
</StackLayout>

而且,要显示在您的页面中:

await Navigation.PushPopupAsync(page);

答案 4 :(得分:0)

使用名为“ Rg.Plugin.Popup”的软件包。它将为您提供帮助。