数据绑定在默认按钮对话框中按下Enter键时不会更新属性

11

我的设置如下:

  • WPF 4 应用程序
  • MVVM Lite 框架
  • 在对话框模式下,在主窗口上方显示“添加项目”窗口(使用view.ShowDialog())
  • 对话框有多个输入字段,“保存”按钮的IsDefault属性设置为True
  • 使用命令绑定处理“保存”按钮
  • 在视图的XAML和ViewModel的相应属性之间定义数据绑定(单向,UI更新ViewModel,Mode=OneWayToSource)

问题是当我按键盘上的Enter键时,我提供的最后一个输入字段的值无法推送到ViewModel 的底层属性。

我怀疑这与输入字段在窗口关闭之前没有失去焦点有关(因此所有绑定都被“解除绑定”)。 作为比较,如果我“点击”“保存”按钮(而不是通过按键处理它的Click事件),则该值将更新为该属性。 此外,如果我(可怕!可怕!)向按钮的Click事件添加事件处理程序,并在代码后台调用button.Focus(),一切都有效!

有什么解决方法吗?

显然,我不想处理任何窗口关闭事件,并“手动”获取缺少的值...这将违反整个MVVM概念:-(

有更好的建议吗?

谢谢, Alex


我在许多其他编程语言中也遇到了相同的行为,特别是Delphi(我认为Hejlsberg认为这是一个功能而不是缺陷 - 如果他也负责WPF的话)。如果您绑定到数字,则PropertyChanged选项会出现问题,因为它将尝试在键入Pi时验证“3.”并引发异常。我不介意button.Focus()选项,因为它适用于所有控件,并且还可以为对话框提供未来支持。 - simo.3792
4个回答

17

默认情况下,TextBox 只有在失去焦点时才会通知其源代码已更改其值。您可以通过在绑定中设置 UpdateSourceTrigger=PropertyChanged 来更改此行为。这将使 TextBox 在其文本更改时向其源发送更新通知,而不仅仅是在失去焦点时。

如果您不希望每次按键时都发送更新通知,可以创建一个 AttachedProperty,以便在按下 Enter 键时更新源。

以下是我在类似情况下使用的 AttachedProperty:

// When set to True, Enter Key will update Source
#region EnterUpdatesTextSource DependencyProperty

// Property to determine if the Enter key should update the source. Default is False
public static readonly DependencyProperty EnterUpdatesTextSourceProperty =
    DependencyProperty.RegisterAttached("EnterUpdatesTextSource", typeof (bool),
                                        typeof (TextBoxHelper),
                                        new PropertyMetadata(false, EnterUpdatesTextSourcePropertyChanged));

// Get
public static bool GetEnterUpdatesTextSource(DependencyObject obj)
{
    return (bool) obj.GetValue(EnterUpdatesTextSourceProperty);
}

// Set
public static void SetEnterUpdatesTextSource(DependencyObject obj, bool value)
{
    obj.SetValue(EnterUpdatesTextSourceProperty, value);
}

// Changed Event - Attach PreviewKeyDown handler
private static void EnterUpdatesTextSourcePropertyChanged(DependencyObject obj,
                                                          DependencyPropertyChangedEventArgs e)
{
    var sender = obj as UIElement;
    if (obj != null)
    {
        if ((bool) e.NewValue)
        {
            sender.PreviewKeyDown += OnPreviewKeyDownUpdateSourceIfEnter;
        }
        else
        {
            sender.PreviewKeyDown -= OnPreviewKeyDownUpdateSourceIfEnter;
        }
    }
}

// If key being pressed is the Enter key, and EnterUpdatesTextSource is set to true, then update source for Text property
private static void OnPreviewKeyDownUpdateSourceIfEnter(object sender, KeyEventArgs e)
{
    if (e.Key == Key.Enter)
    {
        if (GetEnterUpdatesTextSource((DependencyObject) sender))
        {
            var obj = sender as UIElement;
            BindingExpression textBinding = BindingOperations.GetBindingExpression(
                obj, TextBox.TextProperty);

            if (textBinding != null)
                textBinding.UpdateSource();
        }
    }
}

#endregion //EnterUpdatesTextSource DependencyProperty

2
要使用上面附加的属性,您需要将其放入静态类中(例如 public class static EnterUpdatesTextSourceBehaviour)。然后在 XAML 中,您需要在开头添加包含此类的程序集引用(例如 xmlns:myHelpers="clr-namespace:MyHelpers),然后在 <TextBox> 元素中添加设置为 true 的附加属性(例如 <TextBox myHelpers:EnterUpdatesTextSourceBehaviour.EnterUpdatesTextSource="True">)。 - ErrCode

4
尝试这个。
{Binding Property,UpdateSourceTrigger = PropertyChanged,Mode = OneWayToSource}

0
一个简单的技巧是将焦点从控件上移开,例如移到按钮本身。您可以在按钮的 Click 处理程序中执行此操作,因为它将在绑定的命令之前执行:
    public MyView()
    {
        InitializeComponent();

        _button.Click += delegate
        {
            _button.Focus();
        };
    }

如果你不喜欢在视图中编写后端代码,你也可以将其作为行为添加到按钮中完成。


-2

这只是一个 WPF 错误。(我不知道是设计还是实现问题。)失去焦点时 TextBox 更新在大多数情况下是期望的行为,对话框中默认按钮也应该起作用。

至于解决方法,我认为调整 TextBox 是错误的方向。更好的解决方案是强制将焦点设置到默认按钮上。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接