// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.AspNetCore.Blazor.Components;
using Microsoft.AspNetCore.Blazor.RenderTree;
using System;
namespace Microsoft.AspNetCore.Blazor.Rendering
{
///
/// Tracks the rendering state associated with an instance
/// within the context of a . This is an internal implementation
/// detail of .
///
internal class ComponentState
{
private readonly int _componentId; // TODO: Change the type to 'long' when the Mono runtime has more complete support for passing longs in .NET->JS calls
private readonly IComponent _component;
private readonly Renderer _renderer;
private readonly RenderTreeDiffComputer _diffComputer;
private RenderTreeBuilder _renderTreeBuilderCurrent;
private RenderTreeBuilder _renderTreeBuilderPrevious;
///
/// Constructs an instance of .
///
/// The with which the new instance should be associated.
/// The externally visible identifer for the . The identifier must be unique in the context of the .
/// The whose state is being tracked.
public ComponentState(Renderer renderer, int componentId, IComponent component)
{
_componentId = componentId;
_component = component ?? throw new ArgumentNullException(nameof(component));
_renderer = renderer ?? throw new ArgumentNullException(nameof(renderer));
_diffComputer = new RenderTreeDiffComputer(renderer);
_renderTreeBuilderCurrent = new RenderTreeBuilder(renderer);
_renderTreeBuilderPrevious = new RenderTreeBuilder(renderer);
}
///
/// Regenerates the and adds the changes to the
/// .
///
public void Render(RenderBatchBuilder batchBuilder)
{
// Swap the old and new tree builders
(_renderTreeBuilderCurrent, _renderTreeBuilderPrevious) = (_renderTreeBuilderPrevious, _renderTreeBuilderCurrent);
_renderTreeBuilderCurrent.Clear();
_component.BuildRenderTree(_renderTreeBuilderCurrent);
_diffComputer.ApplyNewRenderTreeVersion(
batchBuilder,
_componentId,
_renderTreeBuilderPrevious.GetFrames(),
_renderTreeBuilderCurrent.GetFrames());
}
///
/// Invokes the handler corresponding to an event.
///
/// The index of the current render tree frame that holds the event handler to be invoked.
/// Arguments to be passed to the event handler.
public void DispatchEvent(int renderTreeIndex, UIEventArgs eventArgs)
{
if (eventArgs == null)
{
throw new ArgumentNullException(nameof(eventArgs));
}
var frames = _renderTreeBuilderCurrent.GetFrames();
var eventHandler = frames.Array[renderTreeIndex].AttributeValue as UIEventHandler;
if (eventHandler == null)
{
throw new ArgumentException($"The render tree frame at index {renderTreeIndex} does not specify a {nameof(UIEventHandler)}.");
}
eventHandler.Invoke(eventArgs);
// After any event, we synchronously re-render. Most of the time this means that
// developers don't need to call Render() on their components explicitly.
_renderer.RenderNewBatch(_componentId);
}
///
/// Notifies the component that it is being disposed.
///
public void NotifyDisposed()
{
// TODO: Handle components throwing during dispose. Shouldn't break the whole render batch.
if (_component is IDisposable disposable)
{
disposable.Dispose();
}
}
}
}