Привязка текстового поля при нажатии клавиши Enter
привязка данных по умолчанию на TextBox и TwoWay и он фиксирует текст к свойству только тогда, когда TextBox потерял свой фокус.
есть ли простой способ XAML сделать привязку данных, когда я нажимаю введите ключ TextBox?. Я знаю, что это довольно легко сделать в коде позади, но представьте, если это TextBox находится внутри какого-то комплекса DataTemplate.
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 в текстовом поле.
более простой, просто задайте
UpdateSourceTriggertoPropertyChangedв своем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