Skip to content

Commit

Permalink
Handle EventCallback exceptions via Renderer (#78)
Browse files Browse the repository at this point in the history
  • Loading branch information
Dreamescaper authored Nov 9, 2022
1 parent 8d854ad commit 6b06da5
Show file tree
Hide file tree
Showing 46 changed files with 195 additions and 113 deletions.
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

0 comments on commit 6b06da5

Please sign in to comment.