Skip to content

实践范例:主从视图

Microsoft.UI.Reactor(Reactor)中的主从视图是最经典的多窗格形态:一侧是记录列表, 另一侧是所选记录的详情。整个结构就是为所选 id 准备的一个 UseState, 加上 HStack 中的两个槽位。

原语

槽位 API
选择状态 UseState<int?>
列表渲染 VStack + 键控的 Select(...).ToArray()
选中高亮 逐行 .Background(...)
详情分支 为 null 情形准备 Element 类型的局部变量
布局切分 HStack(0, list, detail)
record Note(int Id, string Title, string Body);

class NoteBrowser : Component
{
    private static readonly Note[] Notes = new[] {
        new Note(1, "Project plan", "Draft the milestone sequence; ship before Friday."),
        new Note(2, "Grocery list", "Bread, olive oil, lemons, parsley, two limes."),
        new Note(3, "Bug triage",   "Refocus on the persistence regression; defer the WinForms host."),
    };

数据层就是普通的 C#。真实应用会从 IDataSource<T>async-resources 数据源拉取笔记;形态保持不变。

选择状态

// Single source of truth for "which note is selected" — the list
// writes to it via the button click; the detail pane reads from it.
// Re-renders are scoped to slots that actually changed.
var (selectedId, setSelectedId) = UseState<int?>(1);
var selected = Notes.FirstOrDefault(n => n.Id == selectedId);

一个 UseState<int?> 持有 id;列表通过按钮点击写入, 详情通过 FirstOrDefault 读取。只有选择真正变化时,两个槽位才会 重渲染。

布局

var list = VStack(2,
    Notes.Select(n =>
        Button(n.Title, () => setSelectedId(n.Id))
            .WithKey(n.Id.ToString())
            .AutomationName(n.Title)
            .HAlign(Microsoft.UI.Xaml.HorizontalAlignment.Stretch)
            .Background(n.Id == selectedId ? Theme.SubtleFill : Theme.SolidBackground)
    ).ToArray()
).Width(200).Padding(8);

Element detail = selected is null
    ? TextBlock("No selection").Opacity(0.6).Padding(20)
    : VStack(8,
        Heading(selected.Title),
        TextBlock(selected.Body).Opacity(0.8)
    ).Padding(20);

return HStack(0, list, detail);

主从视图布局

列表是一个由全宽按钮组成的键控 VStack;被选中的那一行获得 与众不同的背景。详情窗格是条件式的 —— selected is null 时 渲染空状态,否则渲染标题 + 正文。两侧都是普通元素; 不需要中间组件。

提示

只有当第三个组件需要选择状态时,才把它提升到上下文中。 同一个 Render 中的列表与 详情窗格通过局部变量共享选择 —— 在第三个窗格(工具栏、状态栏)想要读写之前, 并不需要 UseContext

把所选记录预先解析一次。 短列表用 FirstOrDefault 就够了; 对于大型目录,请把记录放进 Dictionary<int, Note>,让查找变成 O(1)。

三行数据不要动用 ListView<T> 完整的集合控件要到 50 行以上才值回票价。 在范例这种规模下,用按钮组成的 VStack 就够,而且读起来毫不费力。

后续阅读

  • 集合 —— 当行数增长或每行变得复杂时,升级为 ListView<T>
  • 导航 —— 当详情应该占据独立页面而非同级窗格时。
  • 数据系统 —— 一旦数据层变成网络来源,就把它藏到 IDataSource<T> 之后。
  • 实践范例:登录 —— 姊妹范例 —— 形态相近但 校验面不同。
  • 实践范例索引 —— 回到图库。