啼笑皆非的错误记录| WPF 自定义控件 数据混乱

8 篇文章 0 订阅
5 篇文章 0 订阅

1. 问题来源

在开发自定义控件 SubTitles 时,我遇到了一个问题:ItemsControl 中展示的中英文字幕总是显示相同的文本,尽管我绑定的是不同的字幕。

经过排除不相关因素后,我发现问题并不在 SubTitles 控件本身,而是在 ItemsControl 的 ItemTemplate 中使用的另一个自定义控件 SentenceBox。

SentenceBox 用于显示句子文本,我使用两个 SentenceBox 来展示不同的中英文对应句子。然而,无论每个 SentenceBox 的 Text 内容设置为何,所有 SentenceBox 总是显示最后一个 SentenceBox 的 Text。

显然,问题在于这些 SentenceBox 的 Text 数据共享了同一个对象。

2. 错误代码

public static readonly DependencyProperty WordsProperty =
    DependencyProperty.Register("Words", typeof(ObservableCollection<string>),
        typeof(SentenceBox), new PropertyMetadata(new ObservableCollection<string>));

依赖属性概念回顾

  • 定义:在WPF中,依赖属性是一种特殊的属性,其值不仅可以通过常规方式设置,还可以通过数据绑定、样式和模板等方式动态获取。它们允许属性的值依赖于其他输入值。
  • 注册:通过调用DependencyProperty.Register方法来注册一个依赖属性。注册时需要指定属性名、属性类型、拥有者类型以及属性元数据。容易误将注册依赖属性时设置的静态默认值当作每个实例的初始值。实际上,这个静态默认值仅用于属性未显式设置时的后备值,而非每个实例的初始状态。

静态默认值实例默认值混淆

  • 静态默认值:在注册依赖属性时,可以通过PropertyMetadata的构造函数指定一个默认值。然而,这个默认值对所有实例都是共享的,并不适合作为每个实例特有的初始值。如本例所示,如果依赖属性的默认值是一个可变集合(如ObservableCollection<T>),且该默认值在属性注册时静态创建,则所有使用该属性的实例将共享这个集合实例,导致数据混乱。
  • 实例默认值:对于需要在实例创建时动态设置的默认值,应该避免在属性注册时直接指定默认值,而是在某个属性更改处理程序中(如本例中的TextPropertyChanged)检查并设置。

3. 示例代码修正

// WordsProperty 的注册,移除默认值
public static readonly DependencyProperty WordsProperty =
    DependencyProperty.Register("Words", typeof(ObservableCollection<string>),
        typeof(SentenceBox), new PropertyMetadata(null));

// TextPropertyChanged 处理程序
private static void OnTextPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    var box = d as SentenceBox;
    if (box!= null)
    {
        if (box.Words == null)
        {
            box.Words = new ObservableCollection<string>(); // 动态创建实例级默认值
        }
        box.UpdateWords();
    }
}

4. 总结

  • 在注册依赖属性时,对于不可变类型或基本数据类型,可以直接设置静态默认值。
  • 对于可变类型(如集合),应避免在属性注册时设置静态默认值。应在属性更改处理程序中动态检查并设置实例级默认值。
  • 5
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值