Skip to content

Commit

Permalink
fix: Make sure the Loading event is not raised twice
Browse files Browse the repository at this point in the history
  • Loading branch information
Dr.Rx committed Sep 16, 2020
1 parent 9f39e0d commit bb58a81
Show file tree
Hide file tree
Showing 2 changed files with 63 additions and 37 deletions.
46 changes: 14 additions & 32 deletions src/Uno.UI/UI/Xaml/FrameworkElement.netstd.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,8 @@ public partial class FrameworkElement : IEnumerable
{
public new bool IsLoaded => base.IsLoaded; // The IsLoaded state is managed by the UIElement, FrameworkElement only makes it publicly visible

private protected override sealed void OnElementLoading(int depth)
private protected sealed override void OnFwEltLoading()
{
base.IsLoading = true;

OnLoadingPartial();
ApplyCompiledBindings();

Expand All @@ -44,50 +42,34 @@ private protected override sealed void OnElementLoading(int depth)
}

OnPostLoading();

// Explicit propagation of the loading even must be performed
// after the compiled bindings are applied (cf. OnLoading), as there may be altered
// properties that affect the visual tree.
base.OnElementLoading(depth);
}

partial void OnLoadingPartial();
private protected virtual void OnLoading() { }
private protected virtual void OnPostLoading() { }

private protected override sealed void OnElementLoaded()
private protected sealed override void OnFwEltLoaded()
{
if (!base.IsLoaded)
{
// Make sure to set the flag before raising the loaded event (duplicated with the base.OnLoaded)
base.IsLoaded = true;
base.IsLoading = false;

OnLoadedPartial();
OnLoadedPartial();

try
{
// Raise event before invoking base in order to raise them top to bottom
OnLoaded();
_loaded?.Invoke(this, new RoutedEventArgs(this));
}
catch (Exception error)
{
_log.Error("OnElementLoaded failed in FrameworkElement", error);
Application.Current.RaiseRecoverableUnhandledException(error);
}
try
{
// Raise event before invoking base in order to raise them top to bottom
OnLoaded();
_loaded?.Invoke(this, new RoutedEventArgs(this));
}
catch (Exception error)
{
_log.Error("OnElementLoaded failed in FrameworkElement", error);
Application.Current.RaiseRecoverableUnhandledException(error);
}

base.OnElementLoaded();
}

partial void OnLoadedPartial();
private protected virtual void OnLoaded() { }

private protected override sealed void OnElementUnloaded()
private protected sealed override void OnFwEltUnloaded()
{
base.OnElementUnloaded(); // Will set flag IsLoaded to false

try
{
// Raise event after invoking base in order to raise them bottom to top
Expand Down
54 changes: 49 additions & 5 deletions src/Uno.UI/UI/Xaml/UIElement.netstd.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,20 @@ public partial class UIElement : DependencyObject

// Even if this a concept of FrameworkElement, the loaded state is handled by the UIElement in order to avoid
// to cast to FrameworkElement each time a child is added or removed.
internal bool IsLoaded { get; private protected set; }
#if __WASM__
internal bool IsLoaded { get; private protected set; } // protected for the native loading support
#else
internal bool IsLoaded { get; private set; }
#endif

/// <summary>
/// This flag is transiently set while element is 'loading' but not yet 'loaded'.
/// </summary>
internal bool IsLoading { get; private protected set; }
#if __WASM__
internal bool IsLoading { get; private protected set; } // protected for the native loading support
#else
internal bool IsLoading { get; private set; }
#endif

private protected int Depth { get; private set; } = int.MinValue;

Expand All @@ -49,37 +57,73 @@ internal static void RootElementLoaded(UIElement visualTreeRoot)
internal static void RootElementUnloaded(UIElement visualTreeRoot)
=> visualTreeRoot.OnElementUnloaded();

private protected virtual void OnElementLoading(int depth)
// Overloads for the FrameworkElement to raise the events
// (Load/Unload is actually a concept of the FwElement, but it's easier to handle it directly from the UIElement)
private protected virtual void OnFwEltLoading() { }
private protected virtual void OnFwEltLoaded() { }
private protected virtual void OnFwEltUnloaded() { }

private void OnElementLoading(int depth)
{
if (IsLoading || IsLoaded)
{
// Note: If child is added while in parent's Laoding handler, we might get a double Loading!
return;
}

IsLoading = true;
Depth = depth;

OnFwEltLoading();

// Explicit propagation of the loading even must be performed
// after the compiled bindings are applied (cf. OnLoading), as there may be altered
// properties that affect the visual tree.
foreach (var child in _children)
{
child.OnElementLoading(depth + 1);
}
}

private protected virtual void OnElementLoaded()
private void OnElementLoaded()
{
if (IsLoaded)
{
return;
}

if (!IsLoading && _log.IsEnabled(LogLevel.Error))
{
_log.Error($"Element {this} is being loaded while not in loading state");
}

IsLoading = false;
IsLoaded = true;

OnFwEltLoaded();

foreach (var child in _children)
{
child.OnElementLoaded();
}
}

private protected virtual void OnElementUnloaded()
private void OnElementUnloaded()
{
if (!IsLoaded)
{
return;
}

IsLoaded = false;
Depth = int.MinValue;

foreach (var child in _children)
{
child.OnElementUnloaded();
}

OnFwEltUnloaded();
}

private void OnAddingChild(UIElement child)
Expand Down

0 comments on commit bb58a81

Please sign in to comment.