【问题标题】:Bind TextBox on Enter-key press按 Enter 键绑定文本框
【发布时间】:2010-10-08 11:18:22
【问题描述】:

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

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

【问题讨论】:

    标签: c# wpf xaml .net-3.5 textbox


    【解决方案1】:

    您可以通过创建attached behaviour 来使自己成为纯 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 的命名空间)。

    【讨论】:

    • 为了节省未来的读者几分钟的时间,我只是在我的项目中使用了它,上面提供的示例 XAML 不能正常工作。每次更改字符时都会更新源。将“UpdateSourceTrigger=PropertyChanged”更改为“UpdateSourceTrigger=Explicit”解决了这个问题。现在一切正常。
    • @ihake:我认为您推荐的更改也将防止更新焦点丢失
    • UpdateSourceTrigger=PropertyChanged 在输入实数时可能不方便。例如“3”。绑定到浮点数时会导致问题。除了附加的行为之外,我建议不要指定 UpdateSourceTrigger(或设置为 LostFocus)。这提供了两全其美。
    • 出色的工作!微小的挑剔:当UpdatePropertySourceWhenEnterPressed 从有效值更改为不同的有效值时,您将不必要地取消订阅和重新订阅PreviewKeyDown 事件。相反,您只需要检查e.NewValue 是否为null。如果不是null,则订阅;否则,如果null,则取消订阅。
    • 我喜欢这个解决方案,感谢发布!对于需要将此行为附加到应用程序中的众多文本框的任何人,我发布了对此答案的扩展,说明如何非常轻松地实现这一目标。 See the post here
    【解决方案2】:

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

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

    【讨论】:

    • 有人告诉我这是一篇被低估的帖子。许多答案很受欢迎,因为它们是“XAML-y”。但在大多数情况下,这个似乎可以节省空间。
    • +1 这也允许你不用UpdateSourceTrigger,以防你已经煞费苦心地让你的TextBox绑定已经按照你想要的方式运行(使用样式、验证、双向等),但目前只是按 Enter 后不会收到输入。
    • 这是我发现的最干净的方法,我认为应该标记为答案。添加了对发件人的额外空检查、对 Key.Return 的检查(我的键盘上的 Enter 键返回 Key.Return)和 Keyboard.ClearFocus() 以在更新源属性后从 TextBox 中移除焦点。我已对等待同行评审的答案进行了编辑。
    • 同意上面的cmets。但对我来说 KeyDown 事件更合适
    • 我在 XAML 中使用了KeyBinding 来触发此方法,因为我的 UI 有一个“默认”控件可以捕获回车键。您需要在此 TextBox 中捕获它,以阻止它将 UI 树传播到“默认”控件。
    【解决方案3】:

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

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

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

    【讨论】:

    • 此答案可能比标记为答案的答案更简单,但它有一些限制。您想要在绑定属性的 set-function 中进行某种检查的图像(检查输入是否有效)。您不想在用户按下每个键后调用该检查函数,是吗(尤其是在该函数需要一些时间时)。?
    【解决方案4】:

    您可以轻松地创建自己的继承自 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}" />
    

    【讨论】:

    • 在 WPF/Silverlight 中,您永远不应该使用继承 - 它会混淆样式并且不如附加行为灵活。例如,使用附加行为,您可以在同一个文本框上同时拥有 Watermark 和 UpdateOnEnter。
    【解决方案5】:

    如果您将 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 看起来像这样(如果您在 VM 中使用 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 并且最好在代码隐藏中没有代码,尽管您可能希望将它放在它自己的类中,因此您的VM 中不依赖于System.Windows.Controls .这取决于您的代码准则有多严格。

    【讨论】:

    • 不错。很干净。如果涉及多重绑定,您可能需要使用 BindingOperations.GetBindingExpressionBase(tBox, prop);然后是 binding.UpdateTarget();
    【解决方案6】:

    在我看来,这是一种非常简单的方法,并且比添加 AttachedBehaviour 更容易(这也是一个有效的解决方案)。我们使用默认的UpdateSourceTrigger(TextBox为LostFocus),然后在Enter Key上添加一个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>
    

    那么Command方法就是

    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绑定到Property

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

    【讨论】:

      【解决方案7】:

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

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

      <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>
      

      如果需要,您始终可以通过将样式放入包含目标文本框的 Window 子元素之一的资源(即Grid)来限制范围。

      【讨论】:

        【解决方案8】:

        这对我有用:

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

        【讨论】:

          【解决方案9】:

          如果您在 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();
               }
          }
          

          【讨论】:

            【解决方案10】:

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

            WPF how to make textbox lose focus after hitting enter

            【讨论】:

              【解决方案11】:

              我个人认为使用标记扩展是一种更简洁的方法。

              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>
              

              【讨论】:

                【解决方案12】:

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

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

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 2016-08-13
                  • 2018-10-03
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多