与UpdateSourceTrigger==LostFocus的绑定不会触发菜单或工具栏交互



我注意到,当用户激活菜单或工具栏时,UpdateSourceTrigger==LostFocus的绑定不会更新。

这会导致一个不幸的情况,当用户从菜单或工具栏中选择"保存文件"时,用户所做的最后更改会丢失。

是否有一种简单的方法来解决这个问题,或者我是否必须将所有绑定更改为UpdateSourceTrigger=PropertyChanged

我知道这有点老了,但是对于任何未来的读者来说,简单地在我的ToolBar上设置以下内容对我有用:

FocusManager.IsFocusScope="False"

问题是,当菜单项被激活时,TextBox实际上并没有失去焦点。因此,UpdateSourceTrigger LostFocus不会触发。根据您的(视图)模型,UpdateSourceTrigger PropertyChanged可能是或可能不是一个可行的解决方案。

对我来说,PropertyChanged不是一个选项(我需要在用户完成输入后验证数据,而不是在两者之间),所以我使用了一个解决方法,在"保存文件"(或任何其他需要最新模型的菜单/工具栏条目)之前调用此方法:

Public Shared Sub SaveFocusedTextBox()
    Dim focusedTextBox = TryCast(Keyboard.FocusedElement, TextBox)
    If focusedTextBox IsNot Nothing Then
        Dim be = focusedTextBox.GetBindingExpression(TextBox.TextProperty)
        If be IsNot Nothing Then be.UpdateSource()
    End If
End Sub

在这个相关的问题中可以找到其他一些解决这个问题的方法:

  • 保存前的WPF数据绑定

(事实上,这个方法的功劳要归功于rudigrobler对这个问题的回答)

这个很适合我:

Private Sub MenuItem_Click(sender As System.Object, e As System.Windows.RoutedEventArgs)
  Keyboard.FocusedElement.RaiseEvent(New RoutedEventArgs With {.RoutedEvent = LostFocusEvent})
End Sub

虽然这里有一些有用的答案,但我认为没有一个是最好的方法。对我来说,最好和次好的选择是:

  1. 修复选项卡顺序,以便在选项卡顺序的TextBox之前和之后,与TextBox相同的焦点范围内存在可对焦元素。我个人认为这是最好的选择,但是只有当合理的用户界面选择时才应该使用它。也就是说,已经有一些UI元素自然地围绕着TextBox
  2. 处理LostKeyboardFocus事件,然后显式更新绑定。请注意,虽然TextBox在焦点范围方面没有失去焦点,但如果您退出它,它确实总是失去键盘焦点。

第二个选项看起来像这样:

<TextBox Text="{Binding SomeProperty}" LostKeyboardFocus="TextBox_LostKeyboardFocus"/>
private void TextBox_LostKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
{
    BindingOperations.GetBindingExpression((DependencyObject)sender, TextBox.TextProperty)?.UpdateSource();
}
精致的

:

可接受的答案延迟更新绑定源属性,直到某些特定场景(例如执行"Save")命令)发生已知需要属性值的情况。不幸的是,这否定了MVVM范例的一个关键好处,即当本身发生时,不需要担心,因为绑定引擎应该处理所有事情。

投票最高的答案稍微改善了这一点,但正如下面的评论所指出的,它仍然是一个特定于场景的解决方案。它需要应用于UI中具有自己焦点作用域的每个元素。但更糟糕的是,它实际上修改了一个元素的UI行为,而这个元素与我们真正关心的元素完全无关。良好的编码实践意味着修复最初的问题,而不是应用一些不相关的更改,而这些更改恰好产生了对我们有利的副作用。

如果可以简单地在UI中安排选项卡顺序,这样就有一个元素在TextBox的相同焦点范围内,紧跟着TextBox,那么我认为这将是理想的解决方案。这意味着UI可预测地为用户工作,并且代码与尽可能简单的实现保持一致。

但这并不总是可能的。在某些情况下,自然制表符顺序要求在TextBox之前或之后紧跟着MenuToolBar或作为其自身焦点作用域的其他元素。在这种情况下,对我来说,最直接的方法就是简单地修改"焦点丢失"。通过显式地处理LostKeyboardFocus事件并在该事件发生时更新绑定源,

我改编了@Heinzi的c#解决方案,并将其与文本框上的IsKeyboardFocusWithinChanged事件相结合。我一直很紧张,直到这句话挽救了我的生命。

XAML:

<TextBox
    IsKeyboardFocusWithinChanged="UIElement_OnIsKeyboardFocusWithinChanged"        
  ...
  />

和后面的代码:

private TextBox focusedTextBox;
private void UIElement_OnIsKeyboardFocusWithinChanged(object sender, DependencyPropertyChangedEventArgs e)
{
    if( e.NewValue is bool && (bool)e.NewValue == true )
        focusedTextBox = sender as TextBox;
}
public void SaveFocusedTextBox()
{
    var be = focusedTextBox?.GetBindingExpression(TextBox.TextProperty);
    be?.UpdateSource();
}

最新更新