绑定(Binding)
在 WPF 中,绑定(Binding) 是最核心的概念之一,它允许你将 UI 控件和数据模型之间建立连接,从而实现界面和数据的自动同步。
一、Binding 基本语法
最常见的绑定写法如下:
<TextBlock Text="{Binding Name}" />
这表示将 TextBlock
的 Text
属性绑定到当前 DataContext
中的 Name
属性。
二、Binding 的关键组成
{Binding Path=Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}
{Binding Path=属性名,
Source=数据源对象,
ElementName=元素名,
RelativeSource=相对源,
Mode=绑定模式,
UpdateSourceTrigger=触发时机,
Converter=值转换器,
ConverterParameter=转换参数}
属性 | 作用 |
---|---|
ElementName | 元素名 |
Path | 属性名 |
Mode | 绑定模式(见下表) |
UpdateSourceTrigger | 指定何时将值写回数据源(主要用于双向绑定) |
Converter | 转换器 |
常用绑定模式(Mode)
模式 | 方向 | 说明 |
---|---|---|
OneWay | 目标 ← 数据源 | 目标(UI)会随着数据源变化自动更新,但反之不会。常用于只读显示。 |
TwoWay | 数据源 ↔ 目标 | 双向绑定,UI 和数据源会互相更新。常用于可编辑控件,如 TextBox 、Slider 。 |
OneWayToSource | 目标 → 数据源 | 和 OneWay 相反。目标控件的值传给数据源,数据源变化不会影响目标。 |
OneTime | 初始化绑定(一次) | 加载时从数据源获取一次值,之后不再跟踪更新。性能好,但不自动刷新。 |
Default | 取决于属性类型 | WPF 会自动选择模式:大多数属性用 OneWay ,如 TextBlock.Text ;用户输入控件用 TwoWay ,如 TextBox.Text 。 |
三、DataContext(绑定的上下文)
方法一:在 XAML 中设置(常用于简单演示或静态数据)
<Window x:Class="WpfApp1.MainWindow"
xmlns:local="clr-namespace:WpfApp1">
<Window.DataContext>
<local:Person Name="张三" />
</Window.DataContext>
<Grid>
<TextBlock Text="{Binding Name}" />
</Grid>
</Window>
方法二:在代码后端设置(推荐 MVVM 模式)
一般在构造函数中设置
DataContext
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new Person { Name = "李四" };
}
}
方法三:给特定控件设置 DataContext(只影响它自己及其子控件)
<StackPanel>
<StackPanel.DataContext>
<local:Person Name="王五" />
</StackPanel.DataContext>
<TextBlock Text="{Binding Name}" />
</StackPanel>
方法四:绑定到另一个元素的属性(使用 ElementName)
<StackPanel>
<Slider x:Name="MySlider" Minimum="0" Maximum="100" Value="50"/>
<TextBlock Text="{Binding Value, ElementName=MySlider}" />
</StackPanel>
方法五:使用 RelativeSource(绑定到父控件、模板、自己等)
<TextBlock Text="{Binding DataContext.Title, RelativeSource={RelativeSource AncestorType=Window}}" />
这表示从“当前所在的窗口”的 DataContext
中绑定 Title
属性。
用法 | 适用场景 |
---|---|
Window.DataContext | 设置整个窗口的绑定上下文 |
this.DataContext = ... | 在代码中更灵活设置绑定对象 |
ElementName | 在控件间绑定值(如 Slider → TextBlock) |
RelativeSource | 模板、父控件、自己等特殊结构绑定 |
局部控件 .DataContext | 局部作用域绑定,覆盖继承来的上下文 |
四、绑定示例(单向绑定)
1. 创建数据源:
数据源可以是任何实现了 INotifyPropertyChanged
接口的对象,以便在属性变化时通知绑定系统。
using System.ComponentModel;
public class Person : INotifyPropertyChanged
{
private string name;
public string Name
{
get { return name; }
set
{
if (name != value)
{
name = value;
//通知上下文变化
OnPropertyChanged(nameof(Name));
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
2. 设置 DataContext
在 XAML 或代码中设置 DataContext
,将数据源与 UI 关联起来。
//方法1. 在 .CS 代码绑定
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new Person { Name = "John Doe" };
}
}
<!--方法2. 在xaml中设置上下文-->
xmlns:vm="clr-namespace:MVVM_模式.ViewModel"<!--先引用命名空间-->
<!--绑定上下文-->
<Window.DataContext>
<vm:MainModelViewModel />
</Window.DataContext>
3. 创建绑定
在 XAML 中使用 Binding
标记扩展来创建绑定。
<Window x:Class="WpfApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="200" Width="300">
<StackPanel Margin="10">
<TextBox Text="{Binding Path=Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Width="200" Margin="0,0,0,10"/>
<TextBlock Text="{Binding Path=Name}" Width="200"/>
</StackPanel>
</Window>
五、绑定示例(双向绑定)
这里以 MVVM 模式示例 :
1. Model 类:
通常不需要 实现 INotifyPropertyChanged
-
标准做法:
-
Model 层:仅是纯数据对象,不包含 UI 状态逻辑,不实现
INotifyPropertyChanged
。 -
ViewModel 层:处理通知逻辑,由 ViewModel 来监听 Model 的变化(如果需要),并将其转换为
INotifyPropertyChanged
通知给 View。 -
优点:
-
Model 更加干净、独立、可复用、可用于非 WPF 场景(如 Web API、数据库等)。
-
ViewModel 保持对 UI 状态的完全控制。
-
-
-
可以实现
INotifyPropertyChanged
的情况:- 你的 Model 是领域模型或服务模型,会被多个地方绑定并直接用于 UI 展示;
- 或者你希望简化代码逻辑,让 Model 自身具备通知能力;
- 代价是:
- 模型耦合了 WPF 通知机制,不再是“纯粹”的数据模型;
- 可能导致测试或重用变得复杂。
using System.ComponentModel;
public class Student
{
private int id;
public int Id { get; set; }
private string name;
public string Name { get; set; }
}
2. 设置 ViewModel
(代码中):
也要实现INotifyPropertyChanged接口
internal class MainModelViewModel:INotifyPropertyChanged
{
//ObservableCollection<T> 是 .NET 中的一个集合类,位于命名空间 System.Collections.ObjectModel,它的特点是:
//当集合中的数据发生变化(增、删、清空)时,它会自动通知界面(View)刷新 UI
public ObservableCollection<Student> Students { get; set; } = new ObservableCollection<Student>()
{
new Student(){ Id = 1, Name = "张三" },
new Student(){ Id = 2, Name = "李四" },
new Student(){ Id = 3, Name = "王五" },
};
}
//实现接口自动生成
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
-
如果你只是修改集合中某个对象的属性(例如改名字),这个对象本身也要实现
INotifyPropertyChanged
,才能让界面更新。 -
ObservableCollection
适用于小量数据,对于大数据推荐使用分页等机制。
3. XAML 中绑定控件(双向):
<!--先引用命名空间-->
xmlns:vm="clr-namespace:MVVM_模式.ViewModel"
<!--绑定上下文-->
<Window.DataContext>
<vm:MainModelViewModel />
</Window.DataContext>
双向绑定的两个关键点:
要素 | 说明 |
---|---|
Mode=TwoWay | 显式启用双向绑定 |
INotifyPropertyChanged | 数据源属性变化时通知 UI 更新 |
补充:UpdateSourceTrigger 说明
触发方式 | 用于 |
---|---|
Default | 一般是失去焦点时更新 |
PropertyChanged | 用户每输入一个字符就更新 ViewModel(推荐) |
LostFocus | 控件失去焦点时更新 |
Explicit | 必须手动调用 BindingExpression.UpdateSource() 更新数据 |
转换器 (Converters)
转换器用于在数据绑定过程中转换数据。
单值转换器 / 多值转换器
一、单值转换器
(IValueConverter
)
特点:
- 一次只处理一个值
- 最常用
- 绑定表达式只绑定一个属性
- 必须实现 IValueConverter接口
接口:
public interface IValueConverter
{
object Convert(object value, Type targetType, object parameter, CultureInfo culture);
object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture);
}
示例:bool → Color
public class BoolToBrushConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return (bool)value ? Brushes.Green : Brushes.Red;
}
public object ConvertBack(...) => throw new NotImplementedException();
}
使用:
<Ellipse Fill="{Binding IsOnline, Converter={StaticResource BoolToBrushConverter}}" />
二、多值转换器
(IMultiValueConverter
)
特点:
- 一次处理多个绑定值
- 必须配合
<MultiBinding>
使用 - 比如多个字段决定一个按钮是否启用
- 必须实现 IMultiValueConverter接口
接口:
public interface IMultiValueConverter
{
object Convert(object[] values, Type targetType, object parameter, CultureInfo culture);
object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture);
}
示例:两个 bool → 控件是否可用
public class BothTrueToEnabledConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
//如果传进来的绑定值少于 2 个(例如没绑定好、或数据为空),就直接返回 false,避免报错。
if (values.Length < 2) return false;
//如果两个值都为 true,才返回 true,否则返回 false。
return (bool)values[0] && (bool)values[1];
}
public object[] ConvertBack(...) => throw new NotImplementedException();
}
使用方式:
<Button Content="提交">
<Button.IsEnabled>
<!--多值绑定-->
<MultiBinding Converter="{StaticResource BothTrueToEnabledConverter}">
<Binding Path="IsNameFilled"/>
<Binding Path="IsEmailFilled"/>
</MultiBinding>
</Button.IsEnabled>
</Button>
怎么用转换器?(完整步骤)
如创建一个单值转换器:
第 1 步:创建一个转换器类
public class BoolToColorConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
bool isOnline = (bool)value;
return isOnline ? Brushes.Green : Brushes.Red;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException(); // 通常只需要 Convert
}
}
各个参数的含义如下:
参数名 | 类型 | 作用说明 |
---|---|---|
value | object | 绑定源传入的值。你要转换的“原始数据”,比如 IsOnline 的值(true/false)。 |
targetType | Type | 目标属性的类型,比如目标控件的 Fill 是 Brush 类型。 |
parameter | object | 绑定时额外传入的参数,常用于传递控制逻辑的字符串或数值。 |
culture | CultureInfo | 区域信息,通常用于格式化(比如数字、日期等),很少改动。 |
第 2 步:在 XAML 中声明资源
xmlns:local="clr-namespace:YourApp.Converters" <!--导入命名空间-->
<Window.Resources>
<local:BoolToColorConverter x:Key="BoolToColor"/>
</Window.Resources>
第 3 步:绑定中使用
<Ellipse Width="20" Height="20"
Fill="{Binding IsOnline, Converter={StaticResource BoolToColor}}"/>