使用MVVM模式关闭打开的窗口会产生System.NullReferenceException错误

时间:2019-06-27 17:23:57

标签: c# wpf mvvm

我正在尝试使用WPF C#学习MVVM模式。而且在将信息保存到sqlite数据库后尝试关闭打开的窗口时遇到错误。当提出保存新联系人的命令时,我在Ha⚓Contact(this,new EventArgs())上收到错误消息。

错误:System.NullReferenceException:'对象引用未设置为对象的实例。'

我的ViewModel:

Configuration

SaveNewContactCommand:

Startup

NewContactWindow.Xaml.Cs后面的代码:

public class NewContactViewModel : BaseViewModel
    {
        private ContactViewModel _contact;

        public ContactViewModel Contact
        {
            get { return _contact; }
            set { SetValue(ref _contact, value); }
        }

        public SaveNewContactCommand SaveNewContactCommand { get; set; }

        public event EventHandler HasAddedContact;

        public NewContactViewModel()
        {
            SaveNewContactCommand = new SaveNewContactCommand(this);
            _contact = new ContactViewModel();
        }

        public void SaveNewContact()
        {
            var newContact = new Contact()
            {
                Name = Contact.Name,
                Email = Contact.Email,
                Phone = Contact.Phone
            };

            DatabaseConnection.Insert(newContact);
            HasAddedContact(this, new EventArgs());
        }
    }

在调用新窗口的地方添加其他代码:

    public class SaveNewContactCommand : ICommand
    {
        public NewContactViewModel VM { get; set; }

        public SaveNewContactCommand(NewContactViewModel vm)
        {
            VM = vm;
        }

        public event EventHandler CanExecuteChanged;

        public bool CanExecute(object parameter)
        {
            return true;
        }

        public void Execute(object parameter)
        {
            VM.SaveNewContact();
        }
    }

ContactsWindow.Xaml

public partial class NewContactWindow : Window
    {
        NewContactViewModel _viewModel;

        public NewContactWindow()
        {
            InitializeComponent();

            _viewModel = new NewContactViewModel();
            DataContext = _viewModel;
            _viewModel.HasAddedContact += Vm_ContactAdded;
        }

        private void Vm_ContactAdded(object sender, EventArgs e)
        {
            this.Close();
        }
    }

NewContactWindow.Xaml

public class ContactsViewModel
    {
        public ObservableCollection<IContact> Contacts { get; set; } = new ObservableCollection<IContact>();
        public NewContactCommand NewContactCommand { get; set; }

        public ContactsViewModel()
        {
            NewContactCommand = new NewContactCommand(this);

            GetContacts();
        }

        public void GetContacts()
        {
            using(var conn = new SQLite.SQLiteConnection(DatabaseConnection.dbFile))
            {
                conn.CreateTable<Contact>();
                var contacts = conn.Table<Contact>().ToList();

                Contacts.Clear();
                foreach (var contact in contacts)
                {
                    Contacts.Add(contact);
                }
            }
        }

        public void CreateNewContact()
        {
            var newContactWindow = new NewContactWindow();
            newContactWindow.ShowDialog();

            GetContacts();
        }
    }

1 个答案:

答案 0 :(得分:1)

您正在构造函数中创建NewContactWindow的viewmodel,将其正确分配给DataContext,并向该事件正确添加处理程序。不幸的是,您还在资源中创建了同一视图模型的第二个实例,并手动将所有绑定的Source属性设置为在资源中使用一个实例,而资源中没有事件处理程序。

您在构造函数中设置的

Window.DataContext是Window XAML中任何绑定的默认源。只要让它做它的事。我还从绑定到Mode=TwoWay删除了所有多余的TextBox.Text东西,因为定义了该属性,因此默认情况下,该属性上的所有绑定都是TwoWay。我认为UpdateSourceTrigger=PropertyChanged并没有做任何必要或有帮助的事情:这会导致绑定在每次按下键时(而不是仅在TextBox失去焦点时)更新viewmodel属性。但是我认为您不会在需要的属性上做任何事情;没有任何验证。但是TextBox.Text 是实际使用的少数几个地方之一,因此我将其保留了下来。

您应该在另一个窗口中删除类似的viewmodel资源。它没有任何危害,但充其量是没有用的。最糟糕的是,这是一个令人讨厌的麻烦。午夜用火杀死它,将骨灰埋在寂寞的十字路口下。

<Window x:Class="Contacts_App.View.NewContactWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:Contacts_App.View"
        xmlns:vm="clr-namespace:Contacts_App.ViewModel"
        mc:Ignorable="d"
        Title="New Contact Window" Height="250" Width="350">

    <Grid>
        <StackPanel 
            Margin="10">
            <Label Content="Name" />
            <TextBox 
                Text="{Binding Contact.Name, UpdateSourceTrigger=PropertyChanged}"
                Margin="0,0,0,5"/>
            <Label Content="Email" />
            <TextBox 
                Text="{Binding Contact.Email, UpdateSourceTrigger=PropertyChanged}"
                Margin="0,0,0,5"/>
            <Label Content="Phone Number" />
            <TextBox 
                Text="{Binding Contact.Phone, UpdateSourceTrigger=PropertyChanged}"
                Margin="0,0,0,5"/>
            <Button 
                Content="Save"
                Command="{Binding SaveNewContactCommand}"/>
        </StackPanel>
    </Grid>
</Window>