程序关闭时手动运行UpdateSource会导致异常

时间:2016-07-04 09:07:01

标签: c# wpf xaml mvvm datagrid

我的应用程序在关闭时需要保存数据,所以我实现了:

private void oclmEditor_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
    e.Cancel = false;

    OCLMEditorViewModel vm = this.DataContext as OCLMEditorViewModel;
    if (vm.SaveModelDataCommand.CanExecute(null))
        vm.SaveModelDataCommand.Execute(null);
}
我认为一切都很顺利。但后来我意识到,虽然在关机时保存,但它实际上并非更新源控件首先。这真让我感到惊讶,因为它们被设置为LostFocus,显然如果它关闭,一切都会失去焦点。

我决定我需要找到一种方法来轻松获取所有控件来更新源代码。根据我的研究,我实施了这些扩展:

public static class Helper
{
    public static IEnumerable<DependencyObject> EnumerateVisualChildren(this DependencyObject dependencyObject)
    {
        for (int i = 0; i < VisualTreeHelper.GetChildrenCount(dependencyObject); i++)
        {
            yield return VisualTreeHelper.GetChild(dependencyObject, i);
        }
    }


    public static IEnumerable<DependencyObject> EnumerateVisualDescendents(this DependencyObject dependencyObject)
    {
        yield return dependencyObject;

        foreach (DependencyObject child in dependencyObject.EnumerateVisualChildren())
        {
            foreach (DependencyObject descendent in child.EnumerateVisualDescendents())
            {
                yield return descendent;
            }
        }
    }

    public static void UpdateBindingSources(this DependencyObject dependencyObject)
    {
        foreach (DependencyObject element in dependencyObject.EnumerateVisualDescendents())
        {
            LocalValueEnumerator localValueEnumerator = element.GetLocalValueEnumerator();
            while (localValueEnumerator.MoveNext())
            {
                BindingExpressionBase bindingExpression = BindingOperations.GetBindingExpressionBase(element, localValueEnumerator.Current.Property);
                if (bindingExpression != null)
                {
                    bindingExpression.UpdateSource();
                }
            }
        }
    }
}

老实说,我不知道给这个班级最好的名字是什么,也不知道将它存放在应用程序中的位置(我使用了mainwindow.xaml.cs)。

我离题了......所以,我随后更改了关机代码:

private void oclmEditor_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
    e.Cancel = false;

    oclmEditor.UpdateBindingSources();
    OCLMEditorViewModel vm = this.DataContext as OCLMEditorViewModel;
    if (vm.SaveModelDataCommand.CanExecute(null))
        vm.SaveModelDataCommand.Execute(null);
}

原则上它有效。现在,源代码会在保存之前进行更新。太好了!

但我注意到在输出窗口中:

  

System.Windows.Data错误:8:无法将目标值保存到源。 BindingExpression:路径= SelectedStudentItem;

     

DataItem ='OCLMEditorViewModel'(HashCode = 32322646); target元素是'DataGrid'(Name ='gridStudents');

     

target属性是'SelectedItem'(类型'Object')NullReferenceException:'System.NullReferenceException:对象引用未设置为对象的实例。      位于D:\ My Programs \ OCLMEditor \ OCLMEditor \ ViewModels \ OCLMEditorViewModel.cs中的OCLMEditor.OCLMEditorViewModel.set_SelectedStudentItem(学生值):第151行

DataGrid绑定到CollectionViewSourceSelectedItem绑定到ViewModel中的属性。我做了更多测试,不得不最终做:

private Student _SelectedStudentItem;
public Student SelectedStudentItem
{
    get
    {
        return _SelectedStudentItem;
    }
    set
    {
        // We need to remove this item from the previous student history
        if (_SelectedStudentItem != null)
            _SelectedStudentItem.History.Remove(Meeting.DateMeeting);

        _SelectedStudentItem = value;
        if (_SelectedStudentItem == null)
            return;

        _EditStudentButtonClickCommand.RaiseCanExecuteChanged();
        _DeleteStudentButtonClickCommand.RaiseCanExecuteChanged();
        OnPropertyChanged("SelectedStudentItem");

        if (ActiveStudentAssignmentType == StudentAssignmentType.BibleReadingMain)
        {
            _Meeting.TFGW.BibleReadingItem.Main.Name = _SelectedStudentItem.Name;
            OnPropertyChanged("BibleReadingMain");
        }
        else if (ActiveStudentAssignmentType == StudentAssignmentType.BibleReadingClass1)
        {
            _Meeting.TFGW.BibleReadingItem.Class1.Name = _SelectedStudentItem.Name;
            OnPropertyChanged("BibleReadingClass1");
        }
        else if (ActiveStudentAssignmentType == StudentAssignmentType.BibleReadingClass2)
        {
            _Meeting.TFGW.BibleReadingItem.Class2.Name = _SelectedStudentItem.Name;
            OnPropertyChanged("BibleReadingClass2");
        }
    }
}

我不得不把那个测试放在那里并提前返回。现在异常没有发生。

我是否正确地假设没有其他办法来处理这个问题?我尝试过的一件事是将我的SelectedStudentItem更改为直接使用我的集合视图CurrentItemMoveCurrentTo方法。但是当用户点击网格行时,我失去了使该变量保持最新的能力。这就是为什么我以前一种方式(绑定到SelectedItem)。

很抱歉,如果这个问题听起来很冗长或复杂。我不知道如何传达它。

0 个答案:

没有答案