我有一个 Blazor 服务器端应用程序,试图了解其结构。在 MainLayout.razor 页面中,我看到标签 @Body,这是渲染每个页面内容的地方。
我想知道,是否可以向 mainLayout 页面添加额外的渲染元素?例如,@Header 部分。我更愿意在每个单独的页面中定义此部分。
换句话说,对于每个页面,除了主要内容之外,还需要定义 Header、Footer 或我在 MainLayout 中定义的任何渲染元素。这样,我可以自定义每个页面唯一的页眉/页脚元素。
感谢您的帮助。
MainLayout.razor
注意: 使用一种方法来更新渲染片段字段,我特意将其设为私有,然后调用
StateHasChanged()
。可以轻松创建其他方法来清除或设置其他字段。
@inherits LayoutComponentBase
<CascadingValue Value="this">
<div class="page">
<div class="sidebar">
<NavMenu />
</div>
<div class="main">
<div class="top-row px-4">
@header
</div>
<div class="content px-4">
@Body
@footer
</div>
</div>
</div>
</CascadingValue>
@code {
private RenderFragment header;
private RenderFragment footer;
public void SetHeaderAndFooter(RenderFragment header, RenderFragment footer)
{
this.header = header;
this.footer = footer;
StateHasChanged();
}
}
LayoutSetter.cs
public class LayoutSetter : ComponentBase
{
[CascadingParameter]
public MainLayout Layout { get; set; }
[Parameter]
public RenderFragment Header { get; set; }
[Parameter]
public RenderFragment Footer { get; set; }
protected override void OnInitialized()
{
Layout.SetHeaderAndFooter(Header, Footer);
}
}
在任何页面上...
@page "/"
<h1>Hello, world!</h1>
<LayoutSetter>
<Header>
Hello
</Header>
<Footer>
Goodbye
</Footer>
</LayoutSetter>
MainLayout.razor
@inherits LayoutComponentBase
<div class="sidebar">
<AppNavigation />
</div>
<div class="main">
<div class="top-row px-4">
<AppHeader />
</div>
<div class="content px-4">
@Body
</div>
<div class="bottom-row px-4">
<AppFooter />
</div>
</div>
AppHeader.razor
@implements IDisposable
@LayoutService.Header
@code {
[Inject]
public ILayoutService LayoutService { get; set; }
protected override void OnInitialized()
{
LayoutService.PropertyChanged += LayoutService_PropertyChanged;
base.OnInitialized();
}
private void LayoutService_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(ILayoutService.Header))
{
StateHasChanged();
}
}
public void Dispose()
{
if (LayoutService != null)
{
LayoutService.PropertyChanged -= LayoutService_PropertyChanged;
}
}
}
ILayoutService.cs
public interface ILayoutService
{
RenderFragment Header { get; }
SetHeader HeaderSetter { get; set; }
event PropertyChangedEventHandler PropertyChanged;
void UpdateHeader();
}
LayoutService.cs
public class LayoutService : ILayoutService, INotifyPropertyChanged
{
public RenderFragment Header => HeaderSetter?.ChildContent;
public SetHeader HeaderSetter
{
get => headerSetter;
set
{
if (headerSetter == value) return;
headerSetter = value;
UpdateHeader();
}
}
public void UpdateHeader() => NotifyPropertyChanged(nameof(Header));
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
=> PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
private SetHeader headerSetter;
}
SetHeader.cs
public class SetHeader : ComponentBase, IDisposable
{
[Inject]
private ILayoutService Layout { get; set; }
[Parameter]
public RenderFragment ChildContent { get; set; }
protected override void OnInitialized()
{
if (Layout != null)
{
Layout.HeaderSetter = this;
}
base.OnInitialized();
}
protected override bool ShouldRender()
{
var shouldRender = base.ShouldRender();
if (shouldRender)
{
Layout.UpdateHeader();
}
return shouldRender;
}
public void Dispose()
{
if (Layout != null)
{
Layout.HeaderSetter = null;
}
}
}
就我而言
Program.cs
builder.Services.AddScoped<ILayoutService, LayoutService>();
@page "/counter"
<h1>Counter</h1>
<p>Current count: @currentCount</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
<SetHeader>
<p>Hello Current count: @currentCount</p>
</SetHeader>
<SetFooter>
<p>Goodbye Current count: @currentCount</p>
</SetFooter>
@code {
private int currentCount = 0;
private void IncrementCount() => currentCount++;
}
这也会清除导航上的值,因为我正在使用
IDisposable
。 Setter 不必设置(Null 也可以)。如果使用多个 setter,则最新的优先。我还没有测试过动态删除一页上的多个设置器。
这是一个使用 WebAssembly 3.2.1 的 repo
sections。 如果我们想制作一张引导卡,请创建如下布局:
@using Microsoft.AspNetCore.Components.Sections
@inherits LayoutComponentBase
<div class="card" style="width: 18rem;">
<div class="card-header">
<SectionOutlet SectionName="header"/>
</div>
<div class="card-body">
@Body
</div>
<div class="card-footer">
<SectionOutlet SectionName="footer"/>
</div>
然后我们就可以创建一个页面了:
<SectionContent SectionName="header">
<h3>An interesting title.</h3>
</SectionContent>
<p>This will be the body text of the card.</p>
<SectionContent SectionName="footer">
<p>Some footer content</p>
</SectionContent>
祝你好运!