操作WPF TextBox失去焦点事件和绑定的顺序

4
我有一个WPF 窗口作为数据编辑器。 WindowDataContext是一个Observable对象(实现了INotifyPropertyChanged)。
Observable对象的每个属性都绑定到Window中适当的小部件上(数字类型用TextBox,布尔值用CheckBox等)。
我不想有“OK”和“Cancel”按钮来保存更改的属性;我希望在小部件失去焦点时将底层数据保存到Observable对象中。
为此,我处理了小部件的PreviewLostKeyboardFocusLostFocus事件。我的撤销框架需要Observable对象的当前状态副本和新状态副本。 PreviewLostKeyboardFocus 事件会创建对象的副本,而LostFocus 事件则实际执行保存到数据库的操作。
但我的问题是:字段上的绑定直到LostFocus事件运行之后才实际更新基础Observable对象。这样做所产生的影响是,从“A”更改文本框的文本到“B”会将该字段中的数据保存为“A”。连续更改从“A”到“B”再到“C”会将“B”保存下来。
我在事件处理程序和基础对象的setter中设置了断点。果然,PreviewLostKeyboardFocus先运行,然后是LostFocus,最后是Observable对象的setter。
我想尽可能地使其通用化,因此虽然我可以告诉Observable对象使用KeyboardFocusChangedEventArgs更新自己的属性,但我需要为每个字段单独设置事件处理程序,而且可能会有很多这样的字段。
有没有办法让绑定在PreviewLostKeyboardFocusLostFocus事件之间运行?
<TextBox Text="{Binding ObjectProperty, StringFormat='{}{0:F5}'}" LostFocus="PersistentTextBox_LostFocus" PreviewLostKeyboardFocus="PersistentTextBox_PreviewLostKeyboardFocus" />

后台代码中的事件处理程序:

private void PersistentTextBox_LostFocus(object sender, RoutedEventArgs e) {
    this.ObservableObject.PersistChanges(this.tempObservableObject);
}

private void PersistentTextBox_PreviewLostKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e) {
    this.tempObservableObject = this.ObservableObject;
}

ObservableObject class (setter):

public class ObservableObject : INotifyPropertyChanged {
    ...
    public Single ObjectProperty {
        get {
            return this._objectProperty;
        }
        set {
            this._objectProperty = value;
            RaisePropertyChanged("ObjectProperty");
        }
    }
}

谢谢!

1个回答

6
如果我理解你的问题正确,这可能是因为默认情况下 TextBox 会在失去焦点时更新 Source。解决此问题的一种方法是将 TextBoxUpdateSourceTrigger 设置为 PropertyChanged
<TextBox Text="{Binding ObjectProperty, UpdateSourceTrigger=PropertyChanged, StringFormat='{}{0:F5}'}" 

这将允许在TextBox中进行更改时立即更新基础可观察对象,然后您可以在小部件失去焦点事件中保留更改。

或者,您可以将UpdateSourceTrigger设置为Explicit并在LostFocus处理程序中更新源。

 <TextBox Text="{Binding ObjectProperty, UpdateSourceTrigger=Explicit, StringFormat='{}{0:F5}'}" 

    private void TextBox_LostFocus(object sender, RoutedEventArgs e)
    {
       (sender as TextBox).GetBindingExpression(TextBox.TextProperty).UpdateSource();
    }

我在研究中发现了这个。但是我认为修改每个单独的字符会导致事件触发。这将导致向我的数据库写入大量潜在的开销,并且也会使我的撤消堆栈过于细粒度化。 - James Cronen
哦,我以为当小部件失去焦点时你正在更新数据库。 - sa_ddam213
哦...等一下。我明白你的意思了。是的,你说得对。让我试试看。 - James Cronen
2
你可以将UpdateSourceTrigger设置为Explicit,并在失去焦点事件处理程序中更新属性。 - sa_ddam213
我认为UpdateSourceTrigger=PropertyChanged对我的目的已经足够好了。我不喜欢它在每次按键时都会持续更新基础对象,但实际上我想不到任何理由来避免这种情况。这是用户思考的时间,因此性能真的不应该成为问题。谢谢! - James Cronen

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