Skip to content
This repository was archived by the owner on May 1, 2024. It is now read-only.

Fix NullReferenceException when displaying Toast or SnackBar #963

Merged
merged 15 commits into from
Apr 19, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ namespace Xamarin.CommunityToolkit.Extensions
{
public static class PageExtension
{
public static Task DisplayToastAsync(this Page page, string message, int durationMilliseconds = 3000)
public static async Task DisplayToastAsync(this Page page, string message, int durationMilliseconds = 3000)
{
_ = page ?? throw new ArgumentNullException(nameof(page));

Expand All @@ -26,11 +26,11 @@ public static Task DisplayToastAsync(this Page page, string message, int duratio
#endif
};
var snackBar = new SnackBar();
snackBar.Show(page, args);
return args.Result.Task;
await snackBar.Show(page, args);
await args.Result.Task;
}

public static Task DisplayToastAsync(this Page page, ToastOptions toastOptions)
public static async Task DisplayToastAsync(this Page page, ToastOptions toastOptions)
{
_ = page ?? throw new ArgumentNullException(nameof(page));

Expand All @@ -43,11 +43,11 @@ public static Task DisplayToastAsync(this Page page, ToastOptions toastOptions)
BackgroundColor = arguments.BackgroundColor,
IsRtl = arguments.IsRtl
};
snackBar.Show(page, options);
return options.Result.Task;
await snackBar.Show(page, options);
await options.Result.Task;
}

public static Task<bool> DisplaySnackBarAsync(this Page page, string message, string actionButtonText, Func<Task> action, int durationMilliseconds = 3000)
public static async Task<bool> DisplaySnackBarAsync(this Page page, string message, string actionButtonText, Func<Task> action, int durationMilliseconds = 3000)
{
_ = page ?? throw new ArgumentNullException(nameof(page));

Expand All @@ -71,18 +71,22 @@ public static Task<bool> DisplaySnackBarAsync(this Page page, string message, st
#endif
};
var snackBar = new SnackBar();
snackBar.Show(page, options);
return options.Result.Task;
await snackBar.Show(page, options);
var isButtonClicked = await options.Result.Task;

return isButtonClicked;
}

public static Task<bool> DisplaySnackBarAsync(this Page page, SnackBarOptions snackBarOptions)
public static async Task<bool> DisplaySnackBarAsync(this Page page, SnackBarOptions snackBarOptions)
{
_ = page ?? throw new ArgumentNullException(nameof(page));

var snackBar = new SnackBar();
var arguments = snackBarOptions ?? new SnackBarOptions();
snackBar.Show(page, arguments);
return arguments.Result.Task;
var options = snackBarOptions ?? new SnackBarOptions();
await snackBar.Show(page, options);
var isButtonClicked = await options.Result.Task;

return isButtonClicked;
}
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
using Xamarin.Forms;
using System;
using System.Threading.Tasks;
using Xamarin.Forms;
using Android.Graphics;
using Android.Widget;
using Xamarin.Forms.Platform.Android;
using Xamarin.CommunityToolkit.UI.Views.Options;
using Android.Util;
using System;
#if MONOANDROID10_0
using AndroidSnackBar = Google.Android.Material.Snackbar.Snackbar;
#else
Expand All @@ -15,10 +16,10 @@ namespace Xamarin.CommunityToolkit.UI.Views
{
class SnackBar
{
internal void Show(Page sender, SnackBarOptions arguments)
internal async ValueTask Show(Page sender, SnackBarOptions arguments)
{
var view = Platform.GetRenderer(sender).View;
var snackBar = AndroidSnackBar.Make(view, arguments.MessageOptions.Message, (int)arguments.Duration.TotalMilliseconds);
var renderer = await GetRendererWithRetries(sender) ?? throw new ArgumentException("Provided page cannot be parent to SnackBar", nameof(sender));
var snackBar = AndroidSnackBar.Make(renderer.View, arguments.MessageOptions.Message, (int)arguments.Duration.TotalMilliseconds);
var snackBarView = snackBar.View;
if (arguments.BackgroundColor != Forms.Color.Default)
{
Expand Down Expand Up @@ -100,6 +101,20 @@ internal void Show(Page sender, SnackBarOptions arguments)
snackBar.Show();
}

/// <summary>
/// Tries to get renderer multiple times since it can be null while switching tabs in Shell.
/// See this bug for more info: https://github.com/xamarin/Xamarin.Forms/issues/13950
/// </summary>
static async Task<IVisualElementRenderer?> GetRendererWithRetries(Page page, int retryCount = 5)
{
var renderer = Platform.GetRenderer(page);
if (renderer != null || retryCount <= 0)
return renderer;

await Task.Delay(50);
return await GetRendererWithRetries(page, retryCount - 1);
}

class SnackBarCallback : AndroidSnackBar.BaseCallback
{
readonly SnackBarOptions arguments;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Linq;
using System.Threading.Tasks;
using System.Timers;
using Gtk;
using Pango;
Expand All @@ -13,7 +14,7 @@ class SnackBar
{
Timer? snackBarTimer;

public void Show(Page page, SnackBarOptions arguments)
public ValueTask Show(Page page, SnackBarOptions arguments)
{
var mainWindow = (Platform.GetRenderer(page).Container.Child as Forms.Platform.GTK.Controls.Page)?.Children[0] as VBox;
var snackBarLayout = GetSnackBarLayout(mainWindow, arguments);
Expand All @@ -29,6 +30,7 @@ public void Show(Page page, SnackBarOptions arguments)
};

snackBarTimer.Start();
return default;
}

HBox GetSnackBarLayout(Container? container, SnackBarOptions arguments)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ namespace Xamarin.CommunityToolkit.UI.Views
{
class SnackBar
{
internal void Show(Page sender, SnackBarOptions arguments)
internal ValueTask Show(Page sender, SnackBarOptions arguments)
{
var snackBar = NativeSnackBar.MakeSnackBar(arguments.MessageOptions.Message)
.SetDuration(arguments.Duration.TotalMilliseconds)
Expand Down Expand Up @@ -124,6 +124,8 @@ internal void Show(Page sender, SnackBarOptions arguments)
}

snackBar.Show();

return default;
}
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
using System;
using System.Threading.Tasks;
using Xamarin.CommunityToolkit.UI.Views.Options;
using Xamarin.Forms;

namespace Xamarin.CommunityToolkit.UI.Views
{
class SnackBar
{
internal void Show(Page sender, SnackBarOptions arguments) => throw new PlatformNotSupportedException();
internal ValueTask Show(Page sender, SnackBarOptions arguments) => throw new PlatformNotSupportedException();
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using Xamarin.CommunityToolkit.UI.Views.Options;
using EButton = ElmSharp.Button;

namespace Xamarin.CommunityToolkit.UI.Views
{
class SnackBar
{
internal void Show(Forms.Page sender, SnackBarOptions arguments)
internal ValueTask Show(Forms.Page sender, SnackBarOptions arguments)
{
var snackBarDialog =
Forms.Platform.Tizen.Native.Dialog.CreateDialog(Forms.Forms.NativeParent,
Expand Down Expand Up @@ -36,11 +37,11 @@ internal void Show(Forms.Page sender, SnackBarOptions arguments)
}

snackBarDialog.TimedOut += (s, evt) => DismissSnackBar();

snackBarDialog.BackButtonPressed += (s, evt) => DismissSnackBar();

snackBarDialog.Show();

return default;

void DismissSnackBar()
{
snackBarDialog.Dismiss();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Threading.Tasks;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media;
Expand Down Expand Up @@ -34,7 +35,7 @@ class SnackBar
return null;
}

internal void Show(Forms.Page page, SnackBarOptions arguments)
internal ValueTask Show(Forms.Page page, SnackBarOptions arguments)
{
var snackBarLayout = new SnackBarLayout(arguments);
var pageControl = Platform.GetRenderer(page).ContainerElement.Parent;
Expand All @@ -59,6 +60,7 @@ internal void Show(Forms.Page page, SnackBarOptions arguments)
grid.RowDefinitions.Add(snackBarRow);
grid.Children.Add(snackBarLayout);
Grid.SetRow(snackBarLayout, grid.RowDefinitions.Count - 1);
return default;
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Windows.Forms;
using System.Threading.Tasks;
using System.Windows.Forms;
using Xamarin.CommunityToolkit.UI.Views.Helpers;
using Xamarin.CommunityToolkit.UI.Views.Options;
using Xamarin.Forms;
Expand All @@ -11,7 +12,7 @@ class SnackBar
{
Timer? snackBarTimer;

internal void Show(Page page, SnackBarOptions arguments)
internal ValueTask Show(Page page, SnackBarOptions arguments)
{
var formsAppBar = System.Windows.Application.Current.MainWindow.FindChild<FormsAppBar>("PART_BottomAppBar");
var currentContent = formsAppBar.Content;
Expand All @@ -31,6 +32,7 @@ internal void Show(Page page, SnackBarOptions arguments)
};
snackBarTimer.Start();
formsAppBar.Content = snackBar;
return default;
}
}
}