117

默认的数据绑定TextBox是,它仅在失去焦点TwoWay时才将文本提交给属性。TextBox

Enter当我按下 ?上的键时,是否有任何简单的 XAML 方法可以使数据绑定发生TextBox。我知道在后面的代码中很容易做到,但想象一下,如果这TextBox是在一些复杂DataTemplate的 .

4

12 回答 12

148

您可以通过创建附加行为使自己成为纯 XAML 方法。

像这样的东西:

public static class InputBindingsManager
{

    public static readonly DependencyProperty UpdatePropertySourceWhenEnterPressedProperty = DependencyProperty.RegisterAttached(
            "UpdatePropertySourceWhenEnterPressed", typeof(DependencyProperty), typeof(InputBindingsManager), new PropertyMetadata(null, OnUpdatePropertySourceWhenEnterPressedPropertyChanged));

    static InputBindingsManager()
    {

    }

    public static void SetUpdatePropertySourceWhenEnterPressed(DependencyObject dp, DependencyProperty value)
    {
        dp.SetValue(UpdatePropertySourceWhenEnterPressedProperty, value);
    }

    public static DependencyProperty GetUpdatePropertySourceWhenEnterPressed(DependencyObject dp)
    {
        return (DependencyProperty)dp.GetValue(UpdatePropertySourceWhenEnterPressedProperty);
    }

    private static void OnUpdatePropertySourceWhenEnterPressedPropertyChanged(DependencyObject dp, DependencyPropertyChangedEventArgs e)
    {
        UIElement element = dp as UIElement;

        if (element == null)
        {
            return;
        }

        if (e.OldValue != null)
        {
            element.PreviewKeyDown -= HandlePreviewKeyDown;
        }

        if (e.NewValue != null)
        {
            element.PreviewKeyDown += new KeyEventHandler(HandlePreviewKeyDown);
        }
    }

    static void HandlePreviewKeyDown(object sender, KeyEventArgs e)
    {
        if (e.Key == Key.Enter)
        {
            DoUpdateSource(e.Source);
        }
    }

    static void DoUpdateSource(object source)
    {
        DependencyProperty property =
            GetUpdatePropertySourceWhenEnterPressed(source as DependencyObject);

        if (property == null)
        {
            return;
        }

        UIElement elt = source as UIElement;

        if (elt == null)
        {
            return;
        }

        BindingExpression binding = BindingOperations.GetBindingExpression(elt, property);

        if (binding != null)
        {
            binding.UpdateSource();
        }
    }
}

然后在您的 XAML 中将InputBindingsManager.UpdatePropertySourceWhenEnterPressedProperty属性设置为您想要在Enter按下键时更新的属性。像这样

<TextBox Name="itemNameTextBox"
         Text="{Binding Path=ItemName, UpdateSourceTrigger=PropertyChanged}"
         b:InputBindingsManager.UpdatePropertySourceWhenEnterPressed="TextBox.Text"/>

(您只需要确保在 XAML 文件的根元素中包含“b”的 xmlns clr-namespace 引用,指向您将 InputBindingsManager 放入的任何命名空间)。

于 2009-02-19T10:26:15.300 回答
62

这就是我解决这个问题的方法。我创建了一个特殊的事件处理程序,它进入了后面的代码:

private void TextBox_KeyEnterUpdate(object sender, KeyEventArgs e)
{
    if (e.Key == Key.Enter)
    {
        TextBox tBox = (TextBox)sender;
        DependencyProperty prop = TextBox.TextProperty;

        BindingExpression binding = BindingOperations.GetBindingExpression(tBox, prop);
        if (binding != null) { binding.UpdateSource(); }
    }
}

然后我只是将其添加为 XAML 中的 KeyUp 事件处理程序:

<TextBox Text="{Binding TextValue1}" KeyUp="TextBox_KeyEnterUpdate" />
<TextBox Text="{Binding TextValue2}" KeyUp="TextBox_KeyEnterUpdate" />

事件处理程序使用它的sender引用来更新它自己的绑定。由于事件处理程序是自包含的,因此它应该在复杂的 DataTemplate 中工作。现在可以将这一事件处理程序添加到需要此功能的所有文本框中。

于 2012-11-08T12:26:37.530 回答
47

我不相信有任何“纯 XAML”方式来做你所描述的事情。您可以通过设置UpdateSourceTrigger属性设置绑定,以便在 TextBox 中的文本更改时(而不是在 TextBox 失去焦点时)进行更新,如下所示:

<TextBox Name="itemNameTextBox"
    Text="{Binding Path=ItemName, UpdateSourceTrigger=PropertyChanged}" />

如果您将 UpdateSourceTrigger 设置为“Explicit”,然后处理 TextBox 的 PreviewKeyDown 事件(查找 Enter 键),那么您可以实现您想要的,但它需要代码隐藏。也许某种附加属性(类似于我的EnterKeyTraversal属性)会为您工作。

于 2009-02-18T22:55:20.670 回答
25

您可以轻松地创建自己的继承自 TextBox 的控件,并在整个项目中重用它。

与此类似的东西应该可以工作:

public class SubmitTextBox : TextBox
{
    public SubmitTextBox()
        : base()
    {
        PreviewKeyDown += new KeyEventHandler(SubmitTextBox_PreviewKeyDown);
    }

    void SubmitTextBox_PreviewKeyDown(object sender, KeyEventArgs e)
    {
        if (e.Key == Key.Enter)
        {
            BindingExpression be = GetBindingExpression(TextBox.TextProperty);
            if (be != null)
            {
                be.UpdateSource();
            }
        }
    }
}

可能有一种方法可以绕过这一步,但否则你应该像这样绑定(使用显式):

<custom:SubmitTextBox
    Text="{Binding Path=BoundProperty, UpdateSourceTrigger=Explicit}" />
于 2009-06-10T19:58:08.950 回答
16

如果你将 Ben 和 ausadmin 的解决方案结合起来,你最终会得到一个非常 MVVM 友好的解决方案:

<TextBox Text="{Binding Txt1, Mode=TwoWay, UpdateSourceTrigger=Explicit}">
    <TextBox.InputBindings>
        <KeyBinding Gesture="Enter" 
                    Command="{Binding UpdateTextBoxBindingOnEnterCommand}"
                    CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor,AncestorType={x:Type TextBox}}}" />
    </TextBox.InputBindings>
</TextBox>

...这意味着您将TextBox自身作为参数传递给Command.

这导致您Command看起来像这样(如果您DelegateCommand在 VM 中使用 -style 实现):

    public bool CanExecuteUpdateTextBoxBindingOnEnterCommand(object parameter)
    {
        return true;
    }

    public void ExecuteUpdateTextBoxBindingOnEnterCommand(object parameter)
    {
        TextBox tBox = parameter as TextBox;
        if (tBox != null)
        {
            DependencyProperty prop = TextBox.TextProperty;
            BindingExpression binding = BindingOperations.GetBindingExpression(tBox, prop);
            if (binding != null) 
                binding.UpdateSource();
        }
    }

尽管您可能希望将其放在它自己的类中,但此Command实现可用于代码隐藏中的任何代码,并且最好不要使用任何代码,因此在您的 VMTextBox中没有依赖关系。System.Windows.Controls这取决于您的代码指南有多严格。

于 2014-11-06T10:57:51.733 回答
4

这是一种对我来说似乎非常简单的方法,并且比添加 AttachedBehaviour 更容易(这也是一个有效的解决方案)。我们使用默认的UpdateSourceTrigger(TextBox为LostFocus),然后在Enter Key上添加一个InputBinding,绑定到一个命令。

xml如下

       <TextBox Grid.Row="0" Text="{Binding Txt1}" Height="30" Width="150">
        <TextBox.InputBindings>
            <KeyBinding Gesture="Enter" 
                        Command="{Binding UpdateText1Command}"
                        CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor,AncestorType={x:Type TextBox}},Path=Text}" />
        </TextBox.InputBindings>
    </TextBox>

然后命令方法是

Private Function CanExecuteUpdateText1(ByVal param As Object) As Boolean
    Return True
End Function
Private Sub ExecuteUpdateText1(ByVal param As Object)

    If TypeOf param Is String Then
        Txt1 = CType(param, String)
    End If
End Sub

并且 TextBox 绑定到属性

 Public Property Txt1 As String
    Get
        Return _txt1
    End Get
    Set(value As String)
        _txt1 = value
        OnPropertyChanged("Txt1")
    End Set
End Property

到目前为止,这似乎运作良好,并在 TextBox 中捕获了 Enter Key 事件。

于 2012-11-15T04:09:57.803 回答
4

这不是对原始问题的答案,而是@Samuel Jack接受的答案的扩展。我在自己的应用程序中执行了以下操作,并且对 Samuel 解决方案的优雅感到敬畏。它非常干净,而且非常可重复使用,因为它可以用于任何控件,而不仅仅是TextBox. 我认为这应该与社区分享。

如果您有一个包含一千个TextBoxes都需要在 Enter 上更新绑定源的窗口,您可以通过将下面的 XAML 包含到您的Window Resources而不是将其附加到每个 TextBox 来将此行为附加到所有这些窗口。首先,您当然必须按照Samuel 的帖子实现附加的行为。

<Window.Resources>
    <Style TargetType="{x:Type TextBox}" BasedOn="{StaticResource {x:Type TextBox}}">
        <Style.Setters>
            <Setter Property="b:InputBindingsManager.UpdatePropertySourceWhenEnterPressed" Value="TextBox.Text"/>
        </Style.Setters>
    </Style>
</Window.Resources>

如果需要,您始终可以通过将 Style 放入Grid包含目标 TextBox 的 Window 子元素(即 a )之一的 Resources 来限制范围。

于 2018-03-02T03:27:32.287 回答
3

这对我有用:

        <TextBox                 
            Text="{Binding Path=UserInput, UpdateSourceTrigger=PropertyChanged}">
            <TextBox.InputBindings>
                <KeyBinding Key="Return" 
                            Command="{Binding Ok}"/>
            </TextBox.InputBindings>
        </TextBox>
于 2016-06-09T16:42:59.310 回答
2

如果您在 TextBox 中使用 MultiBinding,则需要使用BindingOperations.GetMultiBindingExpressionmethod 而不是BindingOperations.GetBindingExpression.

// Get the correct binding expression based on type of binding
//(simple binding or multi binding.
BindingExpressionBase binding = 
  BindingOperations.GetBindingExpression(element, prop);
if (binding == null)
{
    binding = BindingOperations.GetMultiBindingExpression(element, prop);
}

if (binding != null)
{
     object value = element.GetValue(prop);
     if (string.IsNullOrEmpty(value.ToString()) == true)
     {
         binding.UpdateTarget();
     }
     else
     {
          binding.UpdateSource();
     }
}
于 2011-09-28T16:11:56.193 回答
1

在这里使用附加行为非常优雅地回答,这是我对几乎任何事情的首选方法。

WPF如何让文本框在点击回车后失去焦点

于 2016-08-30T18:38:17.270 回答
0

我个人认为拥有标记扩展是一种更清洁的方法。

public class UpdatePropertySourceWhenEnterPressedExtension : MarkupExtension
{
    public override object ProvideValue(IServiceProvider serviceProvider)
    {
        return new DelegateCommand<TextBox>(textbox => textbox.GetBindingExpression(TextBox.TextProperty).UpdateSource());
    }
}


<TextBox x:Name="TextBox"
             Text="{Binding Text}">
        <TextBox.InputBindings>
            <KeyBinding Key="Enter"
                        Command="{markupExtensions:UpdatePropertySourceWhenEnterPressed}" 
                        CommandParameter="{Binding ElementName=TextBox}"/>
        </TextBox.InputBindings>
</TextBox>
于 2018-05-03T15:14:24.117 回答
0

一个不同的解决方案(不使用 xaml,但我认为仍然很干净)。

class ReturnKeyTextBox : TextBox
{
    protected override void OnKeyUp(KeyEventArgs e)
    {
        base.OnKeyUp(e);
        if (e.Key == Key.Return)
            GetBindingExpression(TextProperty).UpdateSource();
    }
}
于 2020-04-28T06:22:28.577 回答