TextBox
上的默认数据绑定是TwoWay
,只有在TextBox
失去焦点时才会将文本提交到属性。
当我按下TextBox
上的 Enter 键时,是否有任何简单的XAML方法可以进行数据绑定?我知道在后面的代码中很容易做到,但想象一下这个TextBox
是否在某个复杂的DataTemplate
内。
答案 0 :(得分:128)
您可以通过创建attached behaviour来使自己成为纯粹的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中,当按下 Enter 键时,将InputBindingsManager.UpdatePropertySourceWhenEnterPressedProperty
属性设置为要更新的属性。喜欢这个
<TextBox Name="itemNameTextBox"
Text="{Binding Path=ItemName, UpdateSourceTrigger=PropertyChanged}"
b:InputBindingsManager.UpdatePropertySourceWhenEnterPressed="TextBox.Text"/>
(您只需要确保在XAML文件的根元素中包含“b”的xmlns clr-namespace引用,该文件指向您将InputBindingsManager放入哪个命名空间。)
答案 1 :(得分:46)
我不相信有任何“纯XAML”方式来做你所描述的。您可以通过设置UpdateSourceTrigger属性来设置绑定,以便在TextBox中的文本发生更改时(而不是在TextBox失去焦点时)更新它,如下所示:
<TextBox Name="itemNameTextBox"
Text="{Binding Path=ItemName, UpdateSourceTrigger=PropertyChanged}" />
如果你将UpdateSourceTrigger设置为“Explicit”,然后处理TextBox的PreviewKeyDown事件(寻找Enter键),那么你可以实现你想要的,但它需要代码隐藏。也许某种附加属性(类似于我的EnterKeyTraversal属性)woudld适合你。
答案 2 :(得分:39)
这就是我解决这个问题的方法。我创建了一个特殊的事件处理程序,进入后面的代码:
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中工作。现在可以将此一个事件处理程序添加到需要此功能的所有文本框中。
答案 3 :(得分: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}" />
答案 4 :(得分:11)
如果你将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
看起来像这样(如果您在VM中使用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
的依赖关系。这取决于您的代码指南的严格程度。
答案 5 :(得分:4)
对我而言,这种方法看起来非常简单,并且更容易添加AttachedBehaviour(这也是一种有效的解决方案)。我们使用默认的UpdateSourceTrigger(TextBox的LostFocus),然后将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>
然后Command方法
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事件。
答案 6 :(得分:3)
更简单,只需在UpdateSourceTrigger
的约束中将PropertyChanged
设置为TextBox
,而无需在代码隐藏中添加任何内容。
就像这样:
<TextBox Text="{Binding Path=BoundProperty, UpdateSourceTrigger=PropertyChanged}"/>
它对我有用。
答案 7 :(得分:3)
这不是原始问题的答案,而是@Samuel Jack对accepted answer的扩展。我在自己的应用程序中做了以下事情,并对塞缪尔解决方案的优雅感到敬畏。它非常干净,非常可重复使用,因为它可以用于任何控件,而不仅仅是TextBox
。我认为这应该与社区分享。
如果你有一个千人TextBoxes
的窗口都要求在Enter上更新绑定源,你可以通过将下面的XAML包含在你的Window
{{ 1}}而不是将它附加到每个TextBox。首先,您必须按照Samuel's post实现附加行为。
Resources
如果需要,您可以通过将Style放入包含目标TextBox的一个Window子元素(即<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>
)的Resources中来限制范围。
答案 8 :(得分: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();
}
}
答案 9 :(得分:2)
这对我有用:
<TextBox
Text="{Binding Path=UserInput, UpdateSourceTrigger=PropertyChanged}">
<TextBox.InputBindings>
<KeyBinding Key="Return"
Command="{Binding Ok}"/>
</TextBox.InputBindings>
</TextBox>
答案 10 :(得分:1)
使用附加行为非常优雅地回答这里,这是我几乎所有方法的首选方法。
答案 11 :(得分:0)
我个人认为使用标记扩展是一种更清洁的方法。
Address Age (min) Hardware Addr Interface
127.1.0.2 0 0010.gh587.b001 Internal1/1
192.102.1.20 0 2c21.099a.9c01 Management1/1
192.102.1.23 0 c4f5.6061.b8dd Management1/1
192.102.1.25 0 c4f5.1b49.66a9 Management1/1
192.102.1.46 0 001c.7c03.1470 Management1/1
public class UpdatePropertySourceWhenEnterPressedExtension : MarkupExtension
{
public override object ProvideValue(IServiceProvider serviceProvider)
{
return new DelegateCommand<TextBox>(textbox => textbox.GetBindingExpression(TextBox.TextProperty).UpdateSource());
}
}
答案 12 :(得分:0)
一种不同的解决方案(我认为不使用xaml,但仍然很干净)。
class ReturnKeyTextBox : TextBox
{
protected override void OnKeyUp(KeyEventArgs e)
{
base.OnKeyUp(e);
if (e.Key == Key.Return)
GetBindingExpression(TextProperty).UpdateSource();
}
}