vue-java-tutorials/CSharp/WPFTutorial/WPF-5-CustomTextboxContro/View/UserControllers
bunny a060613e96 添加CSharp 2025-06-22 20:21:42 +08:00
..
ClearableTextBox.xaml 添加CSharp 2025-06-22 20:21:42 +08:00
ClearableTextBox.xaml.cs 添加CSharp 2025-06-22 20:21:42 +08:00
ReadMe.md 添加CSharp 2025-06-22 20:21:42 +08:00

ReadMe.md

在WPF中不推荐直接在属性的set方法中操作UI元素TbPlaceholder.Text = value),原因如下:

主要问题分析

  1. 违反MVVM模式原则

    • 直接在属性setter中操作UI控件将业务逻辑与UI耦合在一起
    • 理想情况下属性变更应该通过数据绑定自动反映到UI而不是手动设置
  2. 可能导致设计时异常

    • 如果XAML设计器在初始化时尝试设置Placeholder属性而此时控件还未完全加载TbPlaceholder可能为null导致异常
  3. 破坏数据绑定

    • 代码中直接设置TbPlaceholder.Text会覆盖XAML中设置的Text="{Binding Placeholder}"
  4. 维护性差

    • 如果未来需要修改占位符的显示方式需要修改属性setter代码而不是XAML

推荐改进方案

方案1完全使用数据绑定

// 修改后的代码
public string Placeholder
{
    get => _placeholder;
    set
    {
        _placeholder = value;
        OnPropertyChanged(nameof(Placeholder)); // 实现INotifyPropertyChanged
    }
}

XAML中

<TextBlock Name="TbPlaceholder" Text="{Binding Placeholder, RelativeSource={RelativeSource AncestorType=UserControl}}"/>

方案2使用DependencyProperty(更符合WPF控件开发规范)

public static readonly DependencyProperty PlaceholderProperty =
    DependencyProperty.Register("Placeholder", typeof(string), typeof(ClearableTextBox), 
        new PropertyMetadata(string.Empty));

public string Placeholder
{
    get => (string)GetValue(PlaceholderProperty);
    set => SetValue(PlaceholderProperty, value);
}

方案3如果必须保持当前结构至少添加null检查

set
{
    _placeholder = value;
    if(TbPlaceholder != null) // 防止设计时或初始化前调用
    {
        TbPlaceholder.Text = value;
    }
}

为什么当前代码还能工作?

  1. 虽然XAML中有Text="{Binding Placeholder}",但代码中直接设置Text属性会覆盖绑定
  2. 在简单场景下可能看不出问题,但当:
    • 需要双向绑定时
    • 需要样式/模板重用时
    • 需要设计时支持时 就会出现问题