Bind TextBox при нажатии клавиши Enter
Данной привязкой по умолчанию на TextBox
является TwoWay
, и она передает текст в свойство только тогда, когда TextBox
потерял фокус.
Есть ли простой способ XAML для создания привязки данных, когда я нажимаю клавишу Enter на TextBox
?. Я знаю, что это довольно легко сделать в коде позади, но представьте, если этот TextBox
находится внутри некоторого сложного DataTemplate
.
Ответы
Ответ 1
Вы можете сделать чистый подход 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"/>
(Вам просто нужно включить ссылку xmlns clr-namespace для "b" в корневом элементе вашего файла XAML, указывающую на какое пространство имен, куда вы помещаете InputBindingsManager).
Ответ 2
Я не верю, что существует какой-то "чистый XAML" способ сделать то, что вы описываете. Вы можете настроить привязку, чтобы она обновлялась всякий раз, когда текст в TextBox менялся (а не когда TextBox теряет фокус), установив UpdateSourceTrigger, например:
<TextBox Name="itemNameTextBox"
Text="{Binding Path=ItemName, UpdateSourceTrigger=PropertyChanged}" />
Если вы установите UpdateSourceTrigger в "Explicit", а затем обработаете событие TextBox PreviewKeyDown (ищет ключ Enter), вы можете достичь того, чего хотите, но для этого потребуется код. Возможно, какое-то прикрепленное свойство (похожее на мое свойство
Ответ 3
Вот как я решил эту проблему. Я создал специальный обработчик событий, который попал в код позади:
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. Этот обработчик событий теперь можно добавить ко всем текстовым полям, которые нуждаются в этой функции.
Ответ 4
Вы можете легко создать свой собственный элемент управления, наследующий от 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}" />
Ответ 5
Если вы объедините оба решения 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
и лучше всего не содержать кода в коде, хотя вы можете захотеть поместить это в свой собственный класс, чтобы в вашей VM не было зависимостей от System.Windows.Controls
, Это зависит от того, насколько строги ваши правила кода.
Ответ 6
Вот такой подход, который мне кажется довольно простым и проще, чем добавление AttachedBehaviour (которое также является допустимым решением). Мы используем по умолчанию UpdateSourceTrigger (LostFocus for TextBox), а затем добавляем InputBinding к Enter Key, привязанный к команде.
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 Key в TextBox.
Ответ 7
Проще всего, просто установите UpdateSourceTrigger
в PropertyChanged
в вашей привязке TextBox
, не добавляя ничего в codebehind.
Точно так же:
<TextBox Text="{Binding Path=BoundProperty, UpdateSourceTrigger=PropertyChanged}"/>
Это работает для меня.
Ответ 8
Если вы используете MultiBinding с вашим TextBox, вам нужно использовать метод 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();
}
}
Ответ 9
Это работает для меня:
<TextBox
Text="{Binding Path=UserInput, UpdateSourceTrigger=PropertyChanged}">
<TextBox.InputBindings>
<KeyBinding Key="Return"
Command="{Binding Ok}"/>
</TextBox.InputBindings>
</TextBox>
Ответ 10
Ответа на этот вопрос довольно элегантно, используя приложенное поведение, мой предпочтительный метод для всего, что угодно.
WPF, как заставить текстовое поле потерять фокус после нажатия кнопки ввода