Привязка текстового поля при нажатии клавиши Enter



привязка данных по умолчанию на TextBox и TwoWay и он фиксирует текст к свойству только тогда, когда TextBox потерял свой фокус.



есть ли простой способ XAML сделать привязку данных, когда я нажимаю введите ключ TextBox?. Я знаю, что это довольно легко сделать в коде позади, но представьте, если это TextBox находится внутри какого-то комплекса DataTemplate.

655   12  

12 ответов:

вы можете сделать себе чистый подход 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 свойство к тому, который вы хотите обновить, когда введите клавиша нажата. Вот так

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

(вам просто нужно убедиться, что вы включили ссылку xmlns clr-namespace для "b" в корневой элемент вашего файла XAML, указывающий, в какое пространство имен вы помещаете InputBindingsManager in).

Я не верю, что есть какой-либо "чистый XAML" способ сделать то, что вы описываете. Вы можете настроить привязку так, чтобы она обновлялась всякий раз, когда текст в текстовом поле изменяется (а не когда текстовое поле теряет фокус), установив UpdateSourceTrigger собственность, как это:

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

Если вы установите UpdateSourceTrigger в "явное", а затем обработаете событие PreviewKeyDown текстового поля (ищете ключ ввода), то вы можете достичь того, что хотите, но для этого потребуется с фоновым кодом. Возможно, какое-то прикрепленное свойство (подобное моему 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(); }
    }
}

затем я просто добавил Это как обработчик событий KeyUp в XAML:

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

может быть способ обойти этот шаг, но в противном случае вы должны привязать вот так (используя явное):

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

...значит, ты мимо TextBox сам как параметр к Command.

это приводит к вашей Command выглядит так (если вы используете 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 в вашей виртуальной машине. Это зависит от того, насколько строгие правила кодекса.

вот подход, который мне кажется довольно простым, и проще, чем добавление AttachedBehaviour (что также является допустимым решением). Мы используем UpdateSourceTrigger по умолчанию (LostFocus для TextBox), а затем добавляем InputBinding к клавише Enter, привязанной к команде.

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

и текстовое поле привязано к свойству

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

пока это, кажется, работает ну и ловит ключевое событие Enter в текстовом поле.

более простой, просто задайте UpdateSourceTrigger to PropertyChanged в своем TextBoxпривязка без добавления чего-либо в codebehind. Вот так:

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

это работает для меня.

в случае, если вы используете 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();
     }
}

это работает для меня:

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

это не ответ на исходный вопрос, а скорее продолжение принято отвечать by @Samuel Jack. Я сделал следующее в своем собственном приложении и был в восторге от элегантности решения Сэмюэля. Он очень чистый и очень многоразовый, так как его можно использовать на любом элементе управления, а не только на TextBox. Я думал, что это должно быть разделено с сообществом.

если у вас есть окно с тысячей TextBoxes что все требуют обновить источник привязки при вводе, вы можете прикрепить это поведение ко всем из них, включив XAML ниже в свой WindowResources вместо того, чтобы прикреплять его к каждому текстовому полю. Сначала вы должны реализовать прикрепленное поведение в соответствии с Сэмюэль должности, конечно.

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

вы всегда можете ограничить область действия, если это необходимо, поместив стиль в ресурсы одного из дочерних элементов окна (т. е. a Grid), которая содержит целевые текстовые поля.

ответил Здесь довольно элегантно, используя прикрепленное поведение, мой предпочтительный метод почти для всего.

WPF как сделать текстовое поле потерять фокус после нажатия enter

Я лично думаю, что расширение разметки-это более чистый подход.

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>

Comments

    Ничего не найдено.