我正在尝试使用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();
}
}
答案 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>