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.