I am working on a layout designer, think of WinForms Designer for Web-Components, and am at the very beginning of the project. I use Blazor Server App, and, as I need to generically generate controls, am using DynamicComponents. As I don't want too much complexity within the actual layout components, I've got a base class which takes care of most common things. Additionally, I have created a Selectable node to allow detection of component selection without the need to implement that in every component.
When a component is now selected, I see that the delete icon is not getting enabled but its disabled/enabled state is correctly set by the code.
I could, however, call StateHasChanged and that would show the correct state but unfortunately lead to other issues, most likely related to the DynamicComponents being used.
I was able to reproduce my problem with a very simple example which can be found here:https://try.mudblazor.com/snippet/QYcIuybXTEvcExgs
I will try to outline the code here as well:
@inherits LayoutComponentBase<PageTitle>BlazorRepro</PageTitle>@Counter<Container @ref="@container"><div> @Body</div></Container>@code { Container? container; int Counter { get; set; } protected override void OnAfterRender(bool firstRender) { base.OnAfterRender(firstRender); if (container != null && firstRender) container.SelectionChanged += OnSelectionChanged; } private void OnSelectionChanged() { System.Diagnostics.Trace.WriteLine($"Counter incremented to : {++Counter}"); }}That's the main component. It generates a Container instance which, in turn, will provide a CascadingValue to all children. It also registers for the SelectionChanged event and increments a counter when ever something is selected (= Enabling/Disabling of my delete button)
<div class="container"><CascadingValue Value="this"> @ChildContent</CascadingValue></div>@code { [Parameter] public RenderFragment? ChildContent { get; set; } public event Action? SelectionChanged; public void Select() { SelectionChanged?.Invoke(); }}Container implementation. As mentioned, it provides a CascadingValue and also a public method to be able to be notified about a new selection.
<div @onclick="Select"> @ChildContent</div>@code { [CascadingParameter] public Container Container { get; set; } [Parameter] public RenderFragment ChildContent { get; set; } private void Select() { Container.Select(); }}The Selectable implementation. When ever something is clicked, the Container.Select() method is invoked.
@page "/"<PageTitle>Index</PageTitle><Selectable><h1>Click me to select</h1></Selectable>Finally, the body of the MainLayout - It just wraps a selectable text.
If 'Click me to select' is clicked, I would expect the counter on the main layout to be incremented but unfortunately, that only happens if the event handler of the main layout is also calling StateHasChanged.
I am not exactly sure where the problem comes from but I know, that that kind of binding should work in general.
Thank you in advance for your help and suggestions!