【问题标题】:How to handle large number of "dialogs" without navigating away from the root component?如何在不离开根组件的情况下处理大量“对话框”?
【发布时间】:2022-10-06 17:54:27
【问题描述】:

拥有一个现有的 WinForms 应用程序,我正在使用 C# Blazor WebAssembly 迁移到 .NET 6。

在这个 WinForms 应用程序中,有一个主窗口,假设大约有 50 个单独的模式对话框窗口(有时又显示其他模式对话框窗口)。

WinForms 中的步骤

在 WinForms 中显示对话框非常简单:

  1. 用户单击主菜单项“编辑此内容”。
  2. 调用了 C# 点击处理程序。
  3. 在这个点击处理程序中,我创建了我的对话框类的一个实例。
  4. 正在显示实例。
  5. 当用户关闭对话框时,该类的实例超出范围,稍后由 GC 自动清理。

    Blazor 中的步骤

    目前,我无法思考如何将这个概念适应 WASM Blazor 世界。

    在我的计划中,我有一个单一的“根”组件(“页面”),它在用户与应用程序交互的整个过程中都不会改变。这相当于 WinForms 中的“主窗口”概念。

    对于每个以前的 WinForms 对话框窗口,我计划创建一个 Blazor 组件(每个组件都有一个 DxPopup component,它将表单控件显示为一个对话框)。

    但我不确定这些组件的放置位置和方式。

    问题

    • 我真的应该在根组件中放置 50 多个组件并根据需要显示/隐藏它们吗?
    • 我应该有一个DynamicComponent component,通过代码告诉我要渲染哪个实际组件吗?
    • 还有其他选择吗?

    当我真正真正需要的只是一次一个组件时,我担心在提前用这么多组件污染组件树时得到horrible performance。

  • 如果您正在寻找性能,则显示/隐藏方法有效。只要您禁用未使用的组件并且没有内存问题,拥有 50 个组件就不是问题。
  • 您可能会发现这个库很有用:github.com/Blazored/Modal。您可以通过传递组件类型来打开一个对话框,例如ModalService.Show<MyDialog>(); 这样您就不必添加隐藏在根页面中的所有对话框。
  • 谢谢,@jdweng。就即时创建组件而言,当我真正需要它时(与将它们全部预先隐藏到我的根组件中相反);这可能并被视为良好做法吗?
  • JUST IN TIME 没有任何问题。一开始就全部打开只会使应用程序在启动时花费更长的时间。如果您只需要少量对话框,为什么要打开您不需要的东西。创建后,您可以简单地隐藏,以防您再次需要该对话框。只要确保代码有良好的文档记录,以便其他开发人员知道你做了什么。

标签: c# .net blazor devexpress blazor-webassembly


【解决方案1】:

正如我在 cmets 中提到的,Blazored.Modal 是一个非常强大的库,用于显示对话框 just-in-time,方法是在组件中注入 IModalService,由 Chris Sainty 创建。示例用法:

@inject IModalService Modal

<button @onclick="ShowFormBtnClicked">Show form</button>

@code {
    void ShowFormBtnClicked()
    {
        // FormDialog is a different component (FormDialog.razor).
        Modal.Show<FormDialog>("Form dialog title");
    }
}

您还可以将参数传递给对话框并从中接收结果(例如确认对话框)。

Documentation

这样,每个对话框都是一个单独的组件,仅在需要的地方和时间加载。使用这个库,您可以避免将所有模式放在组件中,如下所示:

<Modal @bind-Visible="_modal1Visible">...</Modal>
<Modal @bind-Visible="_modal2Visible">...</Modal>
<Modal @bind-Visible="_modal3Visible">...</Modal>
...
...

@code {
    private bool _modal1Visible;
    private bool _modal2Visible;
    private bool _modal3Visible;
    ...
}

【讨论】:

    【解决方案2】:

    我相信好的做法是只在需要时渲染。

    当涉及到基于组件的框架时,您需要尽可能地利用您的组件。动态组件对此很了不起,并且可以真正提高开发速度。

    我建议不要使用对话框/模式,酒吧特殊场合。我看到它被大量使用,坦率地说,我认为它不利于用户体验和性能。仅渲染当时需要的内容,尤其是在 WASM 中,因为客户端正在执行所有 UI 计算。

    在您的实例中,我建议您在处理此应用程序时从基础开始,如果正确完成,此基础将使您的开发速度提高 10 倍。

    需要放入文本框吗?创建一个组件,这样做您将更快地了解 Blazor 的来龙去脉。您还将意识到该做什么和不该做什么。

    好的,现在你有一个文本框,接下来是一个包含文本框的表单项。为表单项创建一个组件。

    现在您可以将文本框包装在表单项中。所以以后,如果你需要改变任何东西,无论是逻辑还是 CSS,你只需要更新一个组件。

    从那里扩展您的组件库,并以此了解您对 Blazor 的理解。

    现在到手头的主题: 隐藏不使用的东西是件好事。将代码块包装在 if 语句中,并在需要时呈现它们。也没有理由不导航到新页面。如果您在单个页面组件中有很多事情要做,我建议将您拥有的内容拆分为更小的组件。它更容易完成,也更容易维护。

    在一天结束时,您可以拥有:

    @if (_showForm1) {
        <MyFormComponent Heading="Form 1">
            <MyFormItem Label="Value 1">
                <MyInputText @bind-Value="@form1.Value1" />
            </MyFormItem>
        </MyFormComponent>
    }
    

    重要的提示:并非每个 Blazor 组件库都是好的。我遇到了MatBlazor,我对这个库中糟糕的实现感到惊讶。这是一个过于复杂的混乱。它在 GitHub 上是开源的,所以你可以看看。

    让你所做的一切尽可能简单。不要让一个组件成为万事通。您的组件必须非常具体,并以性能为重点。如果通用组件具有特定的焦点,它就不是万能的组件。例如,数值的输入组件可以通用以支持int、long、decimal 等。但是可以与string、DateTime 和int 一起使用的输入组件是所有交易。这应该很明显......但我已经看到了一些东西。

    【讨论】:

      【解决方案3】:

      这是我旅程的后续。我仔细查看了推荐的Blazor.Modal,但最初不喜欢他们自己的对话框 HTML 标记。

      所以我开始 asking a similar question in the DevExpress support center 如何使用 DxPopup 做与 Blazored.Modal 相同的事情。我确实得到了一些相当不错的回复,基本上是支持人员为我提供了一个完整的解决方案,告诉我如何做类似于 Blazored.Modal 所做的事情。

      后来,我发现 Blazored.Modal 可以让你用自定义的 UI 完全替换他们的 UI(例如 file 1 和 file 2)。

      所以我现在这样做:使用 Blazored.Modal 并通过 DxPopup 提供我自己的 UI。

      例子

      文件“UweKeimPopupText.razor”:

      <DxPopup Visible="true"
               ShowHeader="true"
               ShowFooter="true"
               HeaderText="Edit element">
          <BodyTemplate>
              <div class="p-3" style="height: 200px">
                  <p>@Message</p>
              </div>
          </BodyTemplate>
          <FooterContentTemplate>
              <DxButton RenderStyle="ButtonRenderStyle.Primary" Text="Store" Click="@Close" />
              <DxButton RenderStyle="ButtonRenderStyle.Secondary" Text="Cancel" Click="@Cancel" />
          </FooterContentTemplate>
      </DxPopup>
      
      @code 
      {
          [CascadingParameter] BlazoredModalInstance BlazoredModal { get; set; } = default!;
      
          [Parameter] public string? Message { get; set; }
      
          async Task Close() => await BlazoredModal.CloseAsync(ModalResult.Ok(true));
          async Task Cancel() => await BlazoredModal.CancelAsync();
      }
      

      文件“index.razor”:

      <DxButton Text="Blazored.Modal with DxPopup" Click="@ShowModalCustomLayout"> />
      
      @code 
      {
          [CascadingParameter] public IModalService Modal2 { get; set; } = default!;
      
          void ShowModalCustomLayout()
          {
              var options = new ModalOptions { UseCustomLayout = true };
              var parameters = new ModalParameters { 
                  { nameof(UweKeimPopupTest.Message), "Hello custom modal." } };
      
              Modal2.Show<UweKeimPopupTest>("Custom Layout", parameters, options);
          }
      }
      

      对我来说,这结合了经过良好测试的库 Blazored.Modal 和 DevExpress Blazor 的优势,以及使用临时弹出窗口创建和仍然能够使用 DevExpress 弹出实现 DxPopup 的核心功能。

      【讨论】:

        猜你喜欢
        • 2016-11-17
        • 2015-05-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-06-12
        • 1970-01-01
        相关资源
        最近更新 更多