vue-java-tutorials/CSharp/WPFTutorial/WPF-5-CustomTextboxContro/View/UserControllers/ReadMe.md

75 lines
2.2 KiB
Markdown
Raw Permalink Normal View History

2025-06-22 20:21:42 +08:00
在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完全使用数据绑定
```csharp
// 修改后的代码
public string Placeholder
{
get => _placeholder;
set
{
_placeholder = value;
OnPropertyChanged(nameof(Placeholder)); // 实现INotifyPropertyChanged
}
}
```
XAML中
```xml
<TextBlock Name="TbPlaceholder" Text="{Binding Placeholder, RelativeSource={RelativeSource AncestorType=UserControl}}"/>
```
#### 方案2使用DependencyProperty(更符合WPF控件开发规范)
```csharp
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检查
```csharp
set
{
_placeholder = value;
if(TbPlaceholder != null) // 防止设计时或初始化前调用
{
TbPlaceholder.Text = value;
}
}
```
### 为什么当前代码还能工作?
1. 虽然XAML中有`Text="{Binding Placeholder}"`,但代码中直接设置`Text`属性会覆盖绑定
2. 在简单场景下可能看不出问题,但当:
- 需要双向绑定时
- 需要样式/模板重用时
- 需要设计时支持时
就会出现问题