I created my own modal dialog component to show a modal component on top the main page's content. The dialog's content is renderd as a RenderFragment inside its main <div> section and created on the fly.
I sometimes show another dialog on top of the already existing one - I store the actually rendered RenderFragment and replace it with the new one - so I can go back to the previous dialog once the top-most is closed.
However, when I re-inject the stored component, the component is partially destroyed: some sub-components are missing or re-initialized (e.g. grid columns or data bindings are lost if Content contains a grid). Or if I register events in the component via the attributes, the seem not to operate on the newly displayed component...
Here the code structure:
<div class="modal-body mod-dlg-content" Id="_ModalBody_">
@Content
</div>
@code {
public async Task ShowModal(Type contentType, Dictionary<string, object> attributes) {
// -- if already showing a modal windows => store content (restored when closing new window)
await StoreCurrentView();
// --- create instance...
Content = new RenderFragment(
x => {
x.OpenComponent(0, contentType);
int iCount = 1;
if (attributes != null) {
foreach (var attr in attributes) {
x.AddAttribute(iCount, attr.Key, attr.Value);
++iCount;
}
}
x.AddComponentReferenceCapture(iCount, inst => { ContentRef = (ComponentBase)inst; });
x.CloseComponent();
});
StateHasChanged();
}
// --- called when user closes the component
public async Task OnClose() {
await RestoreCurrentView();
// --- set to hidden if content is empty here...
StateHasChanged();
}
// ---- internal class to store modal content
public class ModalContent {
protected RenderFragment Content;
protected ComponentBase ContentRef;
public void RestoreView(ModalDialog dlg) {
dlg.Content = this.Content;
dlg.ContentRef = this.ContentRef;
}
}
protected RenderFragment Content { get; set; }
protected ComponentBase ContentRef;
List<ModalContent> _ModalStack = new List<ModalContent>();
// --- called when showing a modal component on top of the actual one
async Task StoreCurrentView() {
await SomeStuff();
var modalContent = new ModalContent();
modalContent.ContentRef = dlg.ContentRef;
modalContent.modalDisplay = dlg.modalDisplay;
_ModalStack.Add(modalContent);
}
// --- called when closing a modal window
async Task RestoreCurrentView() {
await SomeStuff();
int lastId = _ModalStack.Count - 1;
if (lastId >= 0) {
ModalContent restore = _ModalStack[lastId];
_ModalStack.RemoveAt(lastId);
restore.RestoreView(this);
}
else {
// --- clear previous...
Content = null;
ContentRef = null;
}
}
}
All works seamlessly prior to storing the view, but after restoring it, although display looks fine, events and bindings are messed up!
Why would my components be re-initialized or re-created? All bindings are at the Content level so I would expect them not be destroyed... Or is there some specific Blazor visual component GC involved here?