按Enter键键合文本框

时间:2009-02-18 22:44:51

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

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

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

13 个答案:

答案 0 :(得分:128)

您可以通过创建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中,当按下 Enter 键时,将InputBindingsManager.UpdatePropertySourceWhenEnterPressedProperty属性设置为要更新的属性。喜欢这个

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

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

答案 1 :(得分:46)

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

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

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

答案 2 :(得分:39)

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

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

答案 3 :(得分: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();
            }
        }
    }
}

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

<custom:SubmitTextBox
    Text="{Binding Path=BoundProperty, UpdateSourceTrigger=Explicit}" />

答案 4 :(得分: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>

...这意味着您将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的依赖关系。这取决于您的代码指南的严格程度。

答案 5 :(得分:4)

对我而言,这种方法看起来非常简单,并且更容易添加AttachedBehaviour(这也是一种有效的解决方案)。我们使用默认的UpdateSourceTrigger(TextBox的LostFocus),然后将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事件。

答案 6 :(得分:3)

更简单,只需在UpdateSourceTrigger的约束中将PropertyChanged设置为TextBox,而无需在代码隐藏中添加任何内容。 就像这样:

<TextBox Text="{Binding Path=BoundProperty, UpdateSourceTrigger=PropertyChanged}"/>

它对我有用。

答案 7 :(得分:3)

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

如果你有一个千人TextBoxes的窗口都要求在Enter上更新绑定源,你可以通过将下面的XAML包含在你的Window {{ 1}}而不是将它附加到每个TextBox。首先,您必须按照Samuel's post实现附加行为。

Resources

如果需要,您可以通过将Style放入包含目标TextBox的一个Window子元素(即<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> )的Resources中来限制范围。

答案 8 :(得分: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();
     }
}

答案 9 :(得分:2)

这对我有用:

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

答案 10 :(得分:1)

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

WPF how to make textbox lose focus after hitting enter

答案 11 :(得分:0)

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

Address         Age (min)  Hardware Addr   Interface
127.1.0.2               0  0010.gh587.b001  Internal1/1
192.102.1.20            0  2c21.099a.9c01  Management1/1
192.102.1.23            0  c4f5.6061.b8dd  Management1/1
192.102.1.25            0  c4f5.1b49.66a9  Management1/1
192.102.1.46            0  001c.7c03.1470  Management1/1


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

答案 12 :(得分:0)

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

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