Blazor進階:隱含參數Context

為工作紀錄。Context, RenderFragment<TValue>, Blazor Advance。

隱含參數ContextChildContent搭配運用

型別 RenderFragment<TValue> 的具有名為 context 的隱含參數,可與另一個隱含參數ChildContent搭配使用把元件內層的狀態數值傳遞給外層的主程式來呈現的,由主程式決定如何呈現UI。常用的案例是樣板化元件TableTemplate。

參考文件

TableTemplate

一個簡單的範例:MyTimer 時鐘

Shared\MyTimer.razor
@implements IDisposable

<MudPaper Elevation=25 Class="pa-4 ma-2">
  @* 把內部狀態透過 Context 機制送到外顯層再加工。 *@
  @ChildContent(now)
</MudPaper>

@code {
  [Parameter] public RenderFragment<DateTime> ChildContent { get; set; }

  /// State
  DateTime now = DateTime.Now;

  /// Resource
  System.Timers.Timer timer;

  public void Dispose()
  {
    /// ※ 別忘記要釋放 timer 否則會變成無人管理的流浪資源。
    timer?.Dispose();
  }

  protected override void OnAfterRender(bool firstRender)
  {
    base.OnAfterRender(firstRender);

    /// ※ timer 的初始化在 firstRender 執行
    if (firstRender)
    {
      timer = new();
      timer.Interval = 1000;
      timer.Elapsed += OnTimerInterval;
      timer.AutoReset = true;
      // Start the timer
      timer.Enabled = true;
    }
  }

  void OnTimerInterval(object sender, System.Timers.ElapsedEventArgs e)
  {
    now = DateTime.Now;
    StateHasChanged();
  }
}
FooPage.razor
@page "/fooPage"
<PageTitle>我的時鐘</PageTitle>

<MudContainer>
  <MyTimer Context="now">
    <div style="border: dashed thin pink;">
      @now.ToString("HH:mm:ss")
    </div>
    @* 由主程式決定如何呈現UI。 *@
  </MyTimer>
  ...
</MudContainer>

@code {
  ...     
}

另一個範例:HighOrderTable

HihgOrderTable.razor
@* 客製化高階 Table 元件 *@
@typeparam T
@attribute [CascadingTypeParameter(nameof(T))]

<MudTableEx T=T Items=DataList Hover Striped Dense LoadingProgressColor=Color.Info 
            HeaderClass="custom-dark" FixedHeader Height="calc(100vh - 330px)" RowClassFunc=@RowClassFunc>
  <HeaderContent>
    <MudTh>
      <MudCheckBox T="bool" CheckedChanged=HandleCheckAll></MudCheckBox>
    </MudTh>
    @HeaderContent
  </HeaderContent>
  <RowTemplate>
    <MudTd>
      <MudCheckBox T="bool" Checked=@(CheckedList.Contains(context)) CheckedChanged="isChecked => HandleCheckedChanged(isChecked, context)"></MudCheckBox>
    </MudTd>
    @RowTemplate(context)
  </RowTemplate>
</MudTableEx>

@code {
  [Parameter] public RenderFragment HeaderContent { get; set; }
  [Parameter] public RenderFragment<T> RowTemplate { get; set; }
  [Parameter] public List<T> DataList { get; set; }
  [Parameter] public HashSet<T> CheckedList { get; set; }
  [Parameter] public EventCallback<HashSet<T>> CheckedListChanged { get; set; }
  [Parameter] public Func<T, System.Int32, string> RowClassFunc { get; set; }

  void HandleCheckAll(bool isCheck)
  {
    if (isCheck)
    { 
      foreach (var item in DataList)
      {
        if (!CheckedList.Contains(item))
          CheckedList.Add(item);
      }
    }
    else
    {
      CheckedList.Clear();
    }

    // notify out
    CheckedListChanged.InvokeAsync(CheckedList);
  }

  void HandleCheckedChanged(bool isChecked, T item)
  {
    if (CheckedList.Contains(item))
      CheckedList.Remove(item);
    else
      CheckedList.Add(item);

    // notify out
    CheckedListChanged.InvokeAsync(CheckedList);
  }
}
@page "/test004"

<PageTitle>測試客製化高階Table元件</PageTitle>

<style>
    .yellow-card {
        background-color: var(--mud-palette-tertiary) !important;
    }

        .yellow-card > td {
            color: white !important;
        }

            .yellow-card > td .mud-input {
                color: white !important;
            }
</style>

<MudContainer MaxWidth=MaxWidth.False>

  <HihgOrderTable T=TestProduct DataList=@dataList @bind-CheckedList=@chkList RowClassFunc=YellowRowClassFunc >
    <HeaderContent>
        <MudThEx For=@(m=>m.ProductNo) />
        <MudThEx For=@(m=>m.ProductName) />
        <MudThEx For=@(m=>m.Price) />
    </HeaderContent>
    <RowTemplate>
        <MudTd>@context.ProductNo</MudTd>
        <MudTd>@context.ProductName</MudTd>
        <MudTd>@context.Price</MudTd>
    </RowTemplate>
  </HihgOrderTable>

</MudContainer>

@* for debug *@
<MudText Inline>
    Checked items[@(chkList.Count)]: @(chkList == null ? "" : string.Join(", ", chkList.Select(x => x.ProductNo)))
</MudText>

@code {
    // State
    List<TestProduct> dataList = new();
    HashSet<TestProduct> chkList = new();

    protected override void OnInitialized()
    {
        base.OnInitialized();
        
        // 產生測試資料
        for (var i = 0; i < 10; i++)
        {
            dataList.Add(new TestProduct()
            {
                ProductNo = (i + 1).ToString(),
                ProductName = $"{i + 1}號蘋果",
                Price = $"{i + 1}",
                LayoutCode = (i % 2 == 0 ? "W1" : "Y1")
            });
        }
    }

    string YellowRowClassFunc(TestProduct item, int rowNumber)
    {
        return item.LayoutCode.StartsWith("Y") ? "yellow-card" : String.Empty;
    }
}

Last updated