75 lines
2.2 KiB
Markdown
75 lines
2.2 KiB
Markdown
|
在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. 在简单场景下可能看不出问题,但当:
|
|||
|
- 需要双向绑定时
|
|||
|
- 需要样式/模板重用时
|
|||
|
- 需要设计时支持时
|
|||
|
就会出现问题
|