<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      ASP.NET Core Blazor簡介和快速入門二(組件基礎)

      大家好,我是碼農剛子。上一章介紹了Blazor的簡介,開發工具及環境,基本語法和一些示例。接下來我們繼續了解Blazor 組件相關的基礎知識,希望對你有所幫助。

      1、組件生命周期

      1.簡介

      Blazor的生命周期與React組件的生命周期類似,也分為三個階段:初始化階段、運行中階段和銷毀階段,其相關方法有10個,包括設置參數前、初始化、設置參數之后、組件渲染后以及組件的銷毀,但是這些方法有些是重復的,只不過是同步與異步的區別。

      2.圖解

      首先將結果圖呈現,代碼位于第3部分:

      1997712-20200405180006291-1493915010

      Blazor生命周期方法主要包括:

      1

      設置參數前

      SetParametersAsync

      2

      初始化

      OnInitialized/OnInitializedAsync

      3

      設置參數后

      OnParametersSet/OnParametersSetAsync

      4

      組件渲染呈現后

      OnAfterRender/OnAfterRenderAsync

      5

      判斷是否渲染組件

      ShouldRender

      6

      組件刪除前

      Dispose

      7

      通知組件渲染

      StateHasChanged

      在所有生命周期函數中,有以下需要注意的點:

      (1)前5種方法的聲明都是virtual,除SetParametersAsync為public外,其他的都是protected。

      (2)OnAfterRender/OnAfterRenderAsync方法有一個bool類型的形參firstRender,用于指示是否是第一次渲染(即組件初始化時的渲染)。

      (3)同步方法總是先于異步方法執行。

      (4)Dispose函數需要通過使用@implements指令實現IDisposable接口來實現。

      (5)StateHasChanged無法被重寫,可以被顯示調用,以便強制實現組件刷新(如果ShouldRender返回true,并且Blazor認為需要刷新);當組件狀態更改時不必顯示調用此函數,也可導致組件的重新渲染(如果ShouldRender返回true),因為其已經在ComponentBase內部的處理過程(第一次初始化設置參數時、設置參數后和DOM事件處理等)中被調用。

      3.代碼示例

      設置參數時 (SetParametersAsync 設置由組件的父組件在呈現樹或路由參數中提供的參數。

       

      每次調用 ParameterView 時,方法的 參數都包含該組件的SetParametersAsync值集。 通過重寫 SetParametersAsync 方法,C#代碼可以直接與 ParameterView 參數交互。

      @page "/set-params-async/{Param?}"
      
      <PageTitle>Set Parameters Async</PageTitle>
      
      <h1>Set Parameters Async Example</h1>
      
      <p>@message</p>
      
      @code {
          private string message = "Not set";
      
          [Parameter]
          public string? Param { get; set; }
      
          public override async Task SetParametersAsync(ParameterView parameters)
          {
              if (parameters.TryGetValue<string>(nameof(Param), out var value))
              {
                  if (value is null)
                  {
                      message = "The value of 'Param' is null.";
                  }
                  else
                  {
                      message = $"The value of 'Param' is {value}.";
                  }
              }
      
              await base.SetParametersAsync(parameters);
          }
      }

      組件初始化 (OnInitialized 和 OnInitializedAsync 專門用于在組件實例的整個生命周期內初始化組件。 參數值和參數值更改不應影響在這些方法中執行的初始化。 例如,將靜態選項加載到下拉列表中,該下拉列表在組件的生命周期內不會更改,也不依賴于參數值,這是在這些生命周期方法之一中執行的操作。 如果參數值或參數值更改會影響組件狀態,請改為使用 OnParametersSet{Async}。

       

      組件在接收 SetParametersAsync 中的初始參數后初始化,此時,將調用這些方法。

      如果使用同步父組件初始化,則保證父組件初始化在子組件初始化之前完成。 如果使用異步父組件初始化,則無法確定父組件和子組件初始化的完成順序,因為它取決于正在運行的初始化代碼。

      對于同步操作,重寫 OnInitialized:

      @page "/on-init"
      
      <PageTitle>On Initialized</PageTitle>
      
      <h1>On Initialized Example</h1>
      
      <p>@message</p>
      
      @code {
          private string? message;
      
          protected override void OnInitialized() => 
              message = $"Initialized at {DateTime.Now}";
      }

      若要執行異步操作,請替代 OnInitializedAsync 并使用 await 運算符:

      protected override async Task OnInitializedAsync()
      {
          //await ...
          await Task.Delay(2000);    //2秒之后
          message = $"Initialized at {DateTime.Now} after 2 second delay";
      }

      如果自定義基類與自定義初始化邏輯一起使用,需在基類上調用 OnInitializedAsync

      protected override async Task OnInitializedAsync()
      {
          await ...
      
          await base.OnInitializedAsync();
      }

      設置參數之后 (OnParametersSet OnParametersSetAsync 在以下情況下調用:

       

      • OnInitialized OnInitializedAsync 中初始化組件后。
      • 當父組件重新呈現并提供以下內容時:
        • 至少一個參數已更改時的已知或基元不可變類型。
        • 復雜類型的參數。 框架無法知道復雜類型參數的值是否在內部發生了改變,因此,如果存在一個或多個復雜類型的參數,框架始終將參數集視為已更改。

      在組件路由中,不能同時對DateTime參數使用datetime路由約束,并將該參數設為可選。 因此,以下 OnParamsSet 組件使用兩個 @page 指令來處理具有和沒有 URL 中提供的日期段的路由。

      @page "/on-params-set"
      @page "/on-params-set/{StartDate:datetime}"
      
      <PageTitle>On Parameters Set</PageTitle>
      
      <h1>On Parameters Set Example</h1>
      
      <p>
          Pass a datetime in the URI of the browser's address bar. 
          For example, add <code>/1-1-2024</code> to the address.
      </p>
      
      <p>@message</p>
      
      @code {
          private string? message;
      
          [Parameter]
          public DateTime StartDate { get; set; }
      
          protected override void OnParametersSet()
          {
              if (StartDate == default)
              {
                  StartDate = DateTime.Now;
      
                  message = $"No start date in URL. Default value applied " +
                      $"(StartDate: {StartDate}).";
              }
              else
              {
                  message = $"The start date in the URL was used " +
                      $"(StartDate: {StartDate}).";
              }
          }
      }

      應用參數和屬性值時,異步操作必須在 OnParametersSetAsync 生命周期事件期間發生:

      protected override async Task OnParametersSetAsync()
      {
          await ...
      }

      如果自定義基類與自定義初始化邏輯一起使用,需在基類上調用 OnParametersSetAsync

      protected override async Task OnParametersSetAsync()
      {
          await ...
      
          await base.OnParametersSetAsync();
      }

      組件呈現之后 (OnAfterRenderOnAfterRenderAsync 在組件以交互方式呈現并且 UI 完成更新之后被調用(例如,元素添加到瀏覽器 DOM 之后)。 此時會填充元素和組件引用。 在此階段中,可使用呈現的內容執行其他初始化步驟,例如與呈現的 DOM 元素交互的 JS 互操作調用。

       

      這些方法不會在預呈現或靜態服務器端渲染(靜態 SSR)期間在服務器上調用,因為這些進程未附加到實時瀏覽器 DOM,并且已在 DOM 更新之前完成。

      對于 OnAfterRenderAsync,組件在任何返回 Task 的操作完成后不會自動重渲染,以避免無限渲染循環。

      firstRender OnAfterRender OnAfterRenderAsync 參數:

      • 在第一次呈現組件實例時設置為 true
      • 可用于確保初始化操作僅執行一次。
      @page "/after-render"
      @inject ILogger<AfterRender> Logger 
      
      <PageTitle>After Render</PageTitle>
      
      <h1>After Render Example</h1>
      
      <p>
          <button @onclick="HandleClick">Log information (and trigger a render)</button>
      </p>
      
      <p>Study logged messages in the console.</p>
      
      @code {
          protected override void OnAfterRender(bool firstRender) =>
              Logger.LogInformation("firstRender = {FirstRender}", firstRender);
      
          private void HandleClick() => Logger.LogInformation("HandleClick called");
      }

      加載頁面并選擇按鈕時,AfterRender.razor 示例向控制臺輸出以下內容:

      在渲染后立即進行的異步工作必須在 OnAfterRenderAsync 生命周期事件期間發生:

      protected override async Task OnAfterRenderAsync(bool firstRender)
      {
          ...
      }

      如果自定義基類與自定義初始化邏輯一起使用,需在基類上調用 OnAfterRenderAsync

      protected override async Task OnAfterRenderAsync(bool firstRender)
      {
          ...
      
          await base.OnAfterRenderAsync(firstRender);
      }

      基類生命周期方法

      重寫 Blazor 的生命周期方法時,無需為 ComponentBase 調用基類生命周期方法。 但在以下情況下,組件應調用重寫的基類生命周期方法:

      • 重寫 ComponentBase.SetParametersAsync 時,通常會調用 await base.SetParametersAsync(parameters);, 因為基類方法會調用其他生命周期方法并以復雜的方式觸發渲染。 有關詳細信息,請參閱設置參數時 (SetParametersAsync) 部分。
      • 如果基類方法包含必須執行的邏輯。 庫使用者通常在繼承基類時調用基類生命周期方法,因為庫基類通常具有要執行的自定義生命周期邏輯。 如果應用使用某個庫中的基類,請參閱該庫的文檔以獲取指導。

      以下示例中調用了 base.OnInitialized(); 以確保會執行基類的 OnInitialized 方法。 如果沒有調用,BlazorRocksBase2.OnInitialized 不會執行。

      @page "/blazor-rocks-2"
      @inherits BlazorRocksBase2
      @inject ILogger<BlazorRocks2> Logger
      
      <PageTitle>Blazor Rocks!</PageTitle>
      
      <h1>Blazor Rocks! Example 2</h1>
      
      <p>
          @BlazorRocksText
      </p>
      
      @code {
          protected override void OnInitialized()
          {
              Logger.LogInformation("Initialization code of BlazorRocks2 executed!");
      
              base.OnInitialized();
          }
      }
      using Microsoft.AspNetCore.Components;
      
      namespace BlazorAppWasm
      {
          public class BlazorRocksBase2: ComponentBase
          {
              [Inject]
              private ILogger<BlazorRocksBase2> Logger { get; set; } = default!;
      
              public string BlazorRocksText { get; set; } = "Blazor rocks the browser!";
      
              protected override void OnInitialized() =>
                  Logger.LogInformation("Initialization code of BlazorRocksBase2 executed!");
          }
      }
      

      2、數據綁定

      Blazor提供了強大的數據綁定機制,主要包括單向綁定雙向綁定兩種模式。

      1. 單向數據綁定

      單向綁定是指數據從組件流向UI,但UI的變化不會自動更新數據源。

      基本語法

      <!-- 使用 @ 符號進行單向綁定 -->
      <p>當前值: @currentValue</p>
      <span>用戶名: @UserName</span>
      <div>創建時間: @CreateTime.ToString("yyyy-MM-dd")</div>

      完整示例

      <!-- OneWayBinding.razor -->
      <div class="one-way-demo">
          <h3>單向綁定示例</h3>
          
          <!-- 顯示數據,但不允許編輯 -->
          <div class="display-area">
              <p>計數器: <strong>@count</strong></p>
              <p>消息: <strong>@message</strong></p>
              <p>用戶信息: <strong>@user.Name</strong> - <strong>@user.Age</strong>歲</p>
          </div>
      
          <!-- 控制按鈕 -->
          <div class="control-area">
              <button @onclick="Increment" class="btn btn-primary">增加計數</button>
              <button @onclick="ChangeMessage" class="btn btn-secondary">更改消息</button>
              <button @onclick="UpdateUser" class="btn btn-info">更新用戶</button>
          </div>
      </div>
      
      @code {
          private int count = 0;
          private string message = "初始消息";
          private User user = new User { Name = "張三", Age = 25 };
      
          private void Increment()
          {
              count++;
              // StateHasChanged(); // 通常不需要手動調用,事件處理會自動觸發重新渲染
          }
      
          private void ChangeMessage()
          {
              message = $"消息已更新: {DateTime.Now:HH:mm:ss}";
          }
      
          private void UpdateUser()
          {
              user = new User { Name = "李四", Age = 30 };
          }
      
          class User
          {
              public string Name { get; set; } = string.Empty;
              public int Age { get; set; }
          }
      }

      2. 雙向數據綁定

      雙向綁定允許數據在組件和UI之間雙向流動:UI變化自動更新數據源,數據源變化自動更新UI。

      基本語法

      <!-- 使用 @bind 指令進行雙向綁定 -->
      <input @bind="propertyName" />
      <input @bind="fieldName" />
      <select @bind="selectedValue">...</select>

      完整示例

      <!-- TwoWayBinding.razor -->
      <div class="two-way-demo">
          <h3>雙向綁定示例</h3>
          
          <div class="form-group">
              <label>用戶名:</label>
              <input @bind="userName" class="form-control" />
              <small>顯示: @userName</small>
          </div>
      
          <div class="form-group">
              <label>郵箱:</label>
              <input @bind="email" class="form-control" />
              <small>顯示: @email</small>
          </div>
      
          <div class="form-group">
              <label>年齡:</label>
              <input @bind="age" type="number" class="form-control" />
              <small>顯示: @age</small>
          </div>
      
          <div class="form-group">
              <label>城市:</label>
              <select @bind="selectedCity" class="form-control">
                  <option value="">請選擇</option>
                  <option value="Beijing">北京</option>
                  <option value="Shanghai">上海</option>
                  <option value="Guangzhou">廣州</option>
                  <option value="Shenzhen">深圳</option>
              </select>
              <small>選擇: @selectedCity</small>
          </div>
      
          <div class="form-group">
              <label>是否同意協議:</label>
              <input type="checkbox" @bind="isAgreed" />
              <span>@(isAgreed ? "已同意" : "未同意")</span>
          </div>
      
          <!-- 顯示匯總信息 -->
          <div class="summary">
              <h4>匯總信息:</h4>
              <p>用戶名: @userName</p>
              <p>郵箱: @email</p>
              <p>年齡: @age</p>
              <p>城市: @selectedCity</p>
              <p>同意協議: @isAgreed</p>
          </div>
      </div>
      
      @code {
          private string userName = string.Empty;
          private string email = string.Empty;
          private int age = 0;
          private string selectedCity = string.Empty;
          private bool isAgreed = false;
      }

      3. 綁定事件控制

      3.1 綁定特定事件

      默認情況下,@bind 在失去焦點時更新。可以使用 @bind:event 指定觸發事件:

      <!-- 實時綁定(輸入時立即更新) -->
      <div class="real-time-demo">
          <h4>實時綁定示例</h4>
          
          <input @bind="searchText" @bind:event="oninput" 
                 placeholder="輸入搜索內容..." />
          <p>實時搜索: @searchText</p>
      
          <!-- 對比默認行為 -->
          <input @bind="normalText" placeholder="默認綁定(失去焦點更新)" />
          <p>默認綁定: @normalText</p>
      </div>
      
      @code {
          private string searchText = string.Empty;
          private string normalText = string.Empty;
      }

      3.2 綁定格式化

      <div class="format-demo">
          <h4>格式化綁定示例</h4>
          
          <!-- 日期格式化 -->
          <input @bind="startDate" @bind:format="yyyy-MM-dd" type="date" />
          <p>選擇的日期: @startDate.ToString("yyyy年MM月dd日")</p>
      
          <!-- 數字格式化 -->
          <input @bind="price" @bind:format="F2" type="number" step="0.01" />
          <p>價格: @price.ToString("C")</p>
      </div>
      
      @code {
          private DateTime startDate = DateTime.Today;
          private decimal price = 0.00m;
      }

      4. 自定義組件雙向綁定

      在自定義組件中實現雙向綁定:

      子組件

      <!-- CustomInput.razor -->
      <div class="custom-input">
          <label>@Label</label>
          <input 
              value="@Value" 
              @oninput="HandleInput"
              class="form-control @AdditionalClass"
              placeholder="@Placeholder" />
          @if (!string.IsNullOrEmpty(ValidationMessage))
          {
              <div class="text-danger">@ValidationMessage</div>
          }
      </div>
      
      @code {
          [Parameter]
          public string Value { get; set; } = string.Empty;
      
          [Parameter]
          public EventCallback<string> ValueChanged { get; set; }
      
          [Parameter]
          public string Label { get; set; } = string.Empty;
      
          [Parameter]
          public string Placeholder { get; set; } = string.Empty;
      
          [Parameter]
          public string AdditionalClass { get; set; } = string.Empty;
      
          [Parameter]
          public string ValidationMessage { get; set; } = string.Empty;
      
          private async Task HandleInput(ChangeEventArgs e)
          {
              Value = e.Value?.ToString() ?? string.Empty;
              await ValueChanged.InvokeAsync(Value);
          }
      }

      父組件使用

      <!-- ParentComponent.razor -->
      <div class="parent-demo">
          <h3>自定義組件雙向綁定</h3>
          
          <CustomInput 
              @bind-Value="userName" 
              Label="用戶名" 
              Placeholder="請輸入用戶名" />
      
          <CustomInput 
              @bind-Value="email" 
              Label="郵箱" 
              Placeholder="請輸入郵箱地址"
              ValidationMessage="@(IsValidEmail ? "" : "郵箱格式不正確")" />
      
          <div class="result">
              <p>用戶名: @userName</p>
              <p>郵箱: @email</p>
          </div>
      </div>
      
      @code {
          private string userName = string.Empty;
          private string email = string.Empty;
      
          private bool IsValidEmail => email.Contains("@") && email.Contains(".");
      }

      5.復雜對象綁定

      <!-- ComplexObjectBinding.razor -->
      <div class="complex-binding">
          <h3>復雜對象綁定</h3>
          
          <div class="form-section">
              <h4>用戶信息</h4>
              
              <div class="form-group">
                  <label>姓名:</label>
                  <input @bind="currentUser.Name" class="form-control" />
              </div>
      
              <div class="form-group">
                  <label>年齡:</label>
                  <input @bind="currentUser.Age" type="number" class="form-control" />
              </div>
      
              <div class="form-group">
                  <label>地址:</label>
                  <input @bind="currentUser.Address.Street" class="form-control" placeholder="街道" />
                  <input @bind="currentUser.Address.City" class="form-control" placeholder="城市" />
              </div>
          </div>
      
          <div class="display-section">
              <h4>當前用戶信息:</h4>
              <pre>@userInfoJson</pre>
          </div>
      
          <button @onclick="ResetUser" class="btn btn-warning">重置用戶</button>
          <button @onclick="CreateNewUser" class="btn btn-success">創建新用戶</button>
      </div>
      
      @code {
          private User currentUser = new User();
      
          private string userInfoJson => 
              System.Text.Json.JsonSerializer.Serialize(currentUser, new System.Text.Json.JsonSerializerOptions 
              { 
                  WriteIndented = true 
              });
      
          private void ResetUser()
          {
              currentUser = new User();
          }
      
          private void CreateNewUser()
          {
              currentUser = new User
              {
                  Name = "新用戶",
                  Age = 18,
                  Address = new Address { Street = "新建街道", City = "新建城市" }
              };
          }
      
          class User
          {
              public string Name { get; set; } = string.Empty;
              public int Age { get; set; }
              public Address Address { get; set; } = new Address();
          }
      
          class Address
          {
              public string Street { get; set; } = string.Empty;
              public string City { get; set; } = string.Empty;
          }
      }

      6.綁定模式對比

      綁定類型

      語法

      更新時機

      適用場景

      單向綁定

      @property

      數據源變化時

      顯示數據、計算屬性

      雙向綁定

      @bind="property"

      失去焦點時

      表單輸入、用戶交互

      實時雙向

      @bind="property" @bind:event="oninput"

      輸入時實時更新

      搜索框、實時驗證

      自定義綁定

      @bind-Value="property"

      自定義事件觸發

      自定義表單組件

      3、事件處理

      1. 基本事件處理

      1.1 單擊事件

      <!-- ClickEvents.razor -->
      <div class="click-demo">
          <h3>單擊事件示例</h3>
          
          <!-- 基本點擊事件 -->
          <button @onclick="HandleClick" class="btn btn-primary">
              點擊我
          </button>
      
          <!-- 帶參數的事件處理 -->
          <div class="button-group">
              <button @onclick="() => HandleButtonClick(1)" class="btn btn-secondary">按鈕 1</button>
              <button @onclick="() => HandleButtonClick(2)" class="btn btn-secondary">按鈕 2</button>
              <button @onclick="() => HandleButtonClick(3)" class="btn btn-secondary">按鈕 3</button>
          </div>
      
          <!-- 顯示點擊結果 -->
          <div class="result">
              <p>最后點擊的按鈕: @lastClickedButton</p>
              <p>點擊次數: @clickCount</p>
          </div>
      </div>
      
      @code {
          private int lastClickedButton = 0;
          private int clickCount = 0;
      
          private void HandleClick()
          {
              clickCount++;
              Console.WriteLine("按鈕被點擊了!");
          }
      
          private void HandleButtonClick(int buttonNumber)
          {
              lastClickedButton = buttonNumber;
              clickCount++;
              StateHasChanged();
          }
      }

      1.2 異步事件處理

      <!-- AsyncEvents.razor -->
      <div class="async-demo">
          <h3>異步事件處理</h3>
          
          <button @onclick="HandleAsyncClick" class="btn btn-primary" disabled="@isLoading">
              @if (isLoading)
              {
                  <span>加載中...</span>
              }
              else
              {
                  <span>模擬異步操作</span>
              }
          </button>
      
          <div class="result">
              <p>操作結果: @operationResult</p>
              <p>耗時: @elapsedTime 毫秒</p>
          </div>
      </div>
      
      @code {
          private bool isLoading = false;
          private string operationResult = string.Empty;
          private long elapsedTime = 0;
      
          private async Task HandleAsyncClick()
          {
              isLoading = true;
              operationResult = "操作開始...";
              
              var stopwatch = System.Diagnostics.Stopwatch.StartNew();
              
              // 模擬異步操作
              await Task.Delay(2000);
              
              stopwatch.Stop();
              elapsedTime = stopwatch.ElapsedMilliseconds;
              
              operationResult = $"操作完成!數據已保存。";
              isLoading = false;
              
              StateHasChanged();
          }
      }

      2. 表單事件處理

      2.1 輸入事件

      <!-- FormEvents.razor -->
      <div class="form-events">
          <h3>表單事件處理</h3>
          
          <div class="form-group">
              <label>輸入文本:</label>
              <input @oninput="HandleInput" 
                     @onchange="HandleChange"
                     class="form-control" 
                     placeholder="輸入內容..." />
              <small>實時輸入: @inputValue | 變化事件: @changeValue</small>
          </div>
      
          <div class="form-group">
              <label>選擇選項:</label>
              <select @onchange="HandleSelectChange" class="form-control">
                  <option value="">請選擇</option>
                  <option value="option1">選項一</option>
                  <option value="option2">選項二</option>
                  <option value="option3">選項三</option>
              </select>
              <small>選擇的值: @selectedValue</small>
          </div>
      
          <div class="form-group">
              <label>
                  <input type="checkbox" @onchange="HandleCheckboxChange" />
                  同意條款
              </label>
              <small>狀態: @(isChecked ? "已選中" : "未選中")</small>
          </div>
      
          <!-- 表單提交 -->
          <form @onsubmit="HandleSubmit" @onvalidSubmit="HandleValidSubmit">
              <div class="form-group">
                  <label>用戶名:</label>
                  <input @bind="user.Username" class="form-control" required />
              </div>
              
              <div class="form-group">
                  <label>郵箱:</label>
                  <input @bind="user.Email" type="email" class="form-control" required />
              </div>
              
              <button type="submit" class="btn btn-success">提交表單</button>
          </form>
      
          <div class="form-result">
              <h4>表單數據:</h4>
              <pre>@System.Text.Json.JsonSerializer.Serialize(user, new System.Text.Json.JsonSerializerOptions { WriteIndented = true })</pre>
              <p>提交狀態: @submitStatus</p>
          </div>
      </div>
      
      @code {
          private string inputValue = string.Empty;
          private string changeValue = string.Empty;
          private string selectedValue = string.Empty;
          private bool isChecked = false;
          private string submitStatus = "未提交";
          
          private User user = new User();
      
          private void HandleInput(ChangeEventArgs e)
          {
              inputValue = e.Value?.ToString() ?? string.Empty;
          }
      
          private void HandleChange(ChangeEventArgs e)
          {
              changeValue = e.Value?.ToString() ?? string.Empty;
          }
      
          private void HandleSelectChange(ChangeEventArgs e)
          {
              selectedValue = e.Value?.ToString() ?? string.Empty;
          }
      
          private void HandleCheckboxChange(ChangeEventArgs e)
          {
              isChecked = (bool)(e.Value ?? false);
          }
      
          private void HandleSubmit()
          {
              submitStatus = "表單提交(可能有驗證錯誤)";
          }
      
          private void HandleValidSubmit()
          {
              submitStatus = $"表單驗證通過!數據已保存 - {DateTime.Now:HH:mm:ss}";
              // 這里可以調用API保存數據
          }
      
          class User
          {
              public string Username { get; set; } = string.Empty;
              public string Email { get; set; } = string.Empty;
          }
      }

      3. 鼠標和鍵盤事件

      3.1 鼠標事件

      <!-- MouseEvents.razor -->
      <div class="mouse-events">
          <h3>鼠標事件</h3>
          
          <div class="interactive-area"
               @onmousedown="HandleMouseDown"
               @onmouseup="HandleMouseUp"
               @onmousemove="HandleMouseMove"
               @onmouseover="HandleMouseOver"
               @onmouseout="HandleMouseOut"
               @onclick="HandleAreaClick"
               @ondblclick="HandleDoubleClick"
               style="width: 300px; height: 200px; border: 2px solid #007bff; padding: 20px; margin: 10px 0;">
              鼠標交互區域
          </div>
      
          <div class="event-log">
              <h4>事件日志:</h4>
              <ul>
                  @foreach (var log in eventLogs.TakeLast(10).Reverse())
                  {
                      <li>@log</li>
                  }
              </ul>
          </div>
      
          <div class="mouse-info">
              <p>鼠標位置: (@mouseX, @mouseY)</p>
              <p>按鈕狀態: @(isMouseDown ? "按下" : "釋放")</p>
              <p>懸停狀態: @(isMouseOver ? "在區域內" : "在區域外")</p>
          </div>
      </div>
      
      @code {
          private double mouseX = 0;
          private double mouseY = 0;
          private bool isMouseDown = false;
          private bool isMouseOver = false;
          private List<string> eventLogs = new List<string>();
      
          private void LogEvent(string eventName)
          {
              eventLogs.Add($"{DateTime.Now:HH:mm:ss.fff} - {eventName}");
              StateHasChanged();
          }
      
          private void HandleMouseDown(MouseEventArgs e)
          {
              isMouseDown = true;
              LogEvent($"MouseDown - 按鈕: {e.Button}, 位置: ({e.ClientX}, {e.ClientY})");
          }
      
          private void HandleMouseUp(MouseEventArgs e)
          {
              isMouseDown = false;
              LogEvent($"MouseUp - 按鈕: {e.Button}, 位置: ({e.ClientX}, {e.ClientY})");
          }
      
          private void HandleMouseMove(MouseEventArgs e)
          {
              mouseX = e.ClientX;
              mouseY = e.ClientY;
              // 注意:頻繁觸發,生產環境需要節流
              // LogEvent($"MouseMove - 位置: ({e.ClientX}, {e.ClientY})");
          }
      
          private void HandleMouseOver(MouseEventArgs e)
          {
              isMouseOver = true;
              LogEvent("MouseOver");
          }
      
          private void HandleMouseOut(MouseEventArgs e)
          {
              isMouseOver = false;
              LogEvent("MouseOut");
          }
      
          private void HandleAreaClick(MouseEventArgs e)
          {
              LogEvent($"Click - 按鈕: {e.Button}");
          }
      
          private void HandleDoubleClick(MouseEventArgs e)
          {
              LogEvent($"DoubleClick - 按鈕: {e.Button}");
          }
      }

      3.2 鍵盤事件

      <!-- KeyboardEvents.razor -->
      <div class="keyboard-events">
          <h3>鍵盤事件</h3>
          
          <div class="input-area">
              <input @onkeydown="HandleKeyDown"
                     @onkeyup="HandleKeyUp"
                     @onkeypress="HandleKeyPress"
                     class="form-control"
                     placeholder="在這里輸入并觀察鍵盤事件..." />
          </div>
      
          <div class="event-log">
              <h4>鍵盤事件日志:</h4>
              <ul>
                  @foreach (var log in keyEventLogs.TakeLast(10).Reverse())
                  {
                      <li>@log</li>
                  }
              </ul>
          </div>
      
          <div class="key-info">
              <p>最后按下的鍵: @lastKey</p>
              <p>Ctrl 按下: @(isCtrlPressed ? "是" : "否")</p>
              <p>Shift 按下: @(isShiftPressed ? "是" : "否")</p>
              <p>Alt 按下: @(isAltPressed ? "是" : "否")</p>
          </div>
      </div>
      
      @code {
          private string lastKey = "無";
          private bool isCtrlPressed = false;
          private bool isShiftPressed = false;
          private bool isAltPressed = false;
          private List<string> keyEventLogs = new List<string>();
      
          private void LogKeyEvent(string eventName, KeyboardEventArgs e)
          {
              var log = $"{DateTime.Now:HH:mm:ss.fff} - {eventName}: Key='{e.Key}', Code='{e.Code}'";
              if (e.CtrlKey) log += " [Ctrl]";
              if (e.ShiftKey) log += " [Shift]";
              if (e.AltKey) log += " [Alt]";
              
              keyEventLogs.Add(log);
              StateHasChanged();
          }
      
          private void HandleKeyDown(KeyboardEventArgs e)
          {
              lastKey = e.Key;
              isCtrlPressed = e.CtrlKey;
              isShiftPressed = e.ShiftKey;
              isAltPressed = e.AltKey;
              
              LogKeyEvent("KeyDown", e);
      
              // 快捷鍵處理示例
              if (e.CtrlKey && e.Key == "s")
              {
                  e.PreventDefault(); // 阻止瀏覽器默認保存行為
                  LogKeyEvent("快捷鍵: Ctrl+S", e);
              }
          }
      
          private void HandleKeyUp(KeyboardEventArgs e)
          {
              isCtrlPressed = e.CtrlKey;
              isShiftPressed = e.ShiftKey;
              isAltPressed = e.AltKey;
              
              LogKeyEvent("KeyUp", e);
          }
      
          private void HandleKeyPress(KeyboardEventArgs e)
          {
              LogKeyEvent("KeyPress", e);
          }
      }

      4. 焦點和剪貼板事件

      <!-- FocusClipboardEvents.razor -->
      <div class="focus-clipboard">
          <h3>焦點和剪貼板事件</h3>
          
          <div class="form-group">
              <label>焦點測試輸入框:</label>
              <input @onfocus="HandleFocus"
                     @onblur="HandleBlur"
                     class="form-control"
                     placeholder="點擊獲取焦點,點擊別處失去焦點" />
          </div>
      
          <div class="form-group">
              <label>復制粘貼測試:</label>
              <textarea @oncopy="HandleCopy"
                        @oncut="HandleCut"
                        @onpaste="HandlePaste"
                        class="form-control"
                        rows="3"
                        placeholder="在這里測試復制、剪切、粘貼操作">這是一些測試文本</textarea>
          </div>
      
          <div class="event-log">
              <h4>事件狀態:</h4>
              <p>焦點狀態: <span class="@(hasFocus ? "text-success" : "text-danger")">@(hasFocus ? "有焦點" : "無焦點")</span></p>
              <p>最后操作: @lastOperation</p>
              <p>剪貼板內容: @clipboardContent</p>
          </div>
      </div>
      
      @code {
          private bool hasFocus = false;
          private string lastOperation = "無";
          private string clipboardContent = "無";
      
          private void HandleFocus(FocusEventArgs e)
          {
              hasFocus = true;
              lastOperation = "獲得焦點";
              StateHasChanged();
          }
      
          private void HandleBlur(FocusEventArgs e)
          {
              hasFocus = false;
              lastOperation = "失去焦點";
              StateHasChanged();
          }
      
          private void HandleCopy(ClipboardEventArgs e)
          {
              lastOperation = "復制操作";
              clipboardContent = "復制的內容無法直接獲取(安全限制)";
              StateHasChanged();
          }
      
          private void HandleCut(ClipboardEventArgs e)
          {
              lastOperation = "剪切操作";
              clipboardContent = "剪切的內容無法直接獲取(安全限制)";
              StateHasChanged();
          }
      
          private void HandlePaste(ClipboardEventArgs e)
          {
              lastOperation = "粘貼操作";
              clipboardContent = "粘貼的內容無法直接獲取(安全限制)";
              StateHasChanged();
          }
      }

      5. 自定義事件處理

      5.1 事件參數封裝

      <!-- CustomEventHandling.razor -->
      <div class="custom-events">
          <h3>自定義事件處理</h3>
          
          <!-- 事件冒泡和阻止默認行為 -->
          <div @onclick="HandleParentClick" style="padding: 20px; border: 2px solid red;">
              <p>父級區域(點擊會觸發)</p>
              <button @onclick="HandleChildClick" 
                      @onclick:stopPropagation
                      class="btn btn-primary">
                  子按鈕(點擊不會冒泡)
              </button>
              
              <button @onclick="HandleChildClickWithPrevent" 
                      @onclick:preventDefault
                      class="btn btn-secondary">
                  阻止默認行為的按鈕
              </button>
          </div>
      
          <!-- 自定義事件處理邏輯 -->
          <div class="custom-actions">
              <h4>自定義操作:</h4>
              <button @onclick="HandleCustomAction1" class="btn btn-info">操作1</button>
              <button @onclick="HandleCustomAction2" class="btn btn-info">操作2</button>
              <button @onclick="async () => await HandleCustomAsyncAction()" class="btn btn-info">異步操作</button>
          </div>
      
          <div class="action-log">
              <h4>操作日志:</h4>
              <ul>
                  @foreach (var log in actionLogs.TakeLast(5).Reverse())
                  {
                      <li>@log</li>
                  }
              </ul>
          </div>
      </div>
      
      @code {
          private List<string> actionLogs = new List<string>();
      
          private void LogAction(string action)
          {
              actionLogs.Add($"{DateTime.Now:HH:mm:ss} - {action}");
              StateHasChanged();
          }
      
          private void HandleParentClick()
          {
              LogAction("父級區域被點擊");
          }
      
          private void HandleChildClick()
          {
              LogAction("子按鈕被點擊(事件不會冒泡)");
          }
      
          private void HandleChildClickWithPrevent()
          {
              LogAction("阻止默認行為的按鈕被點擊");
          }
      
          private void HandleCustomAction1()
          {
              LogAction("執行自定義操作1");
              // 自定義業務邏輯
          }
      
          private void HandleCustomAction2(MouseEventArgs e)
          {
              LogAction($"執行自定義操作2 - 點擊位置: ({e.ClientX}, {e.ClientY})");
              // 自定義業務邏輯
          }
      
          private async Task HandleCustomAsyncAction()
          {
              LogAction("開始異步操作");
              await Task.Delay(1000);
              LogAction("異步操作完成");
          }
      }

      6. 事件處理最佳實踐

      6.1 性能優化

      <!-- OptimizedEvents.razor -->
      <div class="optimized-events">
          <h3>事件處理性能優化</h3>
      
          <!-- 避免內聯Lambda表達式(可能引起不必要的重渲染) -->
          @foreach (var item in items)
          {
              <div class="item" @key="item.Id">
                  <span>@item.Name</span>
                  <!-- 好的做法:使用方法引用 -->
                  <button @onclick="() => DeleteItem(item.Id)" class="btn btn-sm btn-danger">刪除</button>
              </div>
          }
      
          <!-- 大量事件考慮使用事件委托 -->
          <div class="large-list">
              @foreach (var item in largeList)
              {
                  <div class="list-item" data-id="@item.Id" data-name="@item.Name" @onclick="(e) => HandleListItemClick(e, item.Id)">
                      @item.Name
                  </div>
              }
          </div>
      
          <div class="action-log">
              <h4>操作日志:</h4>
              <ul>
                  @foreach (var log in actionLogs.TakeLast(5).Reverse())
                  {
                      <li>@log</li>
                  }
              </ul>
          </div>
      </div>
      
      @code {
          private List<Item> items = new List<Item>
          {
              new Item { Id = 1, Name = "項目1" },
              new Item { Id = 2, Name = "項目2" },
              new Item { Id = 3, Name = "項目3" }
          };
      
          private List<Item> largeList = Enumerable.Range(1, 100)
              .Select(i => new Item { Id = i, Name = $"項目{i}" })
              .ToList();
      
          private List<string> actionLogs = new List<string>();
      
          private void DeleteItem(int id)
          {
              items.RemoveAll(i => i.Id == id);
              LogAction($"刪除了項目 {id}");
          }
      
          private void HandleListItemClick(MouseEventArgs e, int itemId)
          {
              // 通過參數 itemId 就知道是哪個按鈕被點擊了
              Console.WriteLine($"Clicked item ID: {itemId}");
          }
      
          // 添加 LogAction 方法
          private void LogAction(string action)
          {
              actionLogs.Add($"{DateTime.Now:HH:mm:ss} - {action}");
              StateHasChanged();
          }
      
          class Item
          {
              public int Id { get; set; }
              public string Name { get; set; } = string.Empty;
          }
      }

      7. 常用事件總結

      事件類型

      指令

      事件參數

      說明

      點擊事件

      @onclick

      MouseEventArgs

      鼠標點擊

      雙擊事件

      @ondblclick

      MouseEventArgs

      鼠標雙擊

      鼠標移動

      @onmousemove

      MouseEventArgs

      鼠標移動

      鼠標按下

      @onmousedown

      MouseEventArgs

      鼠標按下

      鼠標釋放

      @onmouseup

      MouseEventArgs

      鼠標釋放

      鍵盤按下

      @onkeydown

      KeyboardEventArgs

      鍵盤按下

      鍵盤釋放

      @onkeyup

      KeyboardEventArgs

      鍵盤釋放

      輸入事件

      @oninput

      ChangeEventArgs

      輸入時觸發

      變化事件

      @onchange

      ChangeEventArgs

      值變化時觸發

      獲得焦點

      @onfocus

      FocusEventArgs

      元素獲得焦點

      失去焦點

      @onblur

      FocusEventArgs

      元素失去焦點

      表單提交

      @onsubmit

      EventArgs

      表單提交

      4、組件參數和級聯參數

      1. 組件參數(Parameter)

      參數主要用來在各組件之間傳遞值,在初始項目的SurveyPrompt組件中就包含了一個參數:

      [Parameter]
      public string Title { get; set; }

      通過用Parameter修飾符來修飾,就可以將指定的屬性(注意要是public的)聲明為參數,使用也很簡單:

      <SurveyPrompt Title="這里是參數的值" />

      2. CaptureUnmatchedValues

      是 Blazor 中一個非常有用的特性,它允許組件捕獲所有未匹配到組件參數的額外屬性。

      基本概念

      當你在組件上設置了屬性,但這些屬性沒有對應的 時, 可以捕獲這些"未匹配"的屬性,而且修飾的屬性必須要是字典類型:IDictionary<string,object>。

      基本用法

      <!-- MyComponent.razor -->
      <div @attributes="AdditionalAttributes">
          組件內容
      </div>
      
      @code {
          [Parameter(CaptureUnmatchedValues = true)]
          public Dictionary<string, object> AdditionalAttributes { get; set; } = 
              new Dictionary<string, object>();
      }

      使用場景示例

      1. 創建可復用的按鈕組件
      <!-- MyButton.razor -->
      <button @attributes="AdditionalAttributes" class="btn @Class">
          @ChildContent
      </button>
      
      @code {
          [Parameter]
          public string Class { get; set; } = string.Empty;
          
          [Parameter]
          public RenderFragment? ChildContent { get; set; }
          
          [Parameter(CaptureUnmatchedValues = true)]
          public Dictionary<string, object> AdditionalAttributes { get; set; } = 
              new Dictionary<string, object>();
      }

      使用方式

      <MyButton class="btn-primary"
                id="submit-btn"
                onclick="console.log('clicked me')"
                data-custom="value">
          點擊我
      </MyButton>
      2.包裝第三方組件
      <!-- WrapperComponent.razor -->
      <ThirdPartyComponent @attributes="AdditionalAttributes" 
                           SpecificParameter="@SpecificValue" />
      
      @code {
          [Parameter]
          public string SpecificValue { get; set; } = string.Empty;
          
          [Parameter(CaptureUnmatchedValues = true)]
          public Dictionary<string, object> AdditionalAttributes { get; set; } = 
              new Dictionary<string, object>();
      }

      實際應用案例

      創建靈活的容器組件
      <!-- FlexContainer.razor -->
      <div @attributes="AdditionalAttributes" class="flex-container @Class">
          @ChildContent
      </div>
      
      @code {
          [Parameter]
          public string Class { get; set; } = string.Empty;
          
          [Parameter]
          public RenderFragment? ChildContent { get; set; }
          
          [Parameter(CaptureUnmatchedValues = true)]
          public Dictionary<string, object> AdditionalAttributes { get; set; } = 
              new Dictionary<string, object>();
      }

      使用示例:

      <FlexContainer class="my-styles" 
                     id="main-container"
                     style=""
                     data-tracking="user-section"
                     aria-label="主要區域">
          <p>這里是可以自定義樣式的容器內容</p>
      </FlexContainer>

      3. 級聯參數(CascadingParameter)

      級聯參數看起來就比Parameter更高級,主要用來在多級組件之間傳遞參數,聽起來有點抽象,咱們舉個栗子:

      考慮以下三個組件的嵌套關系,如果想把一個參數同時傳給CascComp1和CascComp2應該如何做呢?

      三個組件的嵌套關系

      如果采用普通的Parameter,代碼應該是這樣的:

      <!--this is CascCompSample.razor-->
      
      <h3>This is the sample page</h3>
      <CascComp1 NickName="沈先生"></CascComp1>
      <!--this is CascComp1.razor-->
      
      <h3>Comp1: @NickName</h3>
      
      <CascComp2 NickName="@NickName"></CascComp2>
      @code {
          [Parameter]
          public string NickName { get; set; }
      }
      <!--this is CascComp2.razor-->
      
      <h3>Comp2: @NickName</h3>
      
      @code {
          [Parameter]
          public string NickName { get; set; }
      }

      采用CascadingParameter會有什么不一樣呢?請看:

      <!--this is CascCompSample.razor-->
      
      @page "/cascparamsample"
      
      <h3>This is the sample page</h3>
      
      <CascadingValue Value="NickName">
          <CascComp1></CascComp1>
      </CascadingValue>
      
      @code
      {
          private string NickName = "沈先生";
      }
      <!--this is CascComp1.razor-->
      
      <h3>Comp1: @NickName</h3>
      
      <CascComp2></CascComp2>
      
      @code {
          [CascadingParameter]
          public string NickName { get; set; }
      }
      <!--this is CascComp2.razor-->
      
      <h3>Comp2: @NickName</h3>
      
      @code {
          [CascadingParameter]
          public string NickName { get; set; }
      }

      看到區別了嗎?

      • 首先在CascCompSample.razor頁面,我們通過把CascComp1嵌套到CascadingValue里面來傳遞參數。
      • 其次在CascComp1和CascComp2,不再需要顯式傳遞參數,只需要聲明CascadingParameter即可拿到值。

      CascadingValue組件的Value參數不能直接傳遞字符串,必須要聲明一個變量

      那么什么場景下需要用到這種方式呢?我想比較多的還是用來在多個組件之間共享上下文吧。

      4. CascadingParameter如何傳遞多個參數

      前面的例子我們通過CascadingParameter傳遞了一個參數,那么有沒有辦法傳遞多個參數呢?

      當然可以,CascadingValue是支持嵌套的,你可以這樣:

      <!--this is CascCompSample.razor-->
      
      @page "/cascparamsample"
      
      <h3>This is the sample page</h3>
      
      <CascadingValue Value="NickName">
          <CascadingValue Value="36">
              <CascComp1></CascComp1>
          </CascadingValue>
      </CascadingValue>
      
      @code
      {
          private string NickName = "沈先生";
      }
      <!--this is CascComp1.razor-->
      
      <h3>Comp1: @NickName - @Age</h3>
      
      <CascComp2></CascComp2>
      
      @code {
          [CascadingParameter]
          public string NickName { get; set; }
          [CascadingParameter]
          public int Age { get; set; }
      }

      Blazor是通過參數的類型來關聯的,在外層通過CascadingValue傳遞了一個字符串和一個整數,在里層則通過類型匹配將字符串賦值給NickName,將整數賦值給Age。所以里層的參數名是可以隨便取的,你可以把NickName改為FullName,并不會影響參數值的獲取。

      這個方式雖然可以少寫一些代碼,但是容易出錯,而且如果碰到多個同類型的參數就無法處理了,筆者并不建議用這種方式。

      除此之外,CascadingValue還有一個Name參數,可以給每個參數指定參數名,這樣就可以顯式的把各個組件的參數關聯起來,筆者建議不管是一個參數還是多個參數都指定一個名字,這樣可以盡量避免混淆,代碼如下:

      <!--this is CascCompSample.razor-->
      
      @page "/cascparamsample"
      
      <h3>This is the sample page</h3>
      
      <CascadingValue Value="NickName" Name="NickName">
          <CascadingValue Value="36" Name="Age">
              <CascadingValue Value="Sex" Name="Sex">
                  <CascComp1></CascComp1>
              </CascadingValue>
          </CascadingValue>
      </CascadingValue>
      
      @code
      {
          private string NickName = "沈先生";
      }
      <!--this is CascComp1.razor-->
      
      <h3>Comp1: @NickName  - @Sex - @Age</h3>
      
      <CascComp2></CascComp2>
      
      @code {
          [CascadingParameter(Name="NickName")]
          public string NickName { get; set; }
          [CascadingParameter(Name = "Sex")]
          public string? Sex { get; set; }
          [CascadingParameter(Name="Age")]
          public int Age { get; set; }
      }

      需要注意的是如果在CascadingValue組件里面指定了Name參數,那么在所有CascadingParameter的地方也需要指定Name,否則就會找不到參數值。

      posted @ 2025-10-20 08:31  碼農剛子  閱讀(273)  評論(1)    收藏  舉報
      主站蜘蛛池模板: 少妇真人直播免费视频| 日本一卡2卡3卡四卡精品网站 | 亚洲va中文字幕无码久久不卡| 免费AV片在线观看网址| 亚洲高清成人av在线| 亚洲av无码一区二区三区网站| 亚洲国产成人久久精品APP| 亚洲色成人一区二区三区| 日韩熟女乱综合一区二区| 亚洲成a人无码av波多野| 天堂网亚洲综合在线| 办公室强奷漂亮少妇同事| 欧美成人精品手机在线| 四虎永久免费高清视频| 久久99精品国产自在现线小黄鸭| 久久亚洲日韩精品一区二区三区 | 亚洲爆乳精品无码一区二区| 国产熟睡乱子伦视频在线播放| 久久99久国产麻精品66| 国产美女直播亚洲一区色| 99热精品毛片全部国产无缓冲 | 欧美国产亚洲日韩在线二区| 色爱综合另类图片av| 利川市| 亚洲av永久一区二区| 国产日韩av二区三区| 亚洲成人高清av在线| 日韩午夜福利片段在线观看| 嫖妓丰满肥熟妇在线精品| 亚洲无人区码二码三码区| 国产激情艳情在线看视频| 久热这里只有精品12| 五月丁香激激情亚洲综合| 亚洲熟妇无码爱v在线观看| 国产成人精品亚洲午夜| 国语精品国内自产视频| 国产无遮挡性视频免费看| 亚洲国产高清精品线久久| 国产精品露脸视频观看| 国产一区二区波多野结衣| 综合偷自拍亚洲乱中文字幕|