按下 Enter 键时绑定文本框

问题描述 投票:0回答:13

TextBox

 上的默认数据绑定是 
TwoWay
,仅当 
TextBox
 失去焦点时才会将文本提交到属性。

当我按

TextBox 上的 Enter

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

c# .net wpf xaml textbox
13个回答
152
投票
您可以通过创建

附加行为来使自己成为纯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 命名空间引用,指向您将 InputBindingsManager 放入的命名空间)。


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

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 中工作。现在可以将这一事件处理程序添加到需要此功能的所有文本框中。


47
投票
我不相信有任何“纯 XAML”方法可以完成您所描述的操作。您可以设置一个绑定,以便通过设置

UpdateSourceTrigger 属性,只要 TextBox 中的文本发生更改(而不是当 TextBox 失去焦点时)它就会更新,如下所示:

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

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

EnterKeyTraversal 属性)适合您。


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}" />
    

17
投票
如果您结合 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
 样式实现):

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

 实现可用于任何 
TextBox
 ,而且最好的是代码隐藏中没有代码,尽管您可能希望将其放入它自己的类中,这样您的虚拟机中就不会依赖于 
System.Windows.Controls
 。这取决于您的代码指南有多严格。


6
投票
这不是原始问题的答案,而是@Samuel Jack

接受的答案的扩展。我在自己的应用程序中执行了以下操作,并对塞缪尔解决方案的优雅感到敬畏。它非常干净,而且非常可重复使用,因为它可以用于任何控件,而不仅仅是TextBox

。我认为这应该与社区分享。

如果您有一个包含 1000 个

TextBoxes

 的窗口,所有这些都需要在 Enter 时更新绑定源,您可以通过将下面的 XAML 包含到您的 
Window
 
Resources
 中而不是将其附加到所有窗口来将此行为附加到所有窗口每个文本框。当然,首先您必须按照
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>

如果需要,您始终可以通过将样式放入包含目标文本框的窗口子元素之一(即

Grid

)的资源中来限制范围。


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

xaml如下

<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

文本框绑定到属性

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 事件。


3
投票
这对我有用:

<TextBox Text="{Binding Path=UserInput, UpdateSourceTrigger=PropertyChanged}"> <TextBox.InputBindings> <KeyBinding Key="Return" Command="{Binding Ok}"/> </TextBox.InputBindings> </TextBox>
    

2
投票
如果您在 TextBox 中使用 MultiBinding,则需要使用

BindingOperations.GetMultiBindingExpression

 方法而不是 
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(); } }
    

1
投票
使用附加行为在这里非常优雅地回答,这是我几乎所有事情的首选方法。

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


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

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>
    

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

class ReturnKeyTextBox : TextBox { protected override void OnKeyUp(KeyEventArgs e) { base.OnKeyUp(e); if (e.Key == Key.Return) GetBindingExpression(TextProperty).UpdateSource(); } }
    

0
投票
我刚刚发现了一个即使使用 x:Bind 也能做到这一点的 hacky 版本。 你可以做这样的事情:

if (e.Key == Windows.System.VirtualKey.Enter) { FocusManager.TryMoveFocus(FocusNavigationDirection.Next); FocusManager.TryMoveFocus(FocusNavigationDirection.Previous); e.Handled = true; return; }
视图中至少必须有其他控件才能使其工作。但这至少是一些东西。

© www.soinside.com 2019 - 2024. All rights reserved.