using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components.Rendering; namespace Sandbox.UI; public partial class Panel { /// /// true when the tree should be re-rendered next frame. /// bool razorTreeDirty = true; string razorLastTreeChecksum = null; [Parameter] public RenderFragment ChildContent { get; set; } /// /// For razor panels, call when the state of the render tree has changed such that /// it would be a good idea to re-render the tree. You would usually not need to call /// this manually. /// public void StateHasChanged() { ThreadSafe.AssertIsMainThread(); razorTreeDirty = true; } /// /// Overridden/implemented by Razor templating, contains render tree checksum to determine when the render tree content has changed. /// protected virtual string GetRenderTreeChecksum() => ChildContent == null ? null : "1"; internal bool HasRenderTree => GetRenderTreeChecksum() is not null; PanelRenderTreeBuilder renderTree; /// /// Overridden/implemented by Razor templating to build a render tree. /// protected virtual void BuildRenderTree( RenderTreeBuilder tree ) { ChildContent?.Invoke( tree ); } int previousHash; /// /// By overriding this you can return a hash of variables used by the Razor layout, which /// will cause a rebuild when changed. This is useful when your layout uses a global variable /// because by adding it to a HashCode.Combine here you can easily trigger a build when it changes. /// protected virtual int BuildHash() { return 0; } /// /// A RenderFragment has been set on us, so our tree has potential changes now. /// Lets update and see. /// public void OnRenderFragmentChanged( Panel upTo ) { if ( upTo == this ) return; razorTreeDirty = true; Parent?.OnRenderFragmentChanged( upTo ); } internal void InternalTreeBinds() { if ( renderTree == null ) return; try { var hash = BuildHash(); if ( previousHash != hash ) { previousHash = hash; razorTreeDirty = true; } } catch ( System.Exception e ) { Log.Warning( e, $"Error calculating hash on {GetType()} - {e.Message}" ); } if ( renderTree.UpdateBinds() ) { razorTreeDirty = true; } } /// /// Allows building render tree from outside of the class. /// internal RenderTreeBuilder InternalRenderTree() { if ( !HasRenderTree ) return null; razorTreeDirty = false; renderTree ??= new PanelRenderTreeBuilder( this ); razorLastTreeChecksum = GetRenderTreeChecksum(); renderTree.Start(); try { BuildRenderTree( renderTree ); } catch ( System.Exception e ) { Log.Warning( e, $"Error when building render tree on {GetType()} - {e.Message}" ); } renderTree.Finish(); return renderTree; } /// /// Called after the razor tree has been created/rendered. /// protected virtual void OnAfterTreeRender( bool firstTime ) { } /// /// Delete all children generated by the Razor render tree. /// internal void ClearRenderTree() { renderTree?.Clear(); renderTree = null; } }