You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

WPF自定义TextBox重写TextProperty元数据未按预期触发变更事件

自定义TextBox控件TextProperty变更事件不触发的解决方法

我创建了一个继承自TextBox的自定义控件AppTextBox,重写了TextProperty的元数据,期望文本变更时触发OnTextPropertyChanged方法。但运行后发现,该事件仅在控件初始化和窗口关闭时各触发一次,输入字符时并未触发。如何实现每一次按键操作都触发该事件?

现有代码

AppTextBox.cs

public class AppTextBox : TextBox
{
    static AppTextBox()
    {
        TextProperty.OverrideMetadata(typeof(AppTextBox),
                                      new FrameworkPropertyMetadata(
                                          string.Empty,
                                          FrameworkPropertyMetadataOptions.BindsTwoWayByDefault | FrameworkPropertyMetadataOptions.Journal,
                                          OnTextPropertyChanged,
                                          null,
                                          true,
                                          UpdateSourceTrigger.PropertyChanged)
                                      );

        DefaultStyleKeyProperty.OverrideMetadata(typeof(AppTextBox), new FrameworkPropertyMetadata(typeof(AppTextBox)));
    }

    private static void OnTextPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
       // TODO: Check if Text is null or empty...
    }
}

使用示例

<ctrl:AppTextBox Text="{Binding NewWord.WrittenForm}" />

控件初始加载时内容为"Foo",但添加或删除字符时,预期触发的OnTextPropertyChanged事件并未执行。


问题原因

WPF的TextBox内部对Text依赖属性的更新做了延迟处理:输入文本时,控件会先维护内部的编辑缓冲区,仅在失去焦点、按下回车等特定时机才会将缓冲区内容同步到Text依赖属性。因此即使你在元数据中设置了UpdateSourceTrigger.PropertyChanged,也无法让PropertyChanged回调在每次按键时触发。

解决方法

方法1:重写OnTextChanged虚方法(推荐)

TextBox提供了受保护的OnTextChanged虚方法,每次文本内容变更(按键、粘贴、删除等)都会触发,重写该方法即可捕获每次文本变更:

public class AppTextBox : TextBox
{
    static AppTextBox()
    {
        TextProperty.OverrideMetadata(typeof(AppTextBox),
                                      new FrameworkPropertyMetadata(
                                          string.Empty,
                                          FrameworkPropertyMetadataOptions.BindsTwoWayByDefault | FrameworkPropertyMetadataOptions.Journal,
                                          OnTextPropertyChanged,
                                          null,
                                          true,
                                          UpdateSourceTrigger.PropertyChanged)
                                      );

        DefaultStyleKeyProperty.OverrideMetadata(typeof(AppTextBox), new FrameworkPropertyMetadata(typeof(AppTextBox)));
    }

    private static void OnTextPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
       // TODO: Check if Text is null or empty...
    }

    protected override void OnTextChanged(TextChangedEventArgs e)
    {
        base.OnTextChanged(e);
        
        // 获取文本变更前后的值
        string oldText = e.OldText?.ToString() ?? string.Empty;
        string newText = Text;
        
        // 手动触发PropertyChanged回调
        OnTextPropertyChanged(this, new DependencyPropertyChangedEventArgs(TextProperty, oldText, newText));
    }
}

方法2:监听TextChanged事件

在控件构造函数中绑定TextChanged事件,在事件处理逻辑中执行你的业务代码:

public class AppTextBox : TextBox
{
    static AppTextBox()
    {
        TextProperty.OverrideMetadata(typeof(AppTextBox),
                                      new FrameworkPropertyMetadata(
                                          string.Empty,
                                          FrameworkPropertyMetadataOptions.BindsTwoWayByDefault | FrameworkPropertyMetadataOptions.Journal,
                                          OnTextPropertyChanged,
                                          null,
                                          true,
                                          UpdateSourceTrigger.PropertyChanged)
                                      );

        DefaultStyleKeyProperty.OverrideMetadata(typeof(AppTextBox), new FrameworkPropertyMetadata(typeof(AppTextBox)));
    }

    public AppTextBox()
    {
        TextChanged += AppTextBox_TextChanged;
    }

    private static void OnTextPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
       // TODO: Check if Text is null or empty...
    }

    private void AppTextBox_TextChanged(object sender, TextChangedEventArgs e)
    {
        string oldText = e.OldText?.ToString() ?? string.Empty;
        string newText = Text;
        OnTextPropertyChanged(this, new DependencyPropertyChangedEventArgs(TextProperty, oldText, newText));
    }
}

内容的提问来源于stack exchange,提问作者Richard Gale

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 08:22:29