一、什么是绑定(一句话理解)
-
最简单的绑定:把
TextBlock绑定到TextBox -
绑定的核心组成(
Source、Path、Target) -
绑定方向(
Mode:OneWay / TwoWay / OneTime) -
通知机制(
INotifyPropertyChanged是灵魂) -
绑定到其他元素 / 相对资源(
ElementName、RelativeSource) -
绑定集合:
ListBox/DataGrid绑定到ObservableCollection<T> -
值转换器(
IValueConverter)
WPF 绑定 = 让界面(View)和一个数据对象(ViewModel / Model)自动保持同步,不用写大量
xx.Text = yy这样的代码。
-
数据变了 → 界面自动变
-
界面改了(比如
TextBox输入) → 数据自动变
二、简单案例:
-
一个
TextBox输入名字 -
一个
TextBlock实时显示相同内容
cs
<StackPanel Margin="20">
<TextBox x:Name="NameInput" Width="200" Margin="5"/>
<TextBlock Text="{Binding Text, ElementName=NameInput}" Margin="5"/>
</StackPanel>
效果
你往 TextBox 打字 → TextBlock 实时变化
解释
-
{Binding Text, ElementName=NameInput}-
Text:TextBox的属性 -
ElementName:告诉绑定去界面上的哪个控件找
-
✅ 这是最简单的 控件→控件绑定
三、绑定的核心组成
| 角色 | 说明 | 例子 |
|---|---|---|
| 目标(Target) | UI 控件属性 | TextBlock.Text |
| 源(Source) | 数据来源 | 一个 C# 对象、另一个控件、静态资源 |
| 路径(Path) | 源对象的具体属性 | UserName |
| 模式(Mode) | 数据传输方向 | TwoWay / OneWay |
cs
<TextBox Text="{Binding Path=UserName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
四、绑定方向(重要)
| Mode | 含义 | 场景 |
|---|---|---|
OneWay |
数据→界面 | 只读展示 |
TwoWay |
数据⇄界面 | 表单编辑 |
OneTime |
只绑一次 | 静态标签 |
OneWayToSource |
界面→数据 | 罕见 |
五、通知机制(INotifyPropertyChanged)
这是 WPF 绑定的灵魂。
场景
你用代码改了数据,界面不更新
👉 因为没有告诉界面"我变了"
错误写法(无通知)
cs
public class Student
{
public string Name { get; set; }
}
正确写法(支持通知)
cs
using System.ComponentModel;
public class Student : INotifyPropertyChanged
{
private string _name;
public string Name
{
get => _name;
set
{
_name = value;
OnPropertyChanged(nameof(Name));
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string prop) =>
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(prop));
}
界面使用(DataContext 是核心)
cs
// 窗口构造函数中
public MainWindow()
{
InitializeComponent();
DataContext = new Student { Name = "张三" };
}
XML
xml
<TextBox Text="{Binding Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
✅ 现在 TextBox 输入
✅ C# 里 student.Name = "李四"
👉 界面都会同步
UpdateSourceTrigger=PropertyChanged
效果 :
你每敲一个字母 → TextBlock 立刻跟着变一个字
数据流向 :
你按 a → 界面立刻把 a 写回 ViewModel.UserName → TextBlock 收到变化通知 → 显示 a
默认值(LostFocus)→ 失去焦点才更新
UpdateSourceTrigger |
比喻 |
|---|---|
PropertyChanged |
微信实时聊天:你每打一个字,对方立刻看到 |
LostFocus(默认) |
发邮件:你写好整封信,点击"发送"后才发出 |
| 值 | 什么时候更新数据源? | 典型场景 |
|---|---|---|
PropertyChanged |
属性每次变化(每按一个键、每动一下滑块) | 实时搜索、实时计算、实时预览 |
LostFocus(默认) |
控件失去焦点(按Tab、点别处) | 表单提交、设置页面 |
Explicit |
手动调用 BindingExpression.UpdateSource() |
复杂验证、批量保存 |
六、绑定到其他元素 / 相对资源
1. 绑定到另一个控件(滑块→文字)
XML
<Slider x:Name="VolumeSlider" Minimum="0" Maximum="100"/>
<TextBlock Text="{Binding Value, ElementName=VolumeSlider}"/>
2. 绑定到自身窗口属性
XML
<TextBlock Text="{Binding Title, RelativeSource={RelativeSource AncestorType=Window}}"/>
3. 绑定到静态资源
XML
<Window.Resources>
<local:Student x:Key="DefaultStudent" Name="王五"/>
</Window.Resources>
<TextBlock Text="{Binding Name, Source={StaticResource DefaultStudent}}"/>
七、绑定集合(最常用)
场景
用户列表显示在 ListBox / DataGrid
增删改自动刷新界面
✅ 必须用 ObservableCollection<T>
cs
public class MainViewModel
{
public ObservableCollection<Student> Students { get; set; }
= new ObservableCollection<Student>();
public MainViewModel()
{
Students.Add(new Student { Name = "张三" });
Students.Add(new Student { Name = "李四" });
}
}
XAML
XML
<ListBox ItemsSource="{Binding Students}" DisplayMemberPath="Name"/>
<DataGrid ItemsSource="{Binding Students}" AutoGenerateColumns="True"/>
✅ 增删改都会自动更新界面
八、值转换器(IValueConverter)
场景
-
性别 0/1 → "男"/"女"
-
金额 int → "¥100.00"
-
bool → Visibility
示例:bool → 是否显示红字
Converter 代码
cs
public class BoolToRedConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return (bool)value ? "Red" : "Black";
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
XAML 使用
XML
<Window.Resources>
<local:BoolToRedConverter x:Key="BoolToRed"/>
</Window.Resources>
<TextBlock Text="危险操作"
Foreground="{Binding IsDangerous, Converter={StaticResource BoolToRed}}"/>
cs
using System;
using System.Windows.Data;
using System.Windows.Media;
public class BoolToRedConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
// value 就是 Binding 传来的 IsDangerous 的值(true/false)
bool isDangerous = (bool)value;
if (isDangerous)
return new SolidColorBrush(Colors.Red); // 危险 → 红色
else
return new SolidColorBrush(Colors.Black); // 安全 → 黑色
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException(); // 这个例子不需要反向转换
}
}
<TextBlock Foreground="{Binding IsDangerous, Converter=...}" />
↑ ↑
| |
目标属性 源属性(路径)
DataContext 指向的对象
↓
找到 IsDangerous 属性
↓
取出值(true / false)
↓
传给 Converter(BoolToRedConverter)
↓
Converter 返回 Brush(红色/黑色)
↓
赋值给 TextBlock.Foreground
九、常见坑与调试技巧(必看)
🔥 坑1:DataContext 没设置
-
界面没有任何数据
-
检查 :
this.DataContext = new XXX()
🔥 坑2:属性是字段
csharp
// ❌ 不行
public string Name;
🔥 坑3:集合变了但没刷新
List<T>→ 改成ObservableCollection<T>
🔥 坑4:对象内部属性变了界面不变
- 类必须实现
INotifyPropertyChanged
🔥 坑5:绑定路径写错(大小写)
- Path 区分大小写
最后总结一句话
WPF 绑定 = 搞清楚三件事:数据源是谁、目标控件是谁、数据变化怎么通知。
你不需要一次记住所有细节,但下面 4 个点是必须反复写才能理解的:
-
DataContext -
INotifyPropertyChanged -
ObservableCollection<T> -
Mode和UpdateSourceTrigger