Week 06 - 阶段检查
这一页用来确认 Week 6 是否已经变成可动手的能力。检查方式不是背概念,而是重新跑通三个窗口:布局窗口、按钮表单、列表详情。
本周学习地图
| 阶段 | 先掌握什么 | 最终能写什么 |
|---|---|---|
| 启动 | Avalonia 模板、Program.cs、App.axaml | 能创建并运行窗口 |
| 布局 | Grid、StackPanel、Border | 能摆出清楚页面结构 |
| 控件 | TextBlock、TextBox、Button、ListBox | 能完成输入、点击、列表 |
| 表单 | 读取控件值、校验、显示结果 | 能写商品表单 |
| 列表 | 列表、选中项、详情区域 | 能做列表详情交互 |
| 过渡 | code-behind 到 MVVM | 能说明后面为什么要迁移 |
本周自测项目:Avalonia 商品窗口
从 Avalonia 模板开始写一个窗口,至少包含:
- 商品名称、价格、库存输入框。
- 保存按钮和清空按钮。
- 错误提示和成功提示。
- 商品列表。
- 点击列表项后显示详情。
过关标准:能说清 MainWindow.axaml 和 MainWindow.axaml.cs 分别负责什么。
上位机阶段验收:Avalonia 设备配置与监控窗口
本周目标: 从空 Avalonia 项目做出有布局、有控件、有交互的第一版桌面上位机。
必须亲手完成:
- 设备列表与详情左右分栏
- 设备名、IP、端口配置表单
- 开始、停止按钮和清晰状态反馈
过关标准: 窗口在桌面和较小尺寸下都能操作,输入错误有提示,按钮状态正确。
不要只看答案。新建一个空项目重做一次,运行成功后再故意改坏一处并自己排错。
本周流程图
Program.cs 启动应用
-> App.axaml 配置应用资源
-> MainWindow.axaml 描述窗口和控件
-> 用户输入 TextBox
-> 用户点击 Button
-> MainWindow.axaml.cs 读取控件值
-> 校验并更新 TextBlock 或 ListBox第一部分:本周原理地图
Week 6 的主线是:把 C# 代码放进真实桌面窗口里,让用户能看见、输入、点击、选择,并看到处理结果。
本周能力拆解
| 能力 | 你要能做到什么 | 对应页面 |
|---|---|---|
| 创建项目 | 能创建 Avalonia 项目并运行窗口 | Day 1 |
| 写布局 | 能用 Grid、StackPanel、Border 摆区域 | Day 2 |
| 用控件 | 能使用 TextBlock、TextBox、Button、ListBox | Day 3 |
| 管外观 | 能用 Resources 和 Styles 减少重复 | Day 4 |
| 写表单 | 能读取输入、校验、显示错误和成功结果 | Day 5 |
| 做列表详情 | 能选中列表项并显示、编辑当前项 | Day 6 |
| 做复盘 | 能说清楚代码从用户操作到 C# 方法的流程 | Day 7 |
本周必须说清的 10 个关键词
| 关键词 | 完整解释 |
|---|---|
Window | 一个桌面窗口,里面放布局和控件 |
XAML | 用来描述界面结构的标记语言 |
Grid | 按行列分配空间的布局容器 |
StackPanel | 按一个方向排列子控件的布局容器 |
Border | 包住一块内容,提供边框和内边距 |
TextBox | 接收用户文字输入的控件 |
Button.Click | 用户点击按钮后触发的事件 |
x:Name | 给控件起名字,让 C# 能访问它 |
ItemsSource | ListBox 要显示的数据来源 |
SelectedItem | ListBox 当前选中的那一项 |
Week 6 必须会用的 Avalonia 写法
| 主题 | 必须会的写法 |
|---|---|
| 项目运行 | dotnet new avalonia.mvvm、dotnet run、dotnet build |
| 窗口 | Window、Title、Width、Height |
| 布局 | Grid RowDefinitions、ColumnDefinitions、StackPanel Spacing |
| 控件 | TextBlock、TextBox、Button、ListBox、ComboBox |
| 输入提示 | PlaceholderText |
| 绑定文本 | Text="{Binding Name, Mode=TwoWay}" |
| 命令按钮 | Command="{Binding SaveCommand}" |
| 列表数据 | ItemsSource="{Binding Products}" |
| 选中项 | SelectedItem="{Binding SelectedProduct}" |
| 列表刷新 | ObservableCollection<T> |
| 空状态 | IsVisible="{Binding IsEmpty}" |
| 错误提示 | ErrorMessage 绑定到 TextBlock |
本周最重要的数据流
用户看到窗口
-> 用户输入或选择
-> 用户点击按钮或切换列表项
-> Avalonia 执行 C# 事件方法
-> C# 读取控件值
-> C# 校验或修改数据
-> C# 把结果写回界面控件如果这条线说不清,先回看 Day 3、Day 5、Day 6。
第二部分:阶段检查 1 - 布局窗口
目标:确认你能用 Grid 做出“顶部标题 + 左侧列表 + 右侧内容”的页面结构。
MainWindow.axaml 完整答案
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ProductApp.Views.MainWindow"
Width="700"
Height="420"
Title="布局检查">
<Grid RowDefinitions="Auto,*"
ColumnDefinitions="220,*"
RowSpacing="12"
ColumnSpacing="16"
Margin="20">
<TextBlock Grid.Row="0"
Grid.Column="0"
Grid.ColumnSpan="2"
Text="商品管理"
FontSize="24"
FontWeight="Bold" />
<Border Grid.Row="1"
Grid.Column="0"
BorderBrush="#DDDDDD"
BorderThickness="1"
Padding="12">
<StackPanel Spacing="8">
<TextBlock Text="左侧列表"
FontWeight="Bold" />
<ListBox>
<ListBoxItem Content="Keyboard" />
<ListBoxItem Content="Mouse" />
<ListBoxItem Content="Monitor" />
</ListBox>
</StackPanel>
</Border>
<Border Grid.Row="1"
Grid.Column="1"
BorderBrush="#DDDDDD"
BorderThickness="1"
Padding="12">
<StackPanel Spacing="8">
<TextBlock Text="右侧详情"
FontWeight="Bold" />
<TextBlock Text="这里以后显示当前商品详情。" />
</StackPanel>
</Border>
</Grid>
</Window>必须能解释
| 代码 | 解释 |
|---|---|
RowDefinitions="Auto,*" | 第一行按标题高度,第二行占剩余高度 |
ColumnDefinitions="220,*" | 左侧固定 220,右侧占剩余宽度 |
Grid.ColumnSpan="2" | 标题横跨左右两列 |
Border Padding="12" | 边框和内部内容之间留空间 |
第三部分:阶段检查 2 - 按钮表单
目标:确认你能从 TextBox 读取输入,点击按钮后做校验,并显示结果。
MainWindow.axaml 完整答案
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ProductApp.Views.MainWindow"
Width="520"
Height="320"
Title="表单检查">
<StackPanel Margin="20"
Spacing="10">
<TextBlock Text="商品名称" />
<TextBox x:Name="NameBox"
PlaceholderText="请输入商品名称" />
<TextBlock Text="商品价格" />
<TextBox x:Name="PriceBox"
PlaceholderText="请输入价格" />
<Button Content="保存"
Click="OnSaveClick" />
<TextBlock x:Name="ResultText"
Text="保存结果会显示在这里。"
TextWrapping="Wrap" />
</StackPanel>
</Window>MainWindow.axaml.cs 完整答案
using Avalonia.Controls;
using Avalonia.Interactivity;
namespace ProductApp.Views;
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void OnSaveClick(object? sender, RoutedEventArgs e)
{
string name = NameBox.Text?.Trim() ?? "";
if (name.Length == 0)
{
ResultText.Text = "商品名称不能为空。";
return;
}
string priceText = PriceBox.Text?.Trim() ?? "";
if (!decimal.TryParse(priceText, out decimal price))
{
ResultText.Text = "价格必须是数字。";
return;
}
if (price <= 0)
{
ResultText.Text = "价格必须大于 0。";
return;
}
ResultText.Text = $"保存成功:{name},价格 {price}";
}
}必须验证的输入
| 名称 | 价格 | 应该显示 |
|---|---|---|
| 空 | 199 | 商品名称不能为空。 |
Keyboard | abc | 价格必须是数字。 |
Keyboard | 0 | 价格必须大于 0。 |
Keyboard | 199 | 保存成功:Keyboard,价格 199 |
第四部分:阶段检查 3 - 列表详情
目标:确认你能把一组 C# 对象显示到 ListBox,选中一项后显示详情。
MainWindow.axaml 完整答案
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ProductApp.Views.MainWindow"
Width="700"
Height="420"
Title="列表详情检查">
<Grid ColumnDefinitions="220,*"
ColumnSpacing="16"
Margin="20">
<ListBox x:Name="ProductList"
Grid.Column="0"
SelectionChanged="OnProductSelectionChanged" />
<StackPanel Grid.Column="1"
Spacing="10">
<TextBlock Text="商品详情"
FontSize="20"
FontWeight="Bold" />
<TextBlock x:Name="NameText"
Text="名称:" />
<TextBlock x:Name="PriceText"
Text="价格:" />
<TextBlock x:Name="ResultText"
Text="请选择左侧商品。" />
</StackPanel>
</Grid>
</Window>MainWindow.axaml.cs 完整答案
using System.Collections.Generic;
using Avalonia.Controls;
namespace ProductApp.Views;
public partial class MainWindow : Window
{
private readonly List<Product> _products = new()
{
new Product { Name = "Keyboard", Price = 199 },
new Product { Name = "Mouse", Price = 99 },
new Product { Name = "Monitor", Price = 899 }
};
public MainWindow()
{
InitializeComponent();
ProductList.ItemsSource = _products;
ProductList.SelectedIndex = 0;
}
private void OnProductSelectionChanged(object? sender, SelectionChangedEventArgs e)
{
Product? product = ProductList.SelectedItem as Product;
if (product is null)
{
NameText.Text = "名称:";
PriceText.Text = "价格:";
ResultText.Text = "没有选中商品。";
return;
}
NameText.Text = $"名称:{product.Name}";
PriceText.Text = $"价格:{product.Price}";
ResultText.Text = $"当前选中:{product.Name}";
}
private sealed class Product
{
public string Name { get; set; } = "";
public decimal Price { get; set; }
public override string ToString()
{
return $"{Name} - {Price}";
}
}
}必须能解释
| 代码 | 解释 |
|---|---|
ProductList.ItemsSource = _products; | 让列表显示 C# 里的商品集合 |
ProductList.SelectedIndex = 0; | 启动后默认选中第一个商品 |
SelectionChanged="OnProductSelectionChanged" | 选中项变化时执行 C# 方法 |
ProductList.SelectedItem as Product | 把当前选中项转换成 Product |
ToString() | 决定商品对象在列表里显示成什么文字 |
第五部分:常见错误完整修法
| 现象 | 原因 | 修法 |
|---|---|---|
| XAML 报标签错误 | 开始标签和结束标签不匹配 | 从错误行往上找最近的 <Grid>、<StackPanel>、<Border> |
C# 找不到 NameBox | XAML 控件没有 x:Name | 写 x:Name="NameBox" |
C# 找不到 OnSaveClick | XAML 的 Click 名字和 C# 方法名不同 | 两边统一成 OnSaveClick |
C# 找不到 RoutedEventArgs | 缺少事件命名空间 | 加 using Avalonia.Interactivity; |
| 列表显示类型名 | Product 没有重写 ToString() | 添加 public override string ToString() |
| 输入字母导致价格处理失败 | 直接转换数字 | 使用 decimal.TryParse |
| 没有选中商品时报错 | 没判断 SelectedItem 是否为空 | 先判断 product is null |
第六部分:通过标准
达到下面标准,再进入 Week 7。
- 能创建并运行 Avalonia 项目。
- 能写出一个包含
Grid的窗口。 - 能用
TextBox输入数据。 - 能用
Button.Click触发 C# 方法。 - 能用
x:Name在 C# 中访问控件。 - 能用
decimal.TryParse校验数字输入。 - 能把 C# 列表显示到
ListBox。 - 能处理
SelectionChanged。 - 能解释
ItemsSource和SelectedItem的区别。 - 能根据第一条错误信息定位到文件和行号。
通过标准不是完全不犯错,而是能把错误缩小到具体文件、具体行、具体原因,并能把本周三个检查窗口重新跑通。