Skip to Content
Week 08📋 Week 08 阶段检查

Week 08 - 阶段检查

这一页用来确认 Week 8 的界面组织能力是否跑通。检查方式是重做三个小窗口:Style、DataTemplate、UserControl。

本周学习地图

阶段先掌握什么最终能写什么
Style控件外观复用能统一按钮、输入框、文字
DataTemplate数据项显示结构能让商品列表不显示类型名
ControlTemplate控件内部结构能理解何时重写控件结构
UserControl可复用界面块能抽出商品卡片
Theme资源和样式拆分能管理全局外观
UI 状态空、加载、错误、禁用能让页面状态清楚

本周自测项目:商品卡片组件

从一个商品列表页面开始整理界面,至少包含:

  • 一个商品 DataTemplate
  • 一个可复用 ProductCard
  • 页面显示空状态、加载状态、错误提示。
  • 按钮样式只做最少统一,不影响默认可用性。
  • 列表项显示名称、价格、库存状态。

过关标准:能解释什么时候用 Style、DataTemplate、UserControl,不能混用。

上位机阶段验收:可复用的工业监控界面

本周目标: 用 Style、DataTemplate 和 UserControl 统一设备、状态和报警展示。

必须亲手完成:

  1. 在线、离线、报警同时用文字和颜色表达
  2. DeviceCard 可显示任意设备
  3. 空状态、加载状态和失败状态都有反馈

过关标准: 新增设备不需要复制一整段界面代码,所有页面视觉和操作含义一致。

不要只看答案。新建一个空项目重做一次,运行成功后再故意改坏一处并自己排错。

本周流程图

页面出现重复外观 -> Style 统一控件属性 -> 列表项显示重复 -> DataTemplate 统一数据展示 -> 页面块重复 -> UserControl 封装可复用组件 -> 空、加载、错误、禁用状态由 ViewModel 控制

第一部分:本周原理地图

Week 8 的主线是:减少界面重复,让外观、数据项显示、控件结构、组件复用各有固定位置。

本周能力拆解

能力你要能做到什么
Style统一控件外观
DataTemplate统一数据项显示结构
ControlTemplate改控件内部结构并保留内容显示
UserControl封装可复用界面块
Theme拆分全局资源和样式文件
UI 状态显示空、加载、错误、禁用状态

本周最重要的判断表

需求应该用
多个按钮颜色一致Style
商品列表每项显示三行DataTemplate
按钮内部结构要重写ControlTemplate
商品卡片多个页面复用UserControl
全局颜色统一管理Themes/Colors.axaml
没数据时显示提示IsEmpty + IsVisible

Week 8 必须会用的写法

主题必须会的写法
资源x:KeyStaticResource
样式Style SelectorClasses
数据模板DataTemplatex:DataType
列表模板ListBox.ItemTemplate
控件模板ControlTemplateTemplateBinding
用户控件UserControlxmlns:views
主题拆分全局资源、页面资源
状态体验IsVisibleIsEnabledMessageErrorMessage

判断优先级:

默认样式能解决 -> 不自定义 数据项重复显示 -> DataTemplate 界面块重复出现 -> UserControl 外观值重复很多 -> Resource 或 Style 控件内部结构必须改变 -> ControlTemplate

第二部分:阶段检查 1 - Style

目标:确认你能用 StyleClasses 统一按钮外观。

Views/MainWindow.axaml 完整答案

<Window xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" x:Class="ProductApp.Views.MainWindow" Width="480" Height="260" Title="Style 检查"> <Window.Styles> <Style Selector="Button.primary"> <Setter Property="Background" Value="#2563EB" /> <Setter Property="Foreground" Value="White" /> <Setter Property="Padding" Value="12,6" /> </Style> </Window.Styles> <StackPanel Margin="20" Spacing="10"> <Button Classes="primary" Content="保存" /> <Button Content="取消" /> </StackPanel> </Window>

必须能解释

代码解释
Style Selector="Button.primary"只影响带 primary 类的按钮
Classes="primary"让这个按钮套用 primary 样式
普通取消按钮没有 class,所以保持默认外观

第三部分:阶段检查 2 - DataTemplate

目标:确认你能让列表里的对象按模板显示。

ViewModels/MainWindowViewModel.cs 完整答案

using System.Collections.ObjectModel; namespace ProductApp.ViewModels; public partial class MainWindowViewModel : ViewModelBase { public ObservableCollection<ProductItem> Products { get; } = new() { new ProductItem { Name = "Keyboard", Price = 199 }, new ProductItem { Name = "Mouse", Price = 99 } }; } public sealed class ProductItem { public string Name { get; set; } = ""; public decimal Price { get; set; } }

Views/MainWindow.axaml 完整答案

<Window xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:vm="using:ProductApp.ViewModels" x:Class="ProductApp.Views.MainWindow" x:DataType="vm:MainWindowViewModel" Width="520" Height="320" Title="DataTemplate 检查"> <ListBox Margin="20" ItemsSource="{Binding Products}"> <ListBox.ItemTemplate> <DataTemplate x:DataType="vm:ProductItem"> <StackPanel Spacing="4"> <TextBlock Text="{Binding Name}" FontWeight="Bold" /> <TextBlock Text="{Binding Price}" /> </StackPanel> </DataTemplate> </ListBox.ItemTemplate> </ListBox> </Window>

必须能解释

代码解释
ItemsSource="{Binding Products}"ListBox 显示商品集合
DataTemplate x:DataType="vm:ProductItem"模板内部绑定当前商品
Text="{Binding Name}"绑定当前商品的名称

第四部分:阶段检查 3 - UserControl

目标:确认你能封装一个可复用商品卡片,并在页面使用它。

Views/ProductCard.axaml 完整答案

<UserControl xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" x:Class="ProductApp.Views.ProductCard" x:Name="Root"> <Border BorderBrush="#DDDDDD" BorderThickness="1" Padding="12" Margin="0,0,0,8"> <StackPanel Spacing="4"> <TextBlock Text="{Binding Title, ElementName=Root}" FontWeight="Bold" /> <TextBlock Text="{Binding Price, ElementName=Root}" /> </StackPanel> </Border> </UserControl>

Views/ProductCard.axaml.cs 完整答案

using Avalonia; using Avalonia.Controls; namespace ProductApp.Views; public partial class ProductCard : UserControl { public static readonly StyledProperty<string> TitleProperty = AvaloniaProperty.Register<ProductCard, string>(nameof(Title), ""); public static readonly StyledProperty<decimal> PriceProperty = AvaloniaProperty.Register<ProductCard, decimal>(nameof(Price)); public string Title { get => GetValue(TitleProperty); set => SetValue(TitleProperty, value); } public decimal Price { get => GetValue(PriceProperty); set => SetValue(PriceProperty, value); } public ProductCard() { InitializeComponent(); } }

Views/MainWindow.axaml 完整答案

<Window xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:views="using:ProductApp.Views" x:Class="ProductApp.Views.MainWindow" Width="520" Height="320" Title="UserControl 检查"> <StackPanel Margin="20" Spacing="8"> <views:ProductCard Title="Keyboard" Price="199" /> <views:ProductCard Title="Mouse" Price="99" /> </StackPanel> </Window>

必须能解释

代码解释
StyledProperty让外部 XAML 可以给组件传值
ElementName=RootUserControl 内部绑定自己的属性
xmlns:views让 MainWindow 能使用 ProductCard

第五部分:常见错误完整修法

现象原因修法
样式不生效selector 或 class 不匹配检查 Button.primaryClasses="primary"
DataTemplate 绑定空白模板内部类型不明确x:DataType="vm:ProductItem"
按钮模板文字消失ControlTemplate 没有 ContentPresenterContentPresenter Content="{TemplateBinding Content}"
UserControl 属性不显示内部绑定到外部 DataContext改成 ElementName=Root
主题文件找不到Include 路径错检查 avares://ProductApp/Themes/...
空状态不刷新集合变化后没通知计算属性调用 OnPropertyChanged(nameof(IsEmpty))

第六部分:通过标准

达到下面标准,再进入 Week 9。

  1. 能用 StyleClasses 复用控件外观。
  2. 能用 DataTemplate 显示对象列表。
  3. 能解释模板内部绑定的是当前项。
  4. 能写一个最小 ControlTemplate 并保留 ContentPresenter
  5. 能写一个带 StyledProperty 的 UserControl。
  6. 能用 ResourceIncludeStyleInclude 拆分主题文件。
  7. 能用 ViewModel 状态显示空状态和加载状态。
  8. 能判断一个界面问题该用 Style、DataTemplate、ControlTemplate 还是 UserControl。

Week 8 的通过标准是:界面不再靠复制粘贴堆出来,而是能按职责拆成样式、模板、组件和状态。