web-dev-qa-db-ja.com

Enterキーを押してTextBoxをバインド

TextBoxのデフォルトのデータバインディングはTwoWayであり、TextBoxがフォーカスを失ったときにのみテキストをプロパティにコミットします。

を押したときにデータバインディングを実行する簡単なXAML方法はありますか Enter TextBox?のキー。コードビハインドで行うのは非常に簡単ですが、このTextBoxが複雑なDataTemplateの中にあると想像してください。

102
Jobi Joy

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

(InputBindingsManagerを配置したネームスペースを指すXAMLファイルのルート要素に、「b」のxmlns clr-namespace参照を含めることを確認する必要があります)。

129
Samuel Jack

あなたが説明していることを行う「純粋なXAML」の方法があるとは思わない。次のように pdateSourceTrigger プロパティを設定することにより、TextBox内のテキストが変更されるたびに(TextBoxがフォーカスを失うときではなく)更新するようにバインディングを設定できます。

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

UpdateSourceTriggerを "Explicit"に設定し、TextBoxのPreviewKeyDownイベント(Enterキーを探す)を処理した場合、目的を達成できますが、コードビハインドが必要になります。おそらく、何らかの種類の添付プロパティ(私の EnterKeyTraversal プロパティに似ています)woudldが機能します。

46
Matt Hamilton

これが私がこの問題を解決した方法です。コードビハインドに入る特別なイベントハンドラーを作成しました。

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で動作するはずです。これで、この1つのイベントハンドラーを、この機能を必要とするすべてのテキストボックスに追加できます。

43
Ben

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}" />
25
Ryan Versaw

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に使用できますが、コードビハインドにコードがないことをお勧めします。ただし、これを独自のクラスに配置して、System.Windows.ControlsをVMで。コードガイドラインの厳格さに依存します。

11
toadflakz

これは私にとって非常に簡単で、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

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イベントをキャッチします。

4
ausadmin

よりシンプルなのは、コードビハインドに何も追加せずに、UpdateSourceTriggerのバインディングでPropertyChangedTextBoxに設定するだけです。ちょうどこのような:

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

わたしにはできる。

3
Mr.Pidra

これは元の質問に対する答えではなく、@-Samuel Jackによる 受け入れられた答え の拡張です。私は自分のアプリケーションで次のことを行いましたが、サミュエルのソリューションの優雅さにa敬の念を抱いていました。 TextBoxだけでなく、任意のコントロールで使用できるため、非常にクリーンで再利用可能です。これはコミュニティと共有すべきだと思いました。

Enterキーでバインドソースをすべて更新する必要があるTextBoxesが1,000個あるウィンドウがある場合、以下のXAMLをWindowResources各TextBoxに添付するのではなく。まず、 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>

必要に応じて、ターゲットTextBoxを含むWindowの子要素(つまりGrid)のいずれかのリソースにStyleを配置することにより、いつでもスコープを制限できます。

3
Nik

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

これは私のために働く:

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

添付のビヘイビアを使用して非常にエレガントに回答しました。ほとんどすべての場合に私の好みの方法です。

WPF Enterを押した後にテキストボックスのフォーカスを失う方法

1
NielW

個人的には、マークアップ拡張機能を使用する方がよりクリーンなアプローチだと思います。

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