wake-up-neo.com

Bind TextBox bei Drücken der Eingabetaste

Die Standarddatenbindung für TextBox lautet TwoWay und schreibt den Text nur dann in die Eigenschaft, wenn TextBox den Fokus verloren hat.

Gibt es eine einfache XAML-Methode, um die Datenbindung zu aktivieren, wenn ich auf drücke? Enter Schlüssel auf dem TextBox ?. Ich weiß, dass es im Code dahinter ziemlich einfach ist, aber stellen Sie sich vor, dass sich dieser TextBox in einem komplexen DataTemplate befindet.

99
Jobi Joy

Sie können sich einen reinen XAML-Ansatz machen, indem Sie ein angefügtes Verhalten erstellen.

Etwas wie das:

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();
        }
    }
}

Dann setzen Sie in Ihrer XAML die Eigenschaft InputBindingsManager.UpdatePropertySourceWhenEnterPressedProperty auf diejenige, die Sie aktualisieren möchten, wenn die Enter Taste wird gedrückt. So was

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

(Sie müssen lediglich sicherstellen, dass im Stammelement Ihrer XAML-Datei eine xmlns clr-Namespace-Referenz für "b" enthalten ist, die auf den Namespace verweist, in den Sie den InputBindingsManager eingefügt haben.).

127
Samuel Jack

Ich glaube nicht, dass es eine "reine XAML" -Methode gibt, um das zu tun, was Sie beschreiben. Sie können eine Bindung so einrichten, dass sie immer dann aktualisiert wird, wenn sich der Text in einer TextBox ändert (und nicht, wenn die TextBox den Fokus verliert), indem Sie die UpdateSourceTrigger -Eigenschaft wie folgt festlegen:

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

Wenn Sie UpdateSourceTrigger auf "Explicit" setzen und dann das PreviewKeyDown-Ereignis der TextBox behandeln (nach der Eingabetaste suchen), können Sie das erreichen, was Sie möchten, aber dafür wäre ein Code-Behind erforderlich. Vielleicht würde eine Art angehängte Eigenschaft (ähnlich meiner EnterKeyTraversal -Eigenschaft) für Sie funktionieren.

46
Matt Hamilton

So habe ich dieses Problem gelöst. Ich habe einen speziellen Event-Handler erstellt, der in den Code dahinter ging:

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(); }
    }
}

Dann habe ich dies als KeyUp-Eventhandler in der XAML hinzugefügt:

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

Der Event-Handler verwendet seine sender-Referenz, um die Aktualisierung seiner eigenen Bindung zu veranlassen. Da der Event-Handler eigenständig ist, sollte er in einem komplexen DataTemplate arbeiten. Dieser eine Ereignishandler kann jetzt zu allen Textfeldern hinzugefügt werden, die diese Funktion benötigen.

37
Ben

Sie können leicht ein eigenes Steuerelement erstellen, das von TextBox erbt, und es im gesamten Projekt wiederverwenden.

Etwas Ähnliches sollte funktionieren:

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();
            }
        }
    }
}

Es gibt möglicherweise eine Möglichkeit, diesen Schritt zu umgehen, aber ansonsten sollten Sie so binden (mithilfe von Explicit):

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

Wenn Sie die Lösungen von Ben und ausadmin kombinieren, erhalten Sie eine sehr MVVM-freundliche Lösung:

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

... was bedeutet, dass Sie die TextBox selbst als Parameter an die Command übergeben.

Dies führt dazu, dass Ihre Command so aussieht (wenn Sie eine DelegateCommand- Implementierung in Ihrer VM verwenden):

    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();
        }
    }

Diese Command-Implementierung kann für jeden TextBox-Code und vor allem keinen Code im Code-Behind verwendet werden, auch wenn Sie dies in eine eigene Klasse einfügen möchten, sodass in Ihrer VM keine Abhängigkeiten von System.Windows.Controls bestehen. Es hängt davon ab, wie streng Ihre Codierrichtlinien sind.

11
toadflakz

Hier ist ein Ansatz, der mir sehr einfach erscheint, und einfacher als das Hinzufügen eines AttachedBehaviour (was auch eine gültige Lösung ist). Wir verwenden den Standard-UpdateSourceTrigger (LostFocus für TextBox) und fügen der Eingabetaste dann eine InputBinding hinzu, die an einen Befehl gebunden ist.

Das Xaml lautet wie folgt

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

Dann sind die Command-Methoden 

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

Und das Textfeld ist an die Eigenschaft gebunden

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

Bisher scheint dies gut zu funktionieren und fängt das Ereignis Enter Key in der TextBox ab.

4
ausadmin

Einfacher setzen Sie einfach UpdateSourceTrigger auf PropertyChanged in der Bindung Ihrer TextBox, ohne etwas in codebehind ..__ hinzuzufügen.

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

Für mich geht das.

3
Mr.Pidra

Dies ist keine Antwort auf die ursprüngliche Frage, sondern eine Erweiterung der akzeptierten Antwort von @Samuel Jack. Ich tat das Folgende in meiner eigenen Bewerbung und war beeindruckt von der Eleganz von Samuel's Lösung. Es ist sehr sauber und sehr wiederverwendbar, da es für jedes Steuerelement verwendet werden kann, nicht nur für die Variable TextBox. Ich dachte, das sollte mit der Community geteilt werden.

Wenn Sie über ein Fenster mit Tausend TextBoxes verfügen, das zum Aktualisieren der Bindungsquelle bei der Eingabe erforderlich ist, können Sie dieses Verhalten allen zuweisen, indem Sie die unten stehende XAML in Ihre WindowResources einfügen, anstatt sie an jede TextBox anzuhängen. Zuerst müssen Sie das angehängte Verhalten gemäß Samuel's Post natürlich implementieren. 

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

Sie können den Geltungsbereich bei Bedarf jederzeit einschränken, indem Sie den Stil in die Ressourcen eines der untergeordneten Elemente des Fensters (d. H. Einer Grid) einfügen, die die ZieltextBoxen enthält.

3
Nik

Wenn Sie MultiBinding mit Ihrer TextBox verwenden, müssen Sie anstelle von BindingOperations.GetMultiBindingExpression die Methode BindingOperations.GetBindingExpression verwenden.

// 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();
     }
}
2
akjoshi

Das funktioniert für mich:

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

Hier mit ziemlich elegantem Verhalten beantwortet, meine bevorzugte Methode für fast alles.

WPF, wie Textbox nach dem Drücken der Eingabetaste den Fokus verliert

1
NielW

Ich persönlich denke, dass eine Markup-Erweiterung sauberer ist.

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
metoyou