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

如何传递UpdateSourceTrigger值给UserControl或运行时更新该属性?

实现UserControl的UpdateSourceTrigger属性继承及可选方案

要解决你这个问题,核心是把外部设置的UpdateSourceTrigger传递到UserControl内部TextBox的Text绑定上——因为外部View里的绑定是ViewModel ↔ UserControl.Text,而内部是TextBox.Text ↔ UserControl.Text,这是两个独立的绑定链条,外部设置的Trigger只会影响前者,所以得专门做属性传递。

最佳方案:给UserControl添加可绑定的UpdateSourceTrigger属性

这是最符合WPF设计规范、封装性最好的方案,步骤如下:

1. 在UserControl后台添加DependencyProperty

给LabelWithTextBox新增一个类型为UpdateSourceTrigger的依赖属性,用来接收外部传入的触发方式,同时添加属性变更回调来更新内部TextBox的绑定:

public static readonly DependencyProperty TextUpdateSourceTriggerProperty =
    DependencyProperty.Register(
        nameof(TextUpdateSourceTrigger),
        typeof(UpdateSourceTrigger),
        typeof(LabelWithTextBox),
        new PropertyMetadata(UpdateSourceTrigger.LostFocus, OnTextUpdateSourceTriggerChanged));

public UpdateSourceTrigger TextUpdateSourceTrigger
{
    get => (UpdateSourceTrigger)GetValue(TextUpdateSourceTriggerProperty);
    set => SetValue(TextUpdateSourceTriggerProperty, value);
}

private static void OnTextUpdateSourceTriggerChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    var control = (LabelWithTextBox)d;
    // 获取内部TextBox的Text绑定表达式
    var bindingExpression = control.TextBox.GetBindingExpression(TextBox.TextProperty);
    if (bindingExpression != null && bindingExpression.ParentBinding != null)
    {
        // 创建新的Binding(因为Binding是不可变对象,不能直接修改原属性)
        var newBinding = new Binding
        {
            Path = bindingExpression.ParentBinding.Path,
            Mode = bindingExpression.ParentBinding.Mode,
            ElementName = bindingExpression.ParentBinding.ElementName,
            UpdateSourceTrigger = (UpdateSourceTrigger)e.NewValue
        };
        // 重新绑定TextBox的Text属性
        control.TextBox.SetBinding(TextBox.TextProperty, newBinding);
    }
}

2. 外部View中使用时传入Trigger

现在你可以在外部直接指定TextUpdateSourceTrigger,它会自动传递到内部TextBox的绑定上:

<controls:LabelWithTextBox 
    Grid.Row="1" 
    Margin="0,5,0,0" 
    Label="Excel Blattname:" 
    SharedSizeGroup="LabelsX"
    Text="{Binding SheetName, Mode=TwoWay}"
    TextUpdateSourceTrigger="PropertyChanged" />

这个方案保持了UserControl的封装性,默认还是LostFocus(符合你的性能需求),需要实时更新时再显式设置PropertyChanged。

可选方案

方案1:添加布尔开关属性(简化版)

如果只需要两种状态(默认延迟更新/实时更新),可以用一个布尔属性代替UpdateSourceTrigger枚举,代码更简洁:

public static readonly DependencyProperty IsLiveTextUpdateProperty =
    DependencyProperty.Register(
        nameof(IsLiveTextUpdate),
        typeof(bool),
        typeof(LabelWithTextBox),
        new PropertyMetadata(false, (d, e) =>
        {
            var control = (LabelWithTextBox)d;
            var trigger = (bool)e.NewValue ? UpdateSourceTrigger.PropertyChanged : UpdateSourceTrigger.LostFocus;
            // 同上面的绑定更新逻辑...
            var bindingExpression = control.TextBox.GetBindingExpression(TextBox.TextProperty);
            if (bindingExpression != null && bindingExpression.ParentBinding != null)
            {
                var newBinding = new Binding
                {
                    Path = bindingExpression.ParentBinding.Path,
                    Mode = bindingExpression.ParentBinding.Mode,
                    ElementName = bindingExpression.ParentBinding.ElementName,
                    UpdateSourceTrigger = trigger
                };
                control.TextBox.SetBinding(TextBox.TextProperty, newBinding);
            }
        }));

public bool IsLiveTextUpdate
{
    get => (bool)GetValue(IsLiveTextUpdateProperty);
    set => SetValue(IsLiveTextUpdateProperty, value);
}

外部使用时只需设置IsLiveTextUpdate="True"即可。

方案2:通过附加属性直接修改内部绑定(不推荐)

如果不想修改UserControl的代码,可以用附加属性来穿透修改内部TextBox的绑定,但这会破坏封装性,只适合临时场景:

public static class TextBoxTriggerHelper
{
    public static readonly DependencyProperty UpdateSourceTriggerProperty =
        DependencyProperty.RegisterAttached(
            "UpdateSourceTrigger",
            typeof(UpdateSourceTrigger),
            typeof(TextBoxTriggerHelper),
            new PropertyMetadata(UpdateSourceTrigger.LostFocus, (d, e) =>
            {
                var textBox = d as TextBox;
                if (textBox != null)
                {
                    var bindingExpr = textBox.GetBindingExpression(TextBox.TextProperty);
                    if (bindingExpr != null && bindingExpr.ParentBinding != null)
                    {
                        var newBinding = new Binding
                        {
                            Path = bindingExpr.ParentBinding.Path,
                            Mode = bindingExpr.ParentBinding.Mode,
                            ElementName = bindingExpr.ParentBinding.ElementName,
                            UpdateSourceTrigger = (UpdateSourceTrigger)e.NewValue
                        };
                        textBox.SetBinding(TextBox.TextProperty, newBinding);
                    }
                }
            }));

    public static void SetUpdateSourceTrigger(DependencyObject obj, UpdateSourceTrigger value)
    {
        obj.SetValue(UpdateSourceTriggerProperty, value);
    }

    public static UpdateSourceTrigger GetUpdateSourceTrigger(DependencyObject obj)
    {
        return (UpdateSourceTrigger)obj.GetValue(UpdateSourceTriggerProperty);
    }
}

外部使用时需要先把内部TextBox设为可访问(比如给UserControl的TextBox添加x:FieldModifier="public"),再通过ElementName指定:

<controls:LabelWithTextBox x:Name="myControl" ...>
    <telerik:RadWatermarkTextBox x:Name="innerTextBox" x:FieldModifier="public" />
</controls:LabelWithTextBox>

然后在代码后台设置附加属性:

TextBoxTriggerHelper.SetUpdateSourceTrigger(myControl.innerTextBox, UpdateSourceTrigger.PropertyChanged);

运行时修改UpdateSourceTrigger的方法

基于最佳方案的实现,你可以在运行时直接修改UserControl的TextUpdateSourceTrigger属性,回调函数会自动更新内部TextBox的绑定:

// 切换为实时更新
myLabelWithTextBox.TextUpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;

// 切回默认的失去焦点更新
myLabelWithTextBox.TextUpdateSourceTrigger = UpdateSourceTrigger.LostFocus;

如果用的是布尔开关方案,直接修改IsLiveTextUpdate属性即可。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.12 04:38:00