Enter 키를 누를 때 TextBox 바인딩


108

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

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

답변:


137

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


11
미래의 독자들에게 몇 분의 시간을 절약하기 위해 저는 이것을 제 프로젝트에서 방금 사용했으며 위에 제공된 샘플 XAML이 제대로 작동하지 않았습니다. 캐릭터가 변경 될 때마다 소스가 업데이트되었습니다. "UpdateSourceTrigger = PropertyChanged"를 "UpdateSourceTrigger = Explicit"로 변경하면 문제가 해결되었습니다. 이제 모든 것이 원하는대로 작동합니다.
ihake

1
@ihake : 당신의 권장 변경도 불구하고 손실 초점 업데이트를 방지 할 생각
VoteCoffee

4
UpdateSourceTrigger = PropertyChanged는 실수를 입력 할 때 불편할 수 있습니다. 예 : "3." float에 바인딩되면 문제가 발생합니다. 첨부 된 동작 외에 UpdateSourceTrigger (또는 LostFocus로 설정)를 지정하지 않는 것이 좋습니다. 이것은 두 세계의 장점을 모두 제공합니다.
David Hollinshead

2
잘했습니다! Tiny nit-pick : UpdatePropertySourceWhenEnterPressed유효한 값에서 다른 유효한 값으로 변경 되면 PreviewKeyDown불필요하게 이벤트 구독을 취소하고 다시 구독하는 것 입니다. 대신, 당신이 필요로하는 것은 그것이 있는지 아닌지를 확인하는 것 e.NewValue입니다 null. 그렇지 않은 경우 null구독하십시오. 그렇지 않은 경우 null구독을 취소합니다.
Nicholas Miller

1
이 솔루션을 좋아합니다. 게시 해 주셔서 감사합니다! 이 동작을 응용 프로그램의 수많은 TextBox에 연결해야하는 사람을 위해이 답변에 대한 확장을 게시하여이를 매우 쉽게 수행 할 수있는 방법을 게시했습니다. 여기에서 게시물을 참조하십시오
Nik

50

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

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


8
이것이 과소 평가 된 게시물이라는 것을 알려줍니다. "XAML-y"이기 때문에 많은 답변이 인기가 있습니다. 그러나 이것은 대부분의 경우 공간을 절약하는 것으로 보입니다.
j riv

3
+1 이렇게하면 TextBox 바인딩이 이미 원하는 방식 (스타일, 유효성 검사, 양방향 등)으로 동작하도록 힘들게 이미 얻은 경우 UpdateSourceTrigger를 그대로 둘 수 있지만 현재 Enter 키를 누른 후 입력을받지 못합니다. .
Jamin

2
이것은 제가 찾은 가장 깨끗한 접근 방식이며 제 생각에는 답으로 표시되어야합니다. 발신자에 대한 추가 null 검사, Key.Return (키보드의 Enter 키가 Key.Return 반환) 및 Keyboard.ClearFocus ()에 대한 검사를 추가하여 소스 속성을 업데이트 한 후 TextBox에서 포커스를 제거했습니다. 피어 리뷰를 기다리고있는 답변을 수정했습니다.
Oystein

2
위의 의견에 동의하십시오. 하지만 저에게는 KeyDown 이벤트가 더 적절했습니다
Allender

1
내가 사용하는 KeyBinding내 UI는 어획량 키 입력하는 "기본"컨트롤을 가지고 있기 때문에이 방법을 발사하기 위해 XAML에서. UI 트리를 "Default"컨트롤로 전파하는 것을 중지하려면이 TextBox 내에서이를 포착해야합니다.
CAD bloke 19

46

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

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

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


3
이 답변은 답변으로 표시된 답변보다 간단 할 수 있지만 몇 가지 제한 사항이 있습니다. 바인딩 된 속성의 설정 기능에서 어떤 종류의 검사를 수행하려는 이미지 (입력이 유효한지 확인). 사용자가 키를 누를 때마다 체크 기능을 호출하고 싶지 않으십니까? (특히 기능이 시간이 걸리는 경우가 아닙니다).?
sth_Weird

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

9
WPF / Silverlight에서는 상속을 사용해서는 안됩니다. 스타일이 엉망이고 연결된 동작만큼 유연하지 않습니다. 예를 들어 연결된 동작을 사용하면 동일한 텍스트 상자에 Watermark와 UpdateOnEnter를 둘 다 가질 수 있습니다.
Mikhail

14

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과 같이 보입니다 ( DelegateCommandVM에서 스타일 구현을 사용하는 경우 ).

    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.ControlsVM 에 종속성이 없도록 자체 클래스에 넣을 수 있습니다 . 코드 지침이 얼마나 엄격한 지에 따라 다릅니다.


좋은. 매우 깨끗합니다. 멀티 바인딩이 관련된 경우 BindingOperations.GetBindingExpressionBase (tBox, prop); 그런 다음 binding.UpdateTarget ();
VoteCoffee 2014

4

다음은 나에게 매우 간단하고 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

이것은 원래 질문에 대한 답변이 아니라 @Samuel Jack 이 수락 한 답변 의 확장입니다 . 나는 내 자신의 응용 프로그램에서 다음을 수행했으며 Samuel의 솔루션의 우아함에 경외심을 느꼈습니다. 그것은 매우 깨끗하고 재사용이 가능합니다.TextBox . 커뮤니티와 공유해야한다고 생각했습니다.

TextBoxesEnter시 바인딩 소스를 업데이트하는 데 필요한 천 개의 창이있는 경우 Window Resources각 TextBox에 연결하는 대신 아래 XAML을 포함하여이 동작을 모든 항목에 연결할 수 있습니다 . 먼저 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>

필요한 경우 항상 Grid대상 TextBox를 포함하는 Window 자식 요소 중 하나의 리소스 (예 : a )에 스타일을 넣어 범위를 제한 할 수 있습니다 .


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

1

이것은 나를 위해 작동합니다.

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


0

개인적으로 Markup Extension을 사용하는 것이 더 깔끔한 접근 방식이라고 생각합니다.

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

다른 솔루션 (xaml을 사용하지 않지만 여전히 깨끗하다고 ​​생각합니다).

class ReturnKeyTextBox : TextBox
{
    protected override void OnKeyUp(KeyEventArgs e)
    {
        base.OnKeyUp(e);
        if (e.Key == Key.Return)
            GetBindingExpression(TextProperty).UpdateSource();
    }
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.