developer tip

Enter 키를 누를 때 TextBox 바인딩

copycodes 2020. 8. 14. 07:54
반응형

Enter 키를 누를 때 TextBox 바인딩


기본 데이터 바인딩 TextBoxTwoWay이며 TextBox포커스를 잃은 경우에만 속성에 텍스트를 커밋합니다 .

?의 Enter키를 누를 때 데이터 바인딩을 수행하는 쉬운 XAML 방법 TextBox있습니까? 뒤에있는 코드에서하는 것이 매우 쉽다는 것을 알고 있지만 이것이 TextBox복잡한 내부에 있다고 상상해보십시오 DataTemplate.


연결된 동작 을 만들어 순수한 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 참조를 포함해야합니다.)


나는 당신이 설명하는 것을 수행하는 "순수한 XAML"방법이 있다고 생각하지 않습니다. 다음 과 같이 UpdateSourceTrigger 속성 을 설정하여 TextBox의 텍스트가 변경 될 때마다 (TextBox가 포커스를 잃을 때가 아니라) 업데이트되도록 바인딩을 설정할 수 있습니다 .

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

UpdateSourceTrigger를 "Explicit"로 설정 한 다음 TextBox의 PreviewKeyDown 이벤트 (Enter 키 찾기)를 처리하면 원하는 것을 얻을 수 있지만 코드 숨김이 필요합니다. 아마도 일종의 연결된 속성 (내 EnterKeyTraversal 속성 과 유사 )이 당신을 위해 작동합니다.


이것이 제가이 문제를 해결 한 방법입니다. 코드 뒤에있는 특수 이벤트 핸들러를 만들었습니다.

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에서 작동해야합니다. 이제이 하나의 이벤트 핸들러를이 기능이 필요한 모든 텍스트 상자에 추가 할 수 있습니다.


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

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>

...which means you are passing the TextBox itself as the parameter to the Command.

This leads to your Command looking like this (if you're using a DelegateCommand-style implementation in your VM):

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

This Command implementation can be used for any TextBox and best of all no code in the code-behind though you may want to put this in it's own class so there are no dependencies on System.Windows.Controls in your VM. It depends on how strict your code guidelines are.


Here is an approach that to me seems quite straightforward, and easier that adding an AttachedBehaviour (which is also a valid solution). We use the default UpdateSourceTrigger (LostFocus for TextBox), and then add an InputBinding to the Enter Key, bound to a command.

The xaml is as follows

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

Then the Command methods are

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

And the TextBox is bound to the Property

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

So far this seems to work well and catches the Enter Key event in the TextBox.


More simple, just set UpdateSourceTrigger to PropertyChanged in your TextBox's binding without adding anything in codebehind. Just like this:

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

It works for me.


This is not an answer to the original question, but rather an extension of the accepted answer by @Samuel Jack. I did the following in my own application, and was in awe of the elegance of Samuel's solution. It is very clean, and very reusable, as it can be used on any control, not just the TextBox. I thought this should be shared with the community.

If you have a Window with a thousand TextBoxes that all require to update the Binding Source on Enter, you can attach this behaviour to all of them by including the XAML below into your Window Resources rather than attaching it to each TextBox. First you must implement the attached behaviour as per Samuel's post, of course.

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

You can always limit the scope, if needed, by putting the Style into the Resources of one of the Window's child elements (i.e. a Grid) that contains the target TextBoxes.


In case you are using MultiBinding with your TextBox you need to use BindingOperations.GetMultiBindingExpression method instead of 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();
     }
}

This works for me:

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

Answered here quite elegantly using attached behaviors, my preferred method for almost anything.

WPF how to make textbox lose focus after hitting enter


I personally think having a Markup Extension is a cleaner approach.

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>

참고URL : https://stackoverflow.com/questions/563195/bind-textbox-on-enter-key-press

반응형