这个问题有一个简单的答案,或者一个更长更全面的答案,演示如何构建一个“基本”模态对话框框架。这是后者。
您需要将 Modal Dialog 与 EditForm 分开。您应该能够在模态对话框中托管任何表单。
首先,定义一个
IModalDialog
接口,这样我们就可以拥有多个模态对话框的实现 - 比如一个简单的干净的 Css 或 Bootstrap 。
public interface IModalDialog
{
public ModalRequest ModalRequest { get; }
public bool IsActive { get; }
public bool Display { get; }
public Task<ModalResult> ShowAsync<TModal>(ModalRequest modalRequest) where TModal : IComponent;
public Task<bool> SwitchAsync<TModal>(ModalRequest modalRequest) where TModal : IComponent;
public void Update(ModalRequest? modalRequest);
public void Dismiss();
public void Close(ModalResult result);
}
我们传入一个
ModalRequest
:
public record ModalRequest
{
public IDictionary<string, object> Parameters { get; init; } = new Dictionary<string, object>();
public object? InData { get; init; } = null;
}
然后回来
ModalResult
public class ModalResult
{
public ModalResultType ResultType { get; private set; } = ModalResultType.NoSet;
public object? Data { get; set; } = null;
public static ModalResult OK() => new ModalResult() { ResultType = ModalResultType.OK };
public static ModalResult Exit() => new ModalResult() { ResultType = ModalResultType.Exit };
public static ModalResult Cancel() => new ModalResult() { ResultType = ModalResultType.Cancel };
public static ModalResult OK(object data) => new ModalResult() { Data = data, ResultType = ModalResultType.OK };
public static ModalResult Exit(object data) => new ModalResult() { Data = data, ResultType = ModalResultType.Exit };
public static ModalResult Cancel(object data) => new ModalResult() { Data = data, ResultType = ModalResultType.Cancel };
public enum ModalResultType { NoSet, OK, Cancel, Exit }
}
我们的基本实现 - ModalDialog.razor。它使用 TaskCompletionSource
在
async上下文中运行。您可以通过调用
ShowAsync<TModal>
将 TModal
设置为要在表单中托管的组件并通过 ModalRequest
实例提供设置和数据来打开对话框。然后,您等待退出表单时设置为完成的提供 Task
。我们使用 DynamicComponent
来创建 TModal
。请注意,我们将 Modal 实例级联到托管组件。
@implements IModalDialog
@if (this.Display)
{
<CascadingValue Value="(IModalDialog)this">
<div class="base-modal-background" @onclick="OnBackClick">
<div class="base-modal-content" @onclick:stopPropagation="true">
<DynamicComponent Type=this.ModalContentType Parameters=this.ModalRequest.Parameters />
</div>
</div>
</CascadingValue>
}
@code {
[Parameter] public bool ExitOnBackGroundClick { get; set; } = false;
public ModalRequest ModalRequest { get; private set; } = new ModalRequest();
public object? InData { get; } = null;
public bool Display { get; protected set; } = false;
protected TaskCompletionSource<ModalResult> _ModalTask { get; set; } = new TaskCompletionSource<ModalResult>();
protected Type? ModalContentType = null;
public bool IsActive
=> this.ModalContentType is not null;
public Task<ModalResult> ShowAsync<TModal>(ModalRequest modalRequest) where TModal : IComponent
{
this.ModalRequest = modalRequest;
this.ModalContentType = typeof(TModal);
this._ModalTask = new TaskCompletionSource<ModalResult>();
this.Display = true;
InvokeAsync(StateHasChanged);
return this._ModalTask.Task;
}
public async Task<bool> SwitchAsync<TModal>(ModalRequest modalRequest) where TModal : IComponent
{
this.ModalRequest = modalRequest;
this.ModalContentType = typeof(TModal);
await InvokeAsync(StateHasChanged);
return true;
}
public void Update(ModalRequest? modalRequest = null)
{
this.ModalRequest = modalRequest ?? this.ModalRequest;
InvokeAsync(StateHasChanged);
}
private void OnBackClick(MouseEventArgs e)
{
if (ExitOnBackGroundClick)
this.Close(ModalResult.Exit());
}
public async void Dismiss()
=> await this.Reset(ModalResult.Cancel());
public async void Close(ModalResult result)
=> await this.Reset(result);
private async Task Reset(ModalResult result)
{
_ = this._ModalTask.TrySetResult(ModalResult.Cancel());
this.Display = false;
this.ModalContentType = null;
await InvokeAsync(StateHasChanged);
}
}
它是组件 css - ModalDialog.razor.css
div.base-modal-background {
display: block;
position: fixed;
z-index: 101; /* Sit on top */
left: 0;
top: 0;
width: 100%; /* Full width */
height: 100%; /* Full height */
overflow: auto; /* Enable scroll if needed */
background-color: rgb(0,0,0); /* Fallback color */
background-color: rgba(0,0,0,0.4); /* Black w/ opacity */
}
div.base-modal-content {
background-color: #fefefe;
margin: 10% auto;
padding: 10px;
border: 2px solid #888;
width: 90%;
}
您的数据类别:
public class MyData
{
public int Min { get; set; }
public int Max { get; set; }
}
和表单 - MyForm.razor。这将捕获级联
IModalDialog
并与 IModalDialog
交互以获取任何提供的数据并关闭对话框。
<div class="modal-title border-bottom border-secondary">
<h5>@this.Title</h5>
</div>
<div class="modal-body">
<label class="form-label" for="Min">Enter Min [mm]</label>
<input class="form-control" @bind=model.Min type="number">
<label class="form-label" for="Max">Enter Max [mm]:</label>
<input class="form-control" @bind=model.Max type="number">
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary" @onclick="() => Done()">Done</button>
</div>
@code {
private MyData model = new MyData();
[Parameter] public string Title { get; set; } = "I Need a Title!";
[CascadingParameter] private IModalDialog? modalDialog { get; set; }
private ModalRequest modalRequest
=> modalDialog?.ModalRequest ?? new ModalRequest();
protected override void OnInitialized()
{
if (modalDialog is null)
throw new NullReferenceException("You must cascade a IModalDialog to use this Form");
model = (MyData)(modalDialog?.ModalRequest.InData ?? new MyData());
}
private void Done()
=> modalDialog?.Close(ModalResult.OK(model));
}
最后是演示页面。它托管
ModalDialog
组件并与其交互以打开对话框。
@page "/"
<PageTitle>Modal Dialog Demo</PageTitle>
<div class="m-2 b-2">
<button class="btn btn-primary" @onclick=OpenDialog1>Edit Model 1</button>
</div>
<div class="alert alert-primary">
<strong>Model 1</strong> Min: @this.model1.Min Max: @this.model1.Max
</div>
<div class="m-2 b-2">
<button class="btn btn-dark" @onclick=OpenDialog2>Edit Model 2</button>
</div>
<div class="alert alert-dark">
<strong>Model 2</strong> Min: @this.model2.Min Max: @this.model2.Max
</div>
<ModalDialog @ref=modalDialog ExitOnBackGroundClick=false />
@code {
private MyData model1 = new MyData { Max = 20, Min = -10 };
private MyData model2 = new MyData { Max = 50, Min = -50 };
private IModalDialog? modalDialog;
private async Task OpenDialog1()
{
var parameters = new Dictionary<string, object> { { "Title", "Modal Form 1" } };
var request = new ModalRequest { InData = this.model1, Parameters = parameters };
if (this.modalDialog is not null)
await modalDialog.ShowAsync<MyForm>(request);
// This won't complete until the dialog closes and the Task is complete.
// We can use any return data at this point
// and this component will render as part of the ComponentBase UI event handling code.
}
private async Task OpenDialog2()
{
var parameters = new Dictionary<string, object> { { "Title", "Modal Form 2" } };
var request = new ModalRequest { InData = this.model2 };
if (this.modalDialog is not null)
await modalDialog.ShowAsync<MyForm>(request);
}
}
这是其中一个对话框的屏幕截图:
代码暂时放在这里 - https://github.com/ShaunCurtis/SO73617831
我知道这已经有几年了,但我喜欢你在这里所做的概念。我在使用示例时注意到的一件事是 InData 被修改,这在某些情况下可能不是所需的结果。我知道这可以在派生对话框中处理。我还注意到上面示例中的 Reset(ModelResult result) 始终返回 ModalResult.Cancel。那应该是结果参数吗?由于您的临时存储库已消失,我看不到您是否进行了其他更改。