在 ASP.NET Core Blazor Hybrid 中传递根组件参数

注意

此版本不是本文的最新版本。 对于当前版本,请参阅此文的 .NET 8 版本

警告

此版本的 ASP.NET Core 不再受支持。 有关详细信息,请参阅 .NET 和 .NET Core 支持策略。 对于当前版本,请参阅此文的 .NET 8 版本

重要

此信息与预发布产品相关,相应产品在商业发布之前可能会进行重大修改。 Microsoft 对此处提供的信息不提供任何明示或暗示的保证。

对于当前版本,请参阅此文的 .NET 8 版本

本文介绍如何在 Blazor Hybrid 应用中传递根组件参数。

BlazorWebViewRootComponent 类定义 IDictionary<string, object?>? 类型的 Parameters 属性,该属性表示要传递给根组件的参数的可选字典:

以下示例将一个视图模型传递给根组件,根组件进一步将视图模型作为级联类型传递给应用的 Blazor 部分中的 Razor 组件。 该示例基于 .NET MAUI 文档中的键盘示例:

尽管键盘示例侧重于在 .NET MAUIBlazor Hybrid 应用中实现 MVVM 模式:

  • 传递给根组件的对象的字典可以包含任何类型,用于需要将一个或多个参数传递给根组件,以供应用中的 Razor 组件使用的任何目的。
  • 下面的 .NET MAUIBlazor 示例演示的概念对于 Windows 窗体 Blazor 应用和 WPF Blazor 应用是相同的。

将以下视图模型放入 .NET MAUIBlazor Hybrid 应用中。

KeypadViewModel.cs

using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows.Input;

namespace MauiBlazor;

public class KeypadViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    private string _inputString = "";
    private string _displayText = "";
    private char[] _specialChars = { '*', '#' };

    public ICommand AddCharCommand { get; private set; }
    public ICommand DeleteCharCommand { get; private set; }

    public string InputString
    {
        get => _inputString;
        private set
        {
            if (_inputString != value)
            {
                _inputString = value;
                OnPropertyChanged();
                DisplayText = FormatText(_inputString);

                // Perhaps the delete button must be enabled/disabled.
                ((Command)DeleteCharCommand).ChangeCanExecute();
            }
        }
    }

    public string DisplayText
    {
        get => _displayText;
        set
        {
            if (_displayText != value)
            {
                _displayText = value;
                OnPropertyChanged();
            }
        }
    }

    public KeypadViewModel()
    {
        // Command to add the key to the input string
        AddCharCommand = new Command<string>((key) => InputString += key);

        // Command to delete a character from the input string when allowed
        DeleteCharCommand =
            new Command(
                // Command strips a character from the input string
                () => InputString = InputString.Substring(0, InputString.Length - 1),

                // CanExecute is processed here to return true when there's something to delete
                () => InputString.Length > 0
            );
    }

    string FormatText(string str)
    {
        bool hasNonNumbers = str.IndexOfAny(_specialChars) != -1;
        string formatted = str;

        // Format the string based on the type of data and the length
        if (hasNonNumbers || str.Length < 4 || str.Length > 10)
        {
            // Special characters exist, or the string is too small or large for special formatting
            // Do nothing
        }

        else if (str.Length < 8)
            formatted = string.Format("{0}-{1}", str.Substring(0, 3), str.Substring(3));

        else
            formatted = string.Format("({0}) {1}-{2}", str.Substring(0, 3), str.Substring(3, 3), str.Substring(6));

        return formatted;
    }

    public void OnPropertyChanged([CallerMemberName] string name = "") =>
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}

在本文的示例中,应用的根命名空间为 MauiBlazor。 更改 KeypadViewModel 的命名空间以匹配应用的根命名空间:

namespace MauiBlazor;

注意

在为 .NET MAUI 示例应用和 .NET MAUI 文档创建 KeypadViewModel 视图模型时,视图模型放置在名为 ViewModels 的文件夹中,但命名空间设置为应用的根目录,未包含文件夹名称。 如果要更新命名空间以在 KeypadViewModel.cs 文件中包含改文件夹,请修改本文中的示例代码以进行匹配。 将 using (C#) 和 @using (Razor) 语句添加到以下文件中,或将对视图模型类型的引用完全限定为 {APP NAMESPACE}.ViewModels.KeypadViewModel,其中 {APP NAMESPACE} 占位符是应用的根命名空间。

虽然可以直接在 XAML 中设置 Parameters ,但 XAML 文件中的以下示例为根组件命名 (rootComponent),并在代码隐藏文件中设置参数字典。

MainPage.xaml中:

<RootComponent x:Name="rootComponent" 
               Selector="#app" 
               ComponentType="{x:Type local:Main}" />

在代码隐藏文件 (MainPage.xaml.cs) 中,在构造函数中分配视图模型:

public MainPage()
{
    InitializeComponent();

    rootComponent.Parameters = 
        new Dictionary<string, object>
        {
            { "KeypadViewModel", new KeypadViewModel() }
        };
}

以下示例将对象 (KeypadViewModel) 作为 CascadingValue 在应用 Blazor 部分的组件层次结构中级联

Main 组件 (Main.razor) 中:

  • 添加与传递给根组件的对象类型匹配的参数:

    @code {
        [Parameter]
        public KeypadViewModel KeypadViewModel { get; set; }
    }
    
  • KeypadViewModelCascadingValue 组件级联。 将 <Found> XAML 内容更新为以下标记:

    <Found Context="routeData">
        <CascadingValue Value="KeypadViewModel">
            <RouteView RouteData="routeData" DefaultLayout="typeof(MainLayout)" />
            <FocusOnNavigate RouteData="routeData" Selector="h1"/>
        </CascadingValue>
    </Found>
    

此时,级联类型作为 CascadingParameter 对整个应用中的 Razor 组件可用。

下面的 Keypad 组件示例:

  • 显示 KeypadViewModel.DisplayText 的当前值。
  • 在显示字符串长度大于 0(零)的情况下(通过调用 ICommand.CanExecute 来检查),允许通过调用 KeypadViewModel.DeleteCharCommand 命令来删除字符。
  • 允许通过在 UI 中按下键,调用 KeypadViewModel.AddCharCommand 来添加字符。

Pages/Keypad.razor

@page "/keypad"

<h1>Keypad</h1>

<table id="keypad">
    <thead>
        <tr>
            <th colspan="2">@KeypadViewModel.DisplayText</th>
            <th><button @onclick="DeleteChar">&#x21E6;</button></th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td><button @onclick="@(e => AddChar("1"))">1</button></td>
            <td><button @onclick="@(e => AddChar("2"))">2</button></td>
            <td><button @onclick="@(e => AddChar("3"))">3</button></td>
        </tr>
        <tr>
            <td><button @onclick="@(e => AddChar("4"))">4</button></td>
            <td><button @onclick="@(e => AddChar("5"))">5</button></td>
            <td><button @onclick="@(e => AddChar("6"))">6</button></td>
        </tr>
        <tr>
            <td><button @onclick="@(e => AddChar("7"))">7</button></td>
            <td><button @onclick="@(e => AddChar("8"))">8</button></td>
            <td><button @onclick="@(e => AddChar("9"))">9</button></td>
        </tr>
        <tr>
            <td><button @onclick="@(e => AddChar("*"))">*</button></td>
            <td><button @onclick="@(e => AddChar("0"))">0</button></td>
            <td><button @onclick="@(e => AddChar("#"))">#</button></td>
        </tr>
    </tbody>
</table>

@code {
    [CascadingParameter]
    protected KeypadViewModel KeypadViewModel { get; set; }

    private void DeleteChar()
    {
        if (KeypadViewModel.DeleteCharCommand.CanExecute(null))
        {
            KeypadViewModel.DeleteCharCommand.Execute(null);
        }
    }

    private void AddChar(string key)
    {
        KeypadViewModel.AddCharCommand.Execute(key);
    }
}

纯粹出于演示目的,通过在 wwwroot/index.html 文件的 <head> 内容中放置以下 CSS 样式来设置按钮的样式:

<style>
    #keypad button {
        border: 1px solid black;
        border-radius:6px;
        height: 35px;
        width:80px;
    }
</style>

使用以下标记在 NavMenu 组件 (Shared/NavMenu.razor) 中创建边栏导航条目:

<div class="nav-item px-3">
    <NavLink class="nav-link" href="keypad">
        <span class="oi oi-list-rich" aria-hidden="true"></span> Keypad
    </NavLink>
</div>

其他资源