跳到主要内容

2.2 XAML 与代码后置

学习目标​

本节练习阅读 XAML 对象树、区分属性赋值与属性元素,并使用 x:Name 和事件连接 C#。完成后,你应能从一个控件的声明找到它的类型、属性及交互行为,而不把 XAML 当作带有特殊标签的 HTML。

XAML 声明的是对象​

XAML 是声明 .NET 对象及其关系的语言。TextBox 标签对应一个控件对象,Text="学习绑定" 设置它的 Text 属性。它没有浏览器 DOM,也不使用 CSS 选择器。窗口里的布局、绘制和输入由 WPF 完成;和 Razor 相比,XAML 通常不是一次请求中生成的字符串,而是创建持续存在的 UI 对象。

默认 XML 命名空间让你直接写 Window、Grid 等 WPF 类型。带 x: 前缀的命名空间提供 XAML 语言功能,例如 x:Class 指定关联的 C# 类,x:Name 为对象命名。命名空间地址是类型映射标识,不是程序启动时必须访问的网络地址。

MainWindow.xaml 与 MainWindow.xaml.cs 共同组成一个 partial 类。构建工具生成的部分负责连接控件字段和事件,手写部分负责行为。不要编辑 obj 目录中的生成文件,它们会在下一次构建时被覆盖。

一个能直接运行的交互窗口​

沿用上一节的 WpfBasics 项目和模板 App 文件。下面替换两个完整的 MainWindow 文件,不需要额外安装包。

MainWindow.xaml(完整文件)
<Window x:Class="WpfBasics.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="理解 XAML" Width="560" Height="320">
<Grid Margin="24">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>

<TextBlock Text="请输入你的称呼" Margin="0,0,0,8" />
<TextBox x:Name="NameInput" Grid.Row="1"
Text="后端开发者" Margin="0,0,0,12" />
<Button Grid.Row="2" Content="打招呼" Width="100"
HorizontalAlignment="Left"
Click="OnGreetClick" />
<TextBlock x:Name="GreetingText" Grid.Row="3"
Margin="0,16,0,0" TextWrapping="Wrap"
FontSize="20">
<TextBlock.Foreground>
<SolidColorBrush Color="#245AC4" />
</TextBlock.Foreground>
</TextBlock>
</Grid>
</Window>
MainWindow.xaml.cs(完整文件)
using System.Windows;

namespace WpfBasics;

public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}

private void OnGreetClick(object sender, RoutedEventArgs e)
{
string name = NameInput.Text.Trim();
GreetingText.Text = string.IsNullOrEmpty(name)
? "请先输入称呼。"
: $"你好,{name}!";
}
}

先调用 InitializeComponent(),然后才能使用 NameInput 或 GreetingText。Click="OnGreetClick" 指定这个窗口类中的处理方法;运行时点击按钮,WPF 会调用它。事件签名中的 RoutedEventArgs 暂时按事件参数理解,路由事件章节会说明事件如何向父级传播。

三种常见属性写法​

简单属性可以用属性语法。Margin="0,16,0,0" 会经类型转换得到 Thickness,四个数依次代表左、上、右、下;Foreground="#245AC4" 也可以经转换得到画刷。

复杂对象适合属性元素语法。示例中的 TextBlock.Foreground 不是子控件,而是“设置这个 TextBlock 的 Foreground 属性”,内部创建 SolidColorBrush。同样,Grid.RowDefinitions 是行定义集合。属性元素让嵌套对象和集合更容易看清楚。

Grid.Row="1" 是附加属性:属性由 Grid 定义,却写在子控件上,告诉父容器如何放置子控件。索引从 0 开始。它与 C# 扩展方法不是同一概念,也不要求 TextBox 自己声明一个普通的 Row 属性。

后续会见到 Text="{Binding Title}" 这样的标记扩展。花括号说明该值需要由扩展处理,而不是普通字符串;绑定会创建连接对象。若真要显示以左花括号开头的字面量,可以使用 XAML 的转义形式,例如 Text="{}{示例}"。此处先识别语法,绑定机制见下一组教程。

代码后置应该放什么​

刚开始学习时,直接在代码后置读写控件能帮助你验证机制。焦点管理、窗口拖动、纯视觉行为也适合留在视图层。随着界面复杂,数据状态和业务流程若散落在点击事件里,就会难以复用和测试;MVVM 架构会把这些职责整理到独立对象中。

代码后置与 MVVM 并不互相排斥。当前目标是理解对象创建与事件连接,不必为了显示一句欢迎语立即引入依赖注入、消息总线或源生成器。

常见错误​

  • 名称存在却提示字段不存在:核对 x:Name 拼写、x:Class 和项目构建结果;普通的 Name 也能在部分场景命名,但入门统一使用 x:Name。
  • 事件方法找不到:处理方法必须位于关联的窗口类中,名字与 XAML 完全一致,参数类型符合事件要求。
  • 把多个控件直接放进窗口:Window.Content 只能承载一个内容对象,多个控件需要放到布局容器中。
  • 构造函数先访问控件:生成的字段尚未初始化,可能得到空引用;先初始化组件,再设置初始状态。

动手练习与验收​

增加一个“清空”按钮,处理方法将 NameInput.Text 和 GreetingText.Text 都设为空字符串,并调用 NameInput.Focus()。新按钮可以放在另一行,也可以与打招呼按钮共用一个水平容器。

验收标准:输入称呼后显示对应问候;输入纯空格时提示补充;点击清空后输入框重新获得焦点。你应能指出示例中的普通属性、属性元素和附加属性各在哪里。

官方延伸资料​