按Enter键键合文本框

Job*_*Joy 103 c# wpf xaml textbox .net-3.5

默认数据绑定TextBoxTwoWay,它只有在TextBox失去焦点时才将文本提交给属性.

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

Sam*_*ack 130

您可以通过创建附加行为来使自己成为纯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();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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

<TextBox Name="itemNameTextBox"
         Text="{Binding Path=ItemName, UpdateSourceTrigger=PropertyChanged}"
         b:InputBindingsManager.UpdatePropertySourceWhenEnterPressed="TextBox.Text"/>
Run Code Online (Sandbox Code Playgroud)

(您只需要确保在XAML文件的根元素中包含"b"的xmlns clr-namespace引用,该文件指向您将InputBindingsManager放入哪个命名空间).

  • 为了节省未来的读者几分钟的摆弄,我只是在我的项目中使用了这个,并且上面提供的示例XAML无法正常工作.每个字符更改都会更新源.将"UpdateSourceTrigger = PropertyChanged"更改为"UpdateSourceTrigger = Explicit"修复了该问题.现在一切正常. (9认同)
  • 键入实数时,UpdateSourceTrigger = PropertyChanged可能不方便.例如"3" 绑定到float时会导致问题.除了附加的行为之外,我建议不要指定UpdateSourceTrigger(或设置为LostFocus).这给了两全其美. (3认同)
  • 优秀作品!小巧的选择:当UpdatePropertySourceWhenEnterPressed从一个有效值更改为另一个有效值时,您将取消订阅并重新订阅PreviewKeyDown事件。相反,您只需要检查e.NewValue是否为null。如果不为空,则订阅;否则为空。否则,如果为null,则退订。 (2认同)

Ben*_*Ben 47

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

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(); }
    }
}
Run Code Online (Sandbox Code Playgroud)

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

<TextBox Text="{Binding TextValue1}" KeyUp="TextBox_KeyEnterUpdate" />
<TextBox Text="{Binding TextValue2}" KeyUp="TextBox_KeyEnterUpdate" />
Run Code Online (Sandbox Code Playgroud)

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

  • 有人告诉我这是一个被低估的帖子.许多答案很受欢迎,因为它们是"XAML-y".但是在大多数情况下,这个似乎可以节省空间. (8认同)
  • +1这还使您可以不理会UpdateSourceTrigger,以防万一您已经辛苦地使TextBox绑定按照您想要的方式运行(使用样式,验证,双向)等,但是当前仅在按Enter键后才收到输入。 (3认同)
  • 这是我发现的最干净的方法,我认为应该将其标记为答案。添加了对发件人的其他空检查,对Key.Return的检查(我的键盘上的Enter键返回Key.Return),以及Keyboard.ClearFocus()在更新source属性后从TextBox移除焦点。我已对等待同行评审的答案进行了编辑。 (2认同)
  • 同意以上评论。但是对我来说KeyDown事件更合适 (2认同)

Mat*_*ton 46

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

<TextBox Name="itemNameTextBox"
    Text="{Binding Path=ItemName, UpdateSourceTrigger=PropertyChanged}" />
Run Code Online (Sandbox Code Playgroud)

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

  • 这个答案可能比标记为答案的答案简单,但它有一些局限性.想要在绑定属性的set-function中进行某种检查的图像(检查输入是否有效).您不希望在用户按下每个键后调用该检查功能,是否(特别是当功能需要一些时间时). (3认同)

Rya*_*saw 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();
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

可能有办法绕过这一步,但否则你应该像这样绑定(使用Explicit):

<custom:SubmitTextBox
    Text="{Binding Path=BoundProperty, UpdateSourceTrigger=Explicit}" />
Run Code Online (Sandbox Code Playgroud)

  • 在WPF/Silverlight中,你永远不应该使用继承 - 它会混淆样式并且不像附加行为那样灵活.例如,对于附加行为,您可以在同一文本框中同时具有Watermark和UpdateOnEnter. (9认同)

toa*_*akz 11

如果将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>
Run Code Online (Sandbox Code Playgroud)

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

这导致您Command看起来像这样(如果您DelegateCommand在VM中使用a- 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();
        }
    }
Run Code Online (Sandbox Code Playgroud)

这个Command实现可以用于TextBox代码隐藏中的任何代码,尽管你可能想把它放在它自己的类中,所以System.Windows.Controls你的VM 中没有依赖.这取决于您的代码指南的严格程度.


Nik*_*Nik 6

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

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

<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>
Run Code Online (Sandbox Code Playgroud)

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


小智 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>
Run Code Online (Sandbox Code Playgroud)

那么命令方法是

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
Run Code Online (Sandbox Code Playgroud)

并且 TextBox 绑定到 Property

 Public Property Txt1 As String
    Get
        Return _txt1
    End Get
    Set(value As String)
        _txt1 = value
        OnPropertyChanged("Txt1")
    End Set
End Property
Run Code Online (Sandbox Code Playgroud)

到目前为止,这似乎工作得很好,并捕获了文本框中的 Enter Key 事件。