WPF Binding(数据绑定) 是 MVVM 模式的核心,也是 WPF 最强大的功能之一。它的作用是:把 UI 控件的属性 和 数据源(变量 / 对象)自动关联,一方变化,另一方自动同步,不用手动写代码赋值。
我会用最通俗、最完整、可直接上手的方式给你讲透,包含:原理、语法、模式、转换器、通知、命令、常见问题。
一、Binding 到底是什么?
一句话:Binding = 桥梁 连接 目标(Target:UI 控件属性) ←→ 源(Source:后台数据)
例如:TextBox.Text ← Binding → string Name
你改界面,后台自动变;你改后台,界面自动变。
二、Binding 四大核心组成
任何一个绑定都必须包含这 4 部分:
-
Binding Target(绑定目标) 必须是 **依赖属性(Dependency Property)**如:Text、Width、IsChecked、ItemsSource、Visibility 等
-
**Binding Source(绑定源)**可以是:普通变量、类实例、集合、父控件、自身、资源
-
Binding Path(绑定路径) 要绑定源的哪个属性
-
**Binding Mode(绑定模式)**数据流向:单向 / 双向 / 只读一次等
三、最基础的绑定语法(XAML)
1. 标准写法
<TextBox Text="{Binding 路径, Mode=模式, ...}" />
单向绑定
<CheckBox x:Name="chb_inspectAll" Margin="20,0,0,0" Content="inspect all" IsChecked="{Binding SelectRecipe.IsFullInspect, Mode=OneWay}" />
2. 最简单示例
<TextBox Text="{Binding UserName}" />
表示:Text 属性绑定到当前数据源的 UserName 属性。
四、绑定模式(Binding Mode)必考 / 必用
这是 WPF 面试高频题,也是开发必须懂的。
| 模式 | 说明 | 常用控件 |
|---|---|---|
| TwoWay(默认) | 双向同步:UI 变→数据源变,数据源变→UI 变 | TextBox、CheckBox |
| OneWay | 单向:数据源变→UI 变 | TextBlock、ProgressBar |
| OneWayToSource | 反向:UI 变→数据源变 | 极少用 |
| OneTime | 只初始化一次,之后不同步 | 一次性显示数据 |
| Default | 根据控件自动决定 | 文本框双向,文本块单向 |
最常用:TwoWay / OneWay
五、数据源来源(Source)6 种最常用写法
1. 绑定到自身(RelativeSource Self)
控件自己的属性绑定给自己的另一个属性:
<Width="{Binding ActualHeight, RelativeSource={RelativeSource Self}}" />
2. 绑定到父控件(AncestorType)
Text="{Binding DataContext.Title, RelativeSource={RelativeSource AncestorType=Window}}"
3. 绑定到元素名(ElementName)
<TextBox x:Name="input" />
<TextBlock Text="{Binding Text, ElementName=input}" />
4. 绑定到静态资源(StaticResource)
Text="{Binding Name, Source={StaticResource MyViewModel}}"
5. 绑定到静态属性(x:Static)
Text="{Binding Source={x:Static sys:DateTime.Now}}"
6. 绑定到 DataContext(最常用!MVVM 标配)
Text="{Binding UserName}"
DataContext 是传递性的,子控件自动继承父控件数据源。
六、必须实现:数据源变化通知 INotifyPropertyChanged
如果不实现这个接口,数据源变了,UI 不会自动更新!
这是 MVVM 最基础的基类:
using System.ComponentModel;
using System.Runtime.CompilerServices;
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
protected bool SetProperty<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
{
if (EqualityComparer<T>.Default.Equals(field, value)) return false;
field = value;
OnPropertyChanged(propertyName);
return true;
}
}
使用:
public class MainViewModel : ViewModelBase
{
private string _userName;
public string UserName
{
get => _userName;
set => SetProperty(ref _userName, value);
}
}
七、集合绑定:ObservableCollection
普通 List<T> 增删数据 UI 不更新,必须用:
public ObservableCollection<string> Items { get; set; } = new ObservableCollection<string>();
八、值转换器(IValueConverter)
当数据源类型和 UI 属性类型不匹配时使用,例如:
- bool ↔ Visibility
- string ↔ 颜色
- 数字 ↔ 格式化文本
示例:Bool 转 Visibility
public class BoolToVisibilityConverter : IValueConverter
{
public object Convert(object value, ...)
{
return (bool)value ? Visibility.Visible : Visibility.Collapsed;
}
public object ConvertBack(object value, ...)
{
return (Visibility)value == Visibility.Visible;
}
}
使用:
<Button Visibility="{Binding IsShow, Converter={StaticResource BoolToVis}}" />
九、绑定校验(ValidationRules)
输入不符合规则时显示红框并提示:
<TextBox>
<TextBox.Text>
<Binding Path="Age">
<Binding.ValidationRules>
<ExceptionValidationRule/>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
十、字符串格式化(StringFormat)
Text="{Binding Price, StringFormat={}{0:C}}" <!-- 货币格式 -->
Text="{Binding Date, StringFormat=yyyy-MM-dd}"
Text="{Binding Name, StringFormat=欢迎, {0}}"
十一、常用高级特性
1. FallBackValue
绑定失败时显示的默认值
Text="{Binding Age, FallbackValue=未知}"
2. TargetNullValue
数据源为 null 时显示的值
Text="{Binding Description, TargetNullValue=暂无描述}"
3. UpdateSourceTrigger
什么时候把 UI 的值同步回数据源:
-
PropertyChanged:输入一个字符同步一次
-
LostFocus:失去焦点同步
-
Explicit:手动调用 UpdateSource ()
Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}"
十二、MVVM 标准完整示例(可直接复制运行)
1. MainWindow.xaml
<Window x:Class="WpfApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Binding Demo" Height="300" Width="500">
<StackPanel Margin="20">
<TextBox Text="{Binding UserName, Mode=TwoWay}" FontSize="20"/>
<TextBlock Text="{Binding UserName}" FontSize="20" Margin="0 10"/>
<Button Content="测试修改" Click="Button_Click"/>
</StackPanel>
</Window>
2. MainWindow.xaml.cs
public MainWindow()
{
InitializeComponent();
DataContext = new MainViewModel(); // 设置数据源
}
private void Button_Click(object sender, RoutedEventArgs e)
{
var vm = DataContext as MainViewModel;
vm.UserName = "来自后台修改:" + DateTime.Now.Second;
}
3. MainViewModel.cs
public class MainViewModel : ViewModelBase
{
private string _userName = "初始值";
public string UserName
{
get => _userName;
set => SetProperty(ref _userName, value);
}
}
运行后你会发现:
- 输入框修改 → 文本块自动同步
- 点击按钮后台修改 → 输入框和文本块都自动变
这就是 Binding 的魔力!
总结(最重要的 6 条)
- Binding = UI ↔ 数据 自动同步桥梁
- 目标必须是依赖属性
- 源必须实现 INotifyPropertyChanged 才能通知更新
- 集合必须用 ObservableCollection<T>
- 常用模式:TwoWay / OneWay
- 类型不匹配用 IValueConverter 转换器