Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handle EventCallback exceptions via Renderer #78

Merged
merged 2 commits into from
Nov 9, 2022
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
2 changes: 2 additions & 0 deletions samples/ControlGallery/Views/Shell/SearchHandlerPage.razor
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@

async Task SearchItems(string query)
{
await Task.Delay(100);

if (query?.Length > 1)
{
_matchingItems = _allItems.Where(i => i.Contains(query, StringComparison.OrdinalIgnoreCase));
Expand Down
48 changes: 46 additions & 2 deletions src/BlazorBindings.Core/NativeControlComponentBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,15 @@
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Rendering;
using System;
using System.Runtime.ExceptionServices;
using System.Threading.Tasks;

namespace BlazorBindings.Core
{
public abstract class NativeControlComponentBase : ComponentBase
{
private Exception _eventCallbackException;

public IElementHandler ElementHandler { get; private set; }

public void SetElementReference(IElementHandler elementHandler)
Expand All @@ -18,9 +22,13 @@ public void SetElementReference(IElementHandler elementHandler)

protected override void BuildRenderTree(RenderTreeBuilder builder)
{
if (builder is null)
ArgumentNullException.ThrowIfNull(builder);

if (_eventCallbackException != null)
{
throw new ArgumentNullException(nameof(builder));
var oldException = _eventCallbackException;
_eventCallbackException = null;
ExceptionDispatchInfo.Throw(oldException);
}

builder.OpenElement(0, GetType().FullName);
Expand All @@ -46,6 +54,42 @@ protected virtual void RenderAdditionalElementContent(RenderTreeBuilder builder,
{
}

protected Task InvokeEventCallback<T>(EventCallback<T> eventCallback, T value)
{
return InvokeAsync(async () =>
{
try
{
await eventCallback.InvokeAsync(value);
}
catch (Exception ex)
{
// Take a look here for the reasoning
// https://github.com/dotnet/aspnetcore/issues/44920
_eventCallbackException = ex;
StateHasChanged();
}
});
}

protected Task InvokeEventCallback(EventCallback eventCallback)
{
return InvokeAsync(async () =>
{
try
{
await eventCallback.InvokeAsync();
}
catch (Exception ex)
{
// Take a look here for the reasoning
// https://github.com/dotnet/aspnetcore/issues/44920
_eventCallbackException = ex;
StateHasChanged();
}
});
}

protected virtual RenderFragment GetChildContent() => null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ private string GetLocalHandlerFunctionBody()
}
else
{
argument = _eventHandlerType.IsGenericType ? "e" : "";
argument = GetEventArgType(_eventHandlerType).Name != nameof(EventArgs) ? "e" : "";
}

if (_bindedProperty != null && IsPropertyChangedEvent)
Expand All @@ -73,7 +73,7 @@ private string GetLocalHandlerFunctionBody()
{{
var value = {argument};
{_bindedProperty.Name} = value;
InvokeAsync(() => {ComponentPropertyName}.InvokeAsync(value));
InvokeEventCallback({ComponentPropertyName}, value);
}}
}}";
}
Expand All @@ -84,11 +84,13 @@ private string GetLocalHandlerFunctionBody()
{{
var value = {argument};
{_bindedProperty.Name} = value;
InvokeAsync(() => {ComponentPropertyName}.InvokeAsync(value));
InvokeEventCallback({ComponentPropertyName}, value);
}}";
}

return $" => InvokeAsync(() => {ComponentPropertyName}.InvokeAsync({argument}));";
return string.IsNullOrEmpty(argument)
? $" => InvokeEventCallback({ComponentPropertyName});"
: $" => InvokeEventCallback({ComponentPropertyName}, {argument});";
}

internal static GeneratedPropertyInfo[] GetEventCallbackProperties(GeneratedTypeInfo containingType)
Expand Down Expand Up @@ -152,7 +154,7 @@ private static string GetRenderFragmentType(GeneratedTypeInfo containingType, IE
return $"EventCallback<{typeName}>";
}

var eventArgType = eventInfo.Type.GetMethod("Invoke").Parameters[1].Type;
var eventArgType = GetEventArgType(eventInfo.Type);
if (eventArgType.Name != nameof(EventArgs))
{
return $"EventCallback<{containingType.GetTypeNameAndAddNamespace(eventArgType)}>";
Expand Down Expand Up @@ -188,5 +190,10 @@ private static bool IsBindEvent(IEventSymbol eventSymbol, out IPropertySymbol pr

return property != null;
}

private static ITypeSymbol GetEventArgType(ITypeSymbol eventHandlerType)
{
return eventHandlerType.GetMethod("Invoke").Parameters[1].Type;
}
}
}
4 changes: 2 additions & 2 deletions src/BlazorBindings.Maui/Elements/BaseShellItem.generated.cs
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ protected override void HandleParameter(string name, object value)
case nameof(OnAppearing):
if (!Equals(OnAppearing, value))
{
void NativeControlAppearing(object sender, EventArgs e) => InvokeAsync(() => OnAppearing.InvokeAsync());
void NativeControlAppearing(object sender, EventArgs e) => InvokeEventCallback(OnAppearing);

OnAppearing = (EventCallback)value;
NativeControl.Appearing -= NativeControlAppearing;
Expand All @@ -100,7 +100,7 @@ protected override void HandleParameter(string name, object value)
case nameof(OnDisappearing):
if (!Equals(OnDisappearing, value))
{
void NativeControlDisappearing(object sender, EventArgs e) => InvokeAsync(() => OnDisappearing.InvokeAsync());
void NativeControlDisappearing(object sender, EventArgs e) => InvokeEventCallback(OnDisappearing);

OnDisappearing = (EventCallback)value;
NativeControl.Disappearing -= NativeControlDisappearing;
Expand Down
6 changes: 3 additions & 3 deletions src/BlazorBindings.Maui/Elements/Button.generated.cs
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ protected override void HandleParameter(string name, object value)
case nameof(OnClick):
if (!Equals(OnClick, value))
{
void NativeControlClicked(object sender, EventArgs e) => InvokeAsync(() => OnClick.InvokeAsync());
void NativeControlClicked(object sender, EventArgs e) => InvokeEventCallback(OnClick);

OnClick = (EventCallback)value;
NativeControl.Clicked -= NativeControlClicked;
Expand All @@ -159,7 +159,7 @@ protected override void HandleParameter(string name, object value)
case nameof(OnPress):
if (!Equals(OnPress, value))
{
void NativeControlPressed(object sender, EventArgs e) => InvokeAsync(() => OnPress.InvokeAsync());
void NativeControlPressed(object sender, EventArgs e) => InvokeEventCallback(OnPress);

OnPress = (EventCallback)value;
NativeControl.Pressed -= NativeControlPressed;
Expand All @@ -169,7 +169,7 @@ protected override void HandleParameter(string name, object value)
case nameof(OnRelease):
if (!Equals(OnRelease, value))
{
void NativeControlReleased(object sender, EventArgs e) => InvokeAsync(() => OnRelease.InvokeAsync());
void NativeControlReleased(object sender, EventArgs e) => InvokeEventCallback(OnRelease);

OnRelease = (EventCallback)value;
NativeControl.Released -= NativeControlReleased;
Expand Down
4 changes: 2 additions & 2 deletions src/BlazorBindings.Maui/Elements/CarouselView.generated.cs
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ void NativeControlCurrentItemChanged(object sender, MC.CurrentItemChangedEventAr
{
var value = (T)NativeControl.CurrentItem;
CurrentItem = value;
InvokeAsync(() => CurrentItemChanged.InvokeAsync(value));
InvokeEventCallback(CurrentItemChanged, value);
}

CurrentItemChanged = (EventCallback<T>)value;
Expand All @@ -117,7 +117,7 @@ void NativeControlPositionChanged(object sender, MC.PositionChangedEventArgs e)
{
var value = NativeControl.Position;
Position = value;
InvokeAsync(() => PositionChanged.InvokeAsync(value));
InvokeEventCallback(PositionChanged, value);
}

PositionChanged = (EventCallback<int>)value;
Expand Down
2 changes: 1 addition & 1 deletion src/BlazorBindings.Maui/Elements/CheckBox.generated.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ void NativeControlCheckedChanged(object sender, MC.CheckedChangedEventArgs e)
{
var value = NativeControl.IsChecked;
IsChecked = value;
InvokeAsync(() => IsCheckedChanged.InvokeAsync(value));
InvokeEventCallback(IsCheckedChanged, value);
}

IsCheckedChanged = (EventCallback<bool>)value;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ protected override void HandleParameter(string name, object value)
case nameof(OnLayoutChanged):
if (!Equals(OnLayoutChanged, value))
{
void NativeControlLayoutChanged(object sender, EventArgs e) => InvokeAsync(() => OnLayoutChanged.InvokeAsync());
void NativeControlLayoutChanged(object sender, EventArgs e) => InvokeEventCallback(OnLayoutChanged);

OnLayoutChanged = (EventCallback)value;
NativeControl.LayoutChanged -= NativeControlLayoutChanged;
Expand Down
4 changes: 3 additions & 1 deletion src/BlazorBindings.Maui/Elements/ContentPage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,9 @@ protected override bool HandleAdditionalParameter(string name, object value)
if (!Equals(OnBackButtonPressed, value))
{
OnBackButtonPressed = (EventCallback)value;
GetBackButtonBehavior().Command = OnBackButtonPressed.HasDelegate ? new EventCallbackCommand(OnBackButtonPressed) : null;
GetBackButtonBehavior().Command = OnBackButtonPressed.HasDelegate
? new EventCallbackCommand(() => InvokeEventCallback(OnBackButtonPressed))
: null;
}
return true;
case nameof(TitleView):
Expand Down
2 changes: 1 addition & 1 deletion src/BlazorBindings.Maui/Elements/DatePicker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ void NativeControlDateSelected(object sender, MC.DateChangedEventArgs e)
{
var value = DateOnly.FromDateTime(NativeControl.Date);
Date = value;
DateChanged.InvokeAsync(value);
InvokeEventCallback(DateChanged, value);
}

DateChanged = (EventCallback<DateOnly>)value;
Expand Down
2 changes: 1 addition & 1 deletion src/BlazorBindings.Maui/Elements/Editor.generated.cs
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ protected override void HandleParameter(string name, object value)
case nameof(OnCompleted):
if (!Equals(OnCompleted, value))
{
void NativeControlCompleted(object sender, EventArgs e) => InvokeAsync(() => OnCompleted.InvokeAsync());
void NativeControlCompleted(object sender, EventArgs e) => InvokeEventCallback(OnCompleted);

OnCompleted = (EventCallback)value;
NativeControl.Completed -= NativeControlCompleted;
Expand Down
2 changes: 1 addition & 1 deletion src/BlazorBindings.Maui/Elements/Entry.generated.cs
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ protected override void HandleParameter(string name, object value)
case nameof(OnCompleted):
if (!Equals(OnCompleted, value))
{
void NativeControlCompleted(object sender, EventArgs e) => InvokeAsync(() => OnCompleted.InvokeAsync());
void NativeControlCompleted(object sender, EventArgs e) => InvokeEventCallback(OnCompleted);

OnCompleted = (EventCallback)value;
NativeControl.Completed -= NativeControlCompleted;
Expand Down
2 changes: 1 addition & 1 deletion src/BlazorBindings.Maui/Elements/FlyoutPage.generated.cs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ void NativeControlIsPresentedChanged(object sender, EventArgs e)
{
var value = NativeControl.IsPresented;
IsPresented = value;
InvokeAsync(() => IsPresentedChanged.InvokeAsync(value));
InvokeEventCallback(IsPresentedChanged, value);
}

IsPresentedChanged = (EventCallback<bool>)value;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ protected override void HandleParameter(string name, object value)
case nameof(OnInvalidateGradientBrushRequested):
if (!Equals(OnInvalidateGradientBrushRequested, value))
{
void NativeControlInvalidateGradientBrushRequested(object sender, EventArgs e) => InvokeAsync(() => OnInvalidateGradientBrushRequested.InvokeAsync());
void NativeControlInvalidateGradientBrushRequested(object sender, EventArgs e) => InvokeEventCallback(OnInvalidateGradientBrushRequested);

OnInvalidateGradientBrushRequested = (EventCallback)value;
NativeControl.InvalidateGradientBrushRequested -= NativeControlInvalidateGradientBrushRequested;
Expand Down
14 changes: 7 additions & 7 deletions src/BlazorBindings.Maui/Elements/GraphicsView.generated.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ protected override void HandleParameter(string name, object value)
case nameof(OnStartHoverInteraction):
if (!Equals(OnStartHoverInteraction, value))
{
void NativeControlStartHoverInteraction(object sender, MC.TouchEventArgs e) => InvokeAsync(() => OnStartHoverInteraction.InvokeAsync(e));
void NativeControlStartHoverInteraction(object sender, MC.TouchEventArgs e) => InvokeEventCallback(OnStartHoverInteraction, e);

OnStartHoverInteraction = (EventCallback<MC.TouchEventArgs>)value;
NativeControl.StartHoverInteraction -= NativeControlStartHoverInteraction;
Expand All @@ -58,7 +58,7 @@ protected override void HandleParameter(string name, object value)
case nameof(OnMoveHoverInteraction):
if (!Equals(OnMoveHoverInteraction, value))
{
void NativeControlMoveHoverInteraction(object sender, MC.TouchEventArgs e) => InvokeAsync(() => OnMoveHoverInteraction.InvokeAsync(e));
void NativeControlMoveHoverInteraction(object sender, MC.TouchEventArgs e) => InvokeEventCallback(OnMoveHoverInteraction, e);

OnMoveHoverInteraction = (EventCallback<MC.TouchEventArgs>)value;
NativeControl.MoveHoverInteraction -= NativeControlMoveHoverInteraction;
Expand All @@ -68,7 +68,7 @@ protected override void HandleParameter(string name, object value)
case nameof(OnEndHoverInteraction):
if (!Equals(OnEndHoverInteraction, value))
{
void NativeControlEndHoverInteraction(object sender, EventArgs e) => InvokeAsync(() => OnEndHoverInteraction.InvokeAsync());
void NativeControlEndHoverInteraction(object sender, EventArgs e) => InvokeEventCallback(OnEndHoverInteraction);

OnEndHoverInteraction = (EventCallback)value;
NativeControl.EndHoverInteraction -= NativeControlEndHoverInteraction;
Expand All @@ -78,7 +78,7 @@ protected override void HandleParameter(string name, object value)
case nameof(OnStartInteraction):
if (!Equals(OnStartInteraction, value))
{
void NativeControlStartInteraction(object sender, MC.TouchEventArgs e) => InvokeAsync(() => OnStartInteraction.InvokeAsync(e));
void NativeControlStartInteraction(object sender, MC.TouchEventArgs e) => InvokeEventCallback(OnStartInteraction, e);

OnStartInteraction = (EventCallback<MC.TouchEventArgs>)value;
NativeControl.StartInteraction -= NativeControlStartInteraction;
Expand All @@ -88,7 +88,7 @@ protected override void HandleParameter(string name, object value)
case nameof(OnDragInteraction):
if (!Equals(OnDragInteraction, value))
{
void NativeControlDragInteraction(object sender, MC.TouchEventArgs e) => InvokeAsync(() => OnDragInteraction.InvokeAsync(e));
void NativeControlDragInteraction(object sender, MC.TouchEventArgs e) => InvokeEventCallback(OnDragInteraction, e);

OnDragInteraction = (EventCallback<MC.TouchEventArgs>)value;
NativeControl.DragInteraction -= NativeControlDragInteraction;
Expand All @@ -98,7 +98,7 @@ protected override void HandleParameter(string name, object value)
case nameof(OnEndInteraction):
if (!Equals(OnEndInteraction, value))
{
void NativeControlEndInteraction(object sender, MC.TouchEventArgs e) => InvokeAsync(() => OnEndInteraction.InvokeAsync(e));
void NativeControlEndInteraction(object sender, MC.TouchEventArgs e) => InvokeEventCallback(OnEndInteraction, e);

OnEndInteraction = (EventCallback<MC.TouchEventArgs>)value;
NativeControl.EndInteraction -= NativeControlEndInteraction;
Expand All @@ -108,7 +108,7 @@ protected override void HandleParameter(string name, object value)
case nameof(OnCancelInteraction):
if (!Equals(OnCancelInteraction, value))
{
void NativeControlCancelInteraction(object sender, EventArgs e) => InvokeAsync(() => OnCancelInteraction.InvokeAsync());
void NativeControlCancelInteraction(object sender, EventArgs e) => InvokeEventCallback(OnCancelInteraction);

OnCancelInteraction = (EventCallback)value;
NativeControl.CancelInteraction -= NativeControlCancelInteraction;
Expand Down
Loading