WPF 数据绑定完全指南

目录

第一部分:Binding 绑定参数详解

1. 基础参数

Path(路径)

<!-- 完整写法 -->
<TextBlock Text="{Binding Path=UserName}"/>
<!-- 简写(推荐) -->
<TextBlock Text="{Binding UserName}"/>
<!-- 嵌套属性 -->
<TextBlock Text="{Binding Address.City}"/>

Mode(模式)

控制数据流向:

模式方向说明适用场景
OneWay后台 → UI单向绑定(默认)只读显示
TwoWay后台 ⇄ UI双向绑定可编辑控件(TextBox、CheckBox等)
OneWayToSourceUI → 后台反向单向特殊需求
OneTime只绑定一次后续不更新静态数据
<!-- OneWay: 后台 → UI(只读显示) -->
<TextBlock Text="{Binding UserName, Mode=OneWay}"/>

<!-- TwoWay: 后台 ⇄ UI(可编辑控件) -->
<TextBox Text="{Binding SearchText, Mode=TwoWay}"/>

<!-- OneTime: 只绑定一次 -->
<TextBlock Text="{Binding CreateTime, Mode=OneTime}"/>

UpdateSourceTrigger(更新时机)

控制什么时候同步到后台:

值行为场景
Default控件默认行为(TextBox 是失去焦点时)一般输入框
PropertyChanged每次属性变化立即同步实时搜索、实时验证
LostFocus失去焦点时同步表单提交
Explicit手动调用才同步特殊需求
<!-- 实时搜索:每次输入立即同步 -->
<TextBox Text="{Binding SearchText, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>

<!-- 表单提交:失去焦点时同步 -->
<TextBox Text="{Binding Email, UpdateSourceTrigger=LostFocus}"/>

2. 格式化参数

StringFormat(字符串格式)

格式化显示内容:

<!-- 货币格式 -->
<TextBlock Text="{Binding Price, StringFormat='价格: {0:C}'}"/>
<!-- 输出: 价格: ¥99.99 -->

<!-- 数字格式(保留2位小数) -->
<TextBlock Text="{Binding Value, StringFormat={}{0:N2}}"/>
<!-- 输出: 1,234.56 -->

<!-- 百分比格式 -->
<TextBlock Text="{Binding Progress, StringFormat={}{0:P0}}"/>
<!-- 输出: 75% -->

<!-- 日期格式 -->
<TextBlock Text="{Binding CreateTime, StringFormat='创建时间: {0:yyyy-MM-dd HH:mm:ss}'}"/>
<!-- 输出: 创建时间: 2024-01-15 14:30:25 -->

<!-- 自定义文本组合 -->
<TextBlock Text="{Binding UserName, StringFormat='欢迎, {0}!'}"/>
<!-- 输出: 欢迎, 张三! -->

<!-- 多字段组合 -->
<TextBlock Text="{Binding Price, StringFormat='单价: {0:C} × {1}件 = 总计: {2:C}'}"/>

Converter(转换器)

复杂的数据转换,需要实现 IValueConverter 接口:

// 创建转换器
public class BoolToColorConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (value is bool boolValue)
        {
            return boolValue ? Brushes.Green : Brushes.Red;
        }
        return Brushes.Gray;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}
<!-- 在 App.xaml 注册 -->
<Application.Resources>
    <local:BoolToColorConverter x:Key="BoolToColorConverter"/>
</Application.Resources>

<!-- 使用转换器 -->
<Ellipse Fill="{Binding IsOnline, Converter={StaticResource BoolToColorConverter}}"/>

<!-- 带参数的转换器 -->
<TextBlock Text="{Binding Temperature, Converter={StaticResource TempConverter}, ConverterParameter=Celsius}"/>

3. 空值和错误处理

FallbackValue(失败值)

绑定失败时显示的值(找不到属性、类型转换失败等):

<TextBlock Text="{Binding NotExistProperty, FallbackValue='属性不存在'}"/>
<TextBlock Text="{Binding InvalidData, FallbackValue='数据格式错误'}"/>

TargetNullValue(空值)

源数据为 null 时显示的值:

<TextBlock Text="{Binding Description, TargetNullValue='暂无描述'}"/>
<TextBlock Text="{Binding InstallDate, StringFormat='安装日期: {0:yyyy-MM-dd}', TargetNullValue='未设置'}"/>

组合使用

<!-- 同时处理 null 和错误 -->
<TextBlock Text="{Binding Data, TargetNullValue='无数据', FallbackValue='加载失败'}"/>

4. 绑定源指定

ElementName(元素名称)

绑定到同一个窗口的其他控件:

<!-- 滑块控制进度条 -->
<Slider x:Name="volumeSlider" Minimum="0" Maximum="100" Value="50"/>
<ProgressBar Value="{Binding Value, ElementName=volumeSlider}"/>
<TextBlock Text="{Binding Value, ElementName=volumeSlider, StringFormat='音量: {0:F0}'}"/>

<!-- 输入框实时显示 -->
<TextBox x:Name="inputBox" Text="测试文本"/>
<TextBlock Text="{Binding Text, ElementName=inputBox, StringFormat='你输入的是: {0}'}"/>

RelativeSource(相对源)

绑定到自身或父级:

<!-- 绑定到自身(显示自己的某个属性) -->
<TextBox Text="测试" x:Name="txtTest"/>
<TextBlock Text="{Binding Text, RelativeSource={RelativeSource Self}}"/>

<!-- 绑定到父窗口 -->
<TextBlock Text="{Binding Title, RelativeSource={RelativeSource AncestorType=Window}}"/>

<!-- 绑定到父级 StackPanel -->
<StackPanel Tag="父级数据">
    <TextBlock Text="{Binding Tag, RelativeSource={RelativeSource AncestorType=StackPanel}}"/>
</StackPanel>

<!-- 绑定到父级的父级 -->
<Border>
    <StackPanel>
        <TextBlock Text="{Binding DataContext.Title, RelativeSource={RelativeSource AncestorType=Window}}"/>
    </StackPanel>
</Border>

Source(显式源)

指定具体的数据源对象:

<!-- 绑定到静态资源 -->
<TextBlock Text="{Binding Source={StaticResource AppSettings}, Path=ThemeName}"/>

<!-- 绑定到单例 -->
<TextBlock Text="{Binding Source={x:Static local:ConfigService.Instance}, Path=Version}"/>

5. 数据验证

ValidatesOnDataErrors

启用数据验证,配合 IDataErrorInfo 接口:

<TextBox Text="{Binding Email, ValidatesOnDataErrors=True, UpdateSourceTrigger=PropertyChanged}"/>
public class UserViewModel : ObservableObject, IDataErrorInfo
{
    private string _email = "";
    public string Email
    {
        get => _email;
        set => SetProperty(ref _email, value);
    }

    public string Error => null;

    public string this[string columnName]
    {
        get
        {
            if (columnName == nameof(Email))
            {
                if (string.IsNullOrEmpty(Email))
                    return "邮箱不能为空";
                if (!Email.Contains("@"))
                    return "邮箱格式不正确";
            }
            return null;
        }
    }
}

ValidatesOnExceptions

属性设置抛出异常时验证:

<TextBox Text="{Binding Age, ValidatesOnExceptions=True}"/>
private int _age;
public int Age
{
    get => _age;
    set
    {
        if (value < 0 || value > 150)
            throw new ArgumentException("年龄必须在 0-150 之间");
        SetProperty(ref _age, value);
    }
}

6. 其他参数

IsAsync(异步加载)

异步获取数据,不阻塞 UI:

<TextBlock Text="{Binding LargeData, IsAsync=True}"/>

NotifyOnSourceUpdated / NotifyOnTargetUpdated

触发更新事件:

<TextBox Text="{Binding UserName, NotifyOnTargetUpdated=True}" 
         TargetUpdated="OnTargetUpdated"/>
private void OnTargetUpdated(object sender, DataTransferEventArgs e)
{
    // 目标属性更新后触发
}

StringFormat 高级用法

<!-- 条件格式 -->
<TextBlock Text="{Binding Temperature, StringFormat='{}{0:+0.0;-0.0;0}°C'}"/>
<!-- 正数显示 +25.0°C,负数显示 -10.5°C,零显示 0°C -->

<!-- 千分位分隔 -->
<TextBlock Text="{Binding Amount, StringFormat={}{0:#,##0.00}}"/>
<!-- 输出: 1,234,567.89 -->

<!-- 前导零 -->
<TextBlock Text="{Binding Code, StringFormat={}{0:D5}}"/>
<!-- 输出: 00123 -->

7. 常用组合示例

实时搜索框

<TextBox Text="{Binding SearchText, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>

只读显示

<TextBlock Text="{Binding StatusMessage}"/>

下拉框选中项

<ComboBox ItemsSource="{Binding Items}" 
          SelectedItem="{Binding SelectedItem, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>

滑块控制数值

<Slider Value="{Binding Volume, Mode=TwoWay}" Minimum="0" Maximum="100"/>
<TextBlock Text="{Binding Volume, StringFormat='音量: {0:F0}'}"/>

状态显示(在线/离线)

<Ellipse Fill="{Binding IsOnline, Converter={StaticResource BoolToColorConverter}}"/>
<TextBlock Text="{Binding IsOnline, Converter={StaticResource BoolToStringConverter}}"/>

价格、日期显示

<TextBlock Text="{Binding Price, StringFormat='价格: {0:C}'}"/>
<TextBlock Text="{Binding CreateTime, StringFormat={}{0:yyyy年MM月dd日 HH:mm}}"/>

表单输入(带验证)

<TextBox Text="{Binding Email, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}"/>
<TextBlock Text="{Binding (Validation.Errors)[0].ErrorContent, ElementName=emailBox}" 
           Foreground="Red"/>

第二部分:常用控件绑定实战

1. TextBox(文本输入框)

用途: 用户输入文本

常用属性:

绑定示例:

<!-- 双向绑定,实时读取 -->
<TextBox Text="{Binding UserName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>

<!-- 只读显示 -->
<TextBox Text="{Binding DisplayText}" IsReadOnly="True"/>

<!-- 限制长度 -->
<TextBox Text="{Binding Phone}" MaxLength="11"/>

后台读取:

// 自动同步到 ViewModel 属性
[ObservableProperty]
private string _userName = "";

// 用户输入会自动更新 _userName

2. TextBlock(文本显示)

用途: 只显示文本,不可编辑

常用属性:

绑定示例:

<!-- 简单绑定 -->
<TextBlock Text="{Binding StatusMessage}"/>

<!-- 格式化显示 -->
<TextBlock Text="{Binding Price, StringFormat='价格: {0:C}'}"/>

<!-- 多字段组合 -->
<TextBlock>
    <Run Text="用户:"/>
    <Run Text="{Binding UserName}"/>
    <Run Text=", 年龄:"/>
    <Run Text="{Binding Age}"/>
</TextBlock>

<!-- 带颜色 -->
<TextBlock Text="{Binding Status}" Foreground="{Binding StatusColor}"/>

后台读取:

// TextBlock 通常只用于显示,不需要读取
// 如果要获取显示的值,直接读取绑定的属性
string message = StatusMessage;

3. CheckBox(复选框)

用途: 布尔值选择(是/否)

常用属性:

绑定示例:

<!-- 双向绑定 -->
<CheckBox Content="启用功能" IsChecked="{Binding IsEnabled, Mode=TwoWay}"/>

<!-- 三态复选框(true/false/null) -->
<CheckBox Content="不确定状态" IsThreeState="True" 
          IsChecked="{Binding Status, Mode=TwoWay}"/>

<!-- 多个复选框 -->
<StackPanel>
    <CheckBox Content="选项A" IsChecked="{Binding OptionA}"/>
    <CheckBox Content="选项B" IsChecked="{Binding OptionB}"/>
    <CheckBox Content="选项C" IsChecked="{Binding OptionC}"/>
</StackPanel>

后台读取:

[ObservableProperty] private bool _isEnabled;  // true=选中, false=未选中
[ObservableProperty] private bool? _status;    // true/false/null(三态)
[ObservableProperty] private bool _optionA;
[ObservableProperty] private bool _optionB;
[ObservableProperty] private bool _optionC;

4. RadioButton(单选按钮)

用途: 从多个选项中选择一个

常用属性:

绑定示例:

<!-- 方式1:多个布尔属性 -->
<StackPanel>
    <RadioButton Content="男" GroupName="Gender" 
                 IsChecked="{Binding IsMale, Mode=TwoWay}"/>
    <RadioButton Content="女" GroupName="Gender" 
                 IsChecked="{Binding IsFemale, Mode=TwoWay}"/>
</StackPanel>

<!-- 方式2:枚举绑定(推荐) -->
<StackPanel>
    <RadioButton Content="男" GroupName="Gender" 
                 IsChecked="{Binding Path=SelectedGender, Converter={StaticResource EnumToBoolConverter}, ConverterParameter={x:Static local:Gender.Male}}"/>
    <RadioButton Content="女" GroupName="Gender" 
                 IsChecked="{Binding Path=SelectedGender, Converter={StaticResource EnumToBoolConverter}, ConverterParameter={x:Static local:Gender.Female}}"/>
</StackPanel>

后台读取:

// 方式1
[ObservableProperty] private bool _isMale;
[ObservableProperty] private bool _isFemale;

// 方式2(推荐)
public enum Gender { Male, Female }
[ObservableProperty] private Gender _selectedGender;

5. ComboBox(下拉框)

用途: 从列表中选择一项

常用属性:

绑定示例:

<!-- 简单字符串列表 -->
<ComboBox ItemsSource="{Binding CityList}" 
          SelectedItem="{Binding SelectedCity, Mode=TwoWay}"/>

<!-- 对象列表(显示 Name,值为 Id) -->
<ComboBox ItemsSource="{Binding Users}" 
          DisplayMemberPath="Name"
          SelectedValuePath="Id"
          SelectedValue="{Binding SelectedUserId, Mode=TwoWay}"/>

<!-- 获取整个对象 -->
<ComboBox ItemsSource="{Binding Users}" 
          DisplayMemberPath="Name"
          SelectedItem="{Binding SelectedUser, Mode=TwoWay}"/>

<!-- 自定义显示模板 -->
<ComboBox ItemsSource="{Binding Products}">
    <ComboBox.ItemTemplate>
        <DataTemplate>
            <StackPanel Orientation="Horizontal">
                <TextBlock Text="{Binding Name}"/>
                <TextBlock Text="{Binding Price, StringFormat=' - {0:C}'}" Margin="10,0,0,0"/>
            </StackPanel>
        </DataTemplate>
    </ComboBox.ItemTemplate>
</ComboBox>

后台读取:

// 简单列表
[ObservableProperty] private ObservableCollection<string> _cityList;
[ObservableProperty] private string _selectedCity;

// 对象列表
[ObservableProperty] private ObservableCollection<UserModel> _users;
[ObservableProperty] private int _selectedUserId;      // 选中的 Id
[ObservableProperty] private UserModel _selectedUser;  // 或整个对象

// 初始化数据
public void LoadData()
{
    CityList = new ObservableCollection<string> { "北京", "上海", "广州", "深圳" };
    SelectedCity = "北京";
    
    Users = new ObservableCollection<UserModel>
    {
        new UserModel { Id = 1, Name = "张三" },
        new UserModel { Id = 2, Name = "李四" }
    };
}

6. ListBox(列表框)

用途: 显示列表,可选择一项或多项

常用属性:

绑定示例:

<!-- 单选 -->
<ListBox ItemsSource="{Binding Products}" 
         SelectedItem="{Binding SelectedProduct, Mode=TwoWay}">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <StackPanel Orientation="Horizontal">
                <TextBlock Text="{Binding Name}"/>
                <TextBlock Text="{Binding Price, StringFormat=' - {0:C}'}" Margin="10,0,0,0"/>
            </StackPanel>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

<!-- 多选 -->
<ListBox ItemsSource="{Binding Products}" 
         SelectionMode="Multiple">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <CheckBox Content="{Binding Name}" 
                      IsChecked="{Binding IsSelected, RelativeSource={RelativeSource AncestorType=ListBoxItem}}"/>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

后台读取:

[ObservableProperty] private ObservableCollection<ProductModel> _products;
[ObservableProperty] private ProductModel _selectedProduct;

// 多选需要特殊处理,通常用 IsSelected 属性
public class ProductModel : ObservableObject
{
    public string Name { get; set; }
    public decimal Price { get; set; }
    
    [ObservableProperty]
    private bool _isSelected;
}

// 获取所有选中项
var selectedProducts = Products.Where(p => p.IsSelected).ToList();

7. Slider(滑块)

用途: 数值范围选择

常用属性:

绑定示例:

<!-- 基础滑块 -->
<Slider Value="{Binding Volume, Mode=TwoWay}" 
        Minimum="0" Maximum="100"/>

<!-- 带刻度和吸附 -->
<Slider Value="{Binding Temperature, Mode=TwoWay}" 
        Minimum="0" Maximum="100" 
        TickFrequency="10"
        IsSnapToTickEnabled="True"/>

<!-- 实时显示值 -->
<StackPanel>
    <Slider Value="{Binding Brightness, Mode=TwoWay}" Minimum="0" Maximum="100"/>
    <TextBlock Text="{Binding Brightness, StringFormat='亮度: {0:F0}%'}"/>
</StackPanel>

后台读取:

[ObservableProperty] private double _volume;        // 0-100
[ObservableProperty] private double _temperature;   // 带刻度
[ObservableProperty] private double _brightness;    // 实时显示

8. ProgressBar(进度条)

用途: 显示进度

常用属性:

绑定示例:

<!-- 确定进度 -->
<ProgressBar Value="{Binding Progress}" 
             Minimum="0" Maximum="100" 
             Height="20"/>

<!-- 不确定进度(加载动画) -->
<ProgressBar IsIndeterminate="{Binding IsLoading}" 
             Height="20"/>

<!-- 带文本显示 -->
<Grid>
    <ProgressBar Value="{Binding DownloadProgress}" Maximum="100"/>
    <TextBlock Text="{Binding DownloadProgress, StringFormat={}{0:F0}%}" 
               HorizontalAlignment="Center" 
               VerticalAlignment="Center"/>
</Grid>

后台读取:

[ObservableProperty] private double _progress;       // 0-100
[ObservableProperty] private bool _isLoading;
[ObservableProperty] private double _downloadProgress;

// 模拟进度更新
private async Task UpdateProgress()
{
    for (int i = 0; i <= 100; i++)
    {
        Progress = i;
        await Task.Delay(50);
    }
}

9. DatePicker(日期选择器)

用途: 选择日期

常用属性:

绑定示例:

<!-- 基础日期选择 -->
<DatePicker SelectedDate="{Binding BirthDate, Mode=TwoWay}"/>

<!-- 限制日期范围 -->
<DatePicker SelectedDate="{Binding AppointmentDate, Mode=TwoWay}"
            DisplayDateStart="{Binding MinDate}"
            DisplayDateEnd="{Binding MaxDate}"/>

<!-- 格式化显示 -->
<StackPanel>
    <DatePicker SelectedDate="{Binding InstallDate, Mode=TwoWay}"/>
    <TextBlock Text="{Binding InstallDate, StringFormat='安装日期: {0:yyyy-MM-dd}', TargetNullValue='未设置'}"/>
</StackPanel>

后台读取:

[ObservableProperty] private DateTime? _birthDate;       // 可为 null
[ObservableProperty] private DateTime? _appointmentDate;
[ObservableProperty] private DateTime _minDate = DateTime.Today;
[ObservableProperty] private DateTime _maxDate = DateTime.Today.AddYears(1);
[ObservableProperty] private DateTime? _installDate;

10. Image(图片)

用途: 显示图片

常用属性:

绑定示例:

<!-- 从资源加载 -->
<Image Source="/Assets/icon/device.png" Width="64" Height="64"/>

<!-- 从路径绑定 -->
<Image Source="{Binding ImagePath}" Width="100"/>

<!-- 从 BitmapImage 绑定 -->
<Image Source="{Binding DeviceImage}" Width="100"/>

<!-- 带拉伸方式 -->
<Image Source="{Binding PhotoPath}" 
       Stretch="Uniform" 
       Width="200" Height="200"/>

<!-- 圆形图片(需要 Border 裁剪) -->
<Border Width="100" Height="100" CornerRadius="50" ClipToBounds="True">
    <Image Source="{Binding AvatarPath}" Stretch="UniformToFill"/>
</Border>

后台读取:

// 方式1:字符串路径
[ObservableProperty] private string _imagePath = "/Assets/icon/device.png";

// 方式2:BitmapImage 对象
[ObservableProperty] private BitmapImage _deviceImage;

// 加载图片
public void LoadImage()
{
    DeviceImage = new BitmapImage(new Uri("pack://application:,,,/Assets/device.png"));
}

11. 综合示例:设备信息编辑表单

XAML 界面

<StackPanel Margin="20">
    <TextBlock Text="设备信息" FontSize="20" FontWeight="Bold" Margin="0,0,0,20"/>
    
    <!-- 设备名称 -->
    <StackPanel Orientation="Horizontal" Margin="0,5">
        <TextBlock Text="名称:" Width="80" VerticalAlignment="Center"/>
        <TextBox Text="{Binding DeviceName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" 
                 Width="200" Padding="5"/>
    </StackPanel>
    
    <!-- 设备类型 -->
    <StackPanel Orientation="Horizontal" Margin="0,5">
        <TextBlock Text="类型:" Width="80" VerticalAlignment="Center"/>
        <ComboBox ItemsSource="{Binding DeviceTypes}" 
                  SelectedItem="{Binding SelectedType, Mode=TwoWay}"
                  Width="200" Padding="5"/>
    </StackPanel>
    
    <!-- 是否启用 -->
    <StackPanel Orientation="Horizontal" Margin="0,5">
        <TextBlock Text="状态:" Width="80" VerticalAlignment="Center"/>
        <CheckBox IsChecked="{Binding IsEnabled, Mode=TwoWay}"/>
    </StackPanel>
    
    <!-- 温度阈值 -->
    <StackPanel Orientation="Horizontal" Margin="0,5">
        <TextBlock Text="阈值:" Width="80" VerticalAlignment="Center"/>
        <Slider Value="{Binding Threshold, Mode=TwoWay}" 
                Minimum="0" Maximum="100" Width="150"/>
        <TextBlock Text="{Binding Threshold, StringFormat={}{0:F0}°C}" 
                   Margin="10,0,0,0" VerticalAlignment="Center"/>
    </StackPanel>
    
    <!-- 安装日期 -->
    <StackPanel Orientation="Horizontal" Margin="0,5">
        <TextBlock Text="日期:" Width="80" VerticalAlignment="Center"/>
        <DatePicker SelectedDate="{Binding InstallDate, Mode=TwoWay}" 
                    Width="200"/>
    </StackPanel>
    
    <!-- 预览 -->
    <Border Background="#F0F0F0" Padding="15" Margin="0,20,0,0" CornerRadius="5">
        <StackPanel>
            <TextBlock Text="设备预览" FontWeight="Bold" Margin="0,0,0,10"/>
            <TextBlock Text="{Binding DeviceName, StringFormat='名称: {0}'}"/>
            <TextBlock Text="{Binding SelectedType, StringFormat='类型: {0}'}"/>
            <TextBlock Text="{Binding IsEnabled, Converter={StaticResource BoolToStringConverter}, StringFormat='状态: {0}'}"/>
            <TextBlock Text="{Binding Threshold, StringFormat='阈值: {0:F0}°C'}"/>
            <TextBlock Text="{Binding InstallDate, StringFormat='安装: {0:yyyy-MM-dd}', TargetNullValue='未设置'}"/>
        </StackPanel>
    </Border>
    
    <!-- 保存按钮 -->
    <Button Content="保存" Command="{Binding SaveCommand}" 
            Margin="0,20,0,0" Padding="20,10" 
            Background="#3498DB" Foreground="White"/>
</StackPanel>

ViewModel

public partial class DeviceViewModel : ObservableObject
{
    [ObservableProperty] 
    private string _deviceName = "";
    
    [ObservableProperty] 
    private string _selectedType = "";
    
    [ObservableProperty] 
    private bool _isEnabled = true;
    
    [ObservableProperty] 
    private double _threshold = 50;
    
    [ObservableProperty] 
    private DateTime? _installDate;
    
    public ObservableCollection<string> DeviceTypes { get; } = new()
    {
        "温度传感器", "湿度传感器", "光照传感器", "压力传感器"
    };
    
    [RelayCommand]
    private void Save()
    {
        // 这里可以直接读取所有绑定的属性
        Console.WriteLine($"保存设备: {DeviceName}, 类型: {SelectedType}");
        Console.WriteLine($"状态: {(IsEnabled ? "启用" : "禁用")}, 阈值: {Threshold}°C");
        Console.WriteLine($"安装日期: {InstallDate?.ToString("yyyy-MM-dd") ?? "未设置"}");
        
        // 实际项目中这里会调用数据库保存
        // await _deviceService.SaveAsync(new DeviceModel { ... });
    }
}

核心要点总结

赋值(后台 → UI)

读取(UI → 后台)

常用绑定模式

控件类型推荐 ModeUpdateSourceTrigger
TextBox(输入)TwoWayPropertyChanged
TextBox(显示)OneWayDefault
CheckBoxTwoWayPropertyChanged
ComboBoxTwoWayPropertyChanged
SliderTwoWayDefault
DatePickerTwoWayPropertyChanged
TextBlockOneWay-
ProgressBarOneWay-
ImageOneWay-

实时更新

最佳实践

  1. 输入控件用 TwoWay + PropertyChanged
  2. 显示控件用 OneWay(默认)
  3. 格式化用 StringFormat
  4. 复杂转换用 Converter
  5. 空值处理用 TargetNullValue 和 FallbackValue
  6. 绑定其他控件用 ElementName
  7. 绑定父级用 RelativeSource

掌握这些控件的绑定方式,你就能开发大部分 WPF 界面了!