【问题标题】:Blazor Howto - looking for winform MessageBox() alike functionBlazor Howto - 寻找 winform MessageBox() 类似的功能
【发布时间】:2021-08-12 07:10:31
【问题描述】:

我正在使用 VS 2019 和 Blazor Server Net5。包含的 bootstrap 4 环境提供了模式对话框,我想从旧的 Windows 窗体中复制 MessageBox() 之类的东西。这意味着您执行(在按钮单击事件中)类似

DialogResult x = [await] MessageBox[Async]("SomeMsg", SomeButtons+SomeIcon);

在 x 中,您会发现用户单击了哪个选项(DialogResult.OK、Cancel、...)。

所以我找到了几个示例,如何显示模型对话框本身,甚至将其编写为组件。

我有一个简单的组件 MessageBox.razor

    public enum ModalResultType { Closed = 0, OK = 1, Cancel = 2, }
    
    @if (ShowMessageBox == true)
    {
       <div class="modal fade show d-block" id="MessageBox" tabindex="-1"
          role="dialog" aria-hidden="true">
       .... and so forth ....
       <button type="button" class="close" data-dismiss="modal" aria-label="Close"
          @onclick="() => OnButtonClick(ModalResultType.Closed)">X</button>
       ... and so forth ...
       <button type="button" class="btn btn-primary" data-dismiss="modal"
          @onclick="() => OnButtonClick(ModalResultType.OK)">OK</button>
       <button type="button" class="btn btn-secondary" data-dismiss="modal"
          @onclick="() => OnButtonClick(ModalResultType.Cancel)">Cancel</button>
       .. and so forth ...
       </div>
    }

在cs后台文件中我可以打开组件显示标志并显示组件。

    public async Task<ModalResultType> ShowAsync(string title, string messagetext)
    {
       Title = title;
       Message = messagetext;
       ShowMessageBox = true;
       StateHasChanged();
       //
       // Now I m at a loss... how to await here what the User did click???
       //
       return whatTheUserDidClick;
    }

    // Click event from button, called with the appropiate ModalResultType
    //
    public async Task OnButtonClicked(ModalResultType value)
    {
       ShowMessageBox = false;
       //
       // Now I am at a  loss - how to pass the clicked value into the waiting context
       // of the UI above from and "complete" the awaiting ShowAsync();
       //
    }

总体思路是将这个 MessageBox 组件放入框架 App.Razor-Component 中,因此每个“页面”或其他组件都可以(通过级联参数)访问消息框。如果它需要创建一个模态 MessageBox 对话框,例如从某个按钮单击事件,它可以通过调用来简单地做到这一点

    [CascadingParameter] public MessageBoxComponent AppRazorMessageComonent {get;set;}
    
    public async Task SomeClickEvent()
    {
       // get some form data
       // process them
       // question arises ask user if to proceed or defer
       if (await AppRazorMessageComponent.ShowAsync("Attention", "Shall we proceed?") == ModalResultType.OK)
       {
          // do stuff
       }
       else
       {
          // do other stuff
       }
    }

我找到了模态对话框示例,其中事件处理程序被绑定到要执行的操作 - 例如删除记录。但这不是我想要的——我需要始终将 html 专门绑定到我当时所在的页面或组件的细节上。或者我需要提供一个回调函数,这会破坏我当前的轨道;喜欢设置 ShowMessageBox 标志,从点击事件返回,然后在另一个方法中继续进行逻辑。

所以问题是:如何在事件处理程序中等待由其他 UI 事件触发的事件?

我需要线程吗?我不这么认为。应该只能通过 Task、async 和 await 来实现。但是如何创建一个等待对象,“发出”这样一个任务完成或取消的“信号”呢?并且以某种方式在 Blazor UI 组件环境中工作。

【问题讨论】:

  • 这是每个 Blazor 组件框架的一项功能。 Here是一个简单的DIY。

标签: asynchronous events async-await blazor


【解决方案1】:

我利用System.Threading.SemaphoreSlim 类在Modal.cs 中实现ValueTask&lt;ModalResult&lt;T&gt;&gt; OpenModal() 中的等待结果

BlazorRepl

ModalLauncher.razor

<CascadingValue Value="this">
    @if (ModalContent is not null)
    {
        @ModalContent
    }
    @ChildContent
</CascadingValue>

ModalLauncher.razor.cs

using Microsoft.AspNetCore.Components;

public partial class ModalLauncher : ComponentBase
{
    [Parameter]
    public RenderFragment ChildContent { get; set; }

    public RenderFragment ModalContent { get; set; }

    public void ShowModal(RenderFragment renderFragment)
    {
        ModalContent = renderFragment;
        StateHasChanged();
    }

    public void CloseModal()
    {
        ModalContent = null;
        StateHasChanged();
    }
}

将它包裹在您的布局周围。

@inherits LayoutComponentBase
<ModalLauncher>
    <div class="page">
        ...
    </div>
</ModalLauncher>

Modal.cs

public class Modal<T> : ComponentBase
{
    [Parameter]
    public RenderFragment<ModalContext<T>> ChildContent { get; set; }

    [Parameter]
    public T Value { get; set; }

    [CascadingParameter]
    public ModalLauncher Launcher { get; set; }

    public async ValueTask<ModalResult<T>> OpenModal(T value)
    {
        var modalContext = new ModalContext<T> { Modal = this, Value = value };
        RenderFragment renderFragment = ChildContent.Invoke(modalContext);
        Launcher.ShowModal(renderFragment);
        await semaphore.WaitAsync();
        return new ModalResult<T> { ModalAction = modalAction, Value = value };
    }

    public void CancelModal() => CloseModal(ModalAction.Cancel);
    public void CloseModal() => CloseModal(ModalAction.Close);
    public void OkModal() => CloseModal(ModalAction.Ok);

    private void CloseModal(ModalAction action)
    {
        modalAction = action;
        Launcher.CloseModal();
        semaphore.Release();
    }

    private ModalAction modalAction;
    private SemaphoreSlim semaphore = new SemaphoreSlim(0, 1);
}
public enum ModalAction
{
    Cancel,
    Close,
    Ok,
}

public class ModalContext<T>
{
    public T Value { get; set; }
    public Modal<T> Modal { get; set; }
}
public class ModalResult<T>
{
    public T Value { get; set; }
    public ModalAction ModalAction { get; set; }
}

public class SomeClass
{
    public int SomeValue { get; set; }
}

用法 注意:当我定义模态时,我只使用一个类型,它们没有绑定到一个实例。当您调用OpenModal(...) 时,您可以传递一个实例。

@page "/"

<button @onclick="@OpenSomeClassModal">Run Demo</button>

<Modal @ref="someClassModal" T="SomeClass">
    ...   
    <input type="number" @bind-value="@context.Value.SomeValue" />
    ...
    <button type="button" class="btn btn-secondary" @onclick="@context.Modal.CancelModal">Cancel</button>
    <button type="button" class="btn btn-primary" @onclick="@context.Modal.OkModal">Save changes</button>
    ...             
</Modal>

<Modal @ref="someStringModal" T="string">
    ...
    <p> @context.Value</p>
    ...
    <button type="button" class="btn btn-secondary" @onclick="@context.Modal.OkModal">Close</button>
    ...
</Modal>


@code {
    Modal<SomeClass> someClassModal;
    Modal<string> someStringModal;

    async Task OpenSomeClassModal()
    {
        var someClass = new SomeClass { SomeValue = 9 };
        var result1 = await someClassModal.OpenModal(someClass);
        var result2 = await someStringModal.OpenModal($"The value was set to {result1.Value.SomeValue}, you pressed {result1.ModalAction}");
    }
}

您还需要覆盖引导程序.modal 类的一部分。把这个放在wwwroot\css\app.css:

.modal {
    display: block;
}

【讨论】:

猜你喜欢
  • 2019-10-20
  • 1970-01-01
  • 1970-01-01
  • 2014-12-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-12
  • 1970-01-01
相关资源
最近更新 更多