Закрытие открытого окна с использованием шаблона MVVM приводит к ошибке System.NullReferenceException - PullRequest
0 голосов
/ 27 июня 2019

Я пытаюсь изучить шаблон MVVM с помощью WPF C #.И я сталкиваюсь с ошибкой при попытке закрыть открытое окно после сохранения информации в базе данных sqlite.Когда вызывается команда сохранения нового контакта, я получаю сообщение об ошибке в HasAddedContact (this, new EventArgs ());

Ошибка: System.NullReferenceException: 'Ссылка на объект не установлена ​​для экземпляра объекта. '

Моя ViewModel:

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());
        }
    }

SaveNewContactCommand:

    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();
        }
    }

NewContactWindow.Xaml.Cs код позади:

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();
        }
    }

Добавление дополнительныхкод, по которому я вызываю новое окно:

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();
        }
    }

ContactsWindow.Xaml

<Window x:Class="Contacts_App.View.ContactsWindow"
        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="Contacts Window" Height="320" Width="400">

    <Window.Resources>
        <vm:ContactsViewModel x:Key="vm"/>
    </Window.Resources>

    <StackPanel Margin="10">
        <Button 
            Content="New Contact"
            Command="{Binding NewContactCommand}"/>
        <TextBox Margin="0,5,0,5"/>
        <ListView
            Height="200"
            Margin="0,5,0,0"
            ItemsSource="{Binding Contacts}">
            <ListView.ItemTemplate>
                <DataTemplate>
                    <Label Content="{Binding Name}" />
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
    </StackPanel>

</Window>

NewContactWindow.Xaml

<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">

    <Window.Resources>
        <vm:NewContactViewModel x:Key="vm"/>
    </Window.Resources>

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

1 Ответ

1 голос
/ 27 июня 2019

Вы создаете модель представления NewContactWindow в конструкторе, правильно назначаете ее для DataContext и правильно добавляете обработчик для этого события.К сожалению, вы также создаете второй экземпляр той же модели представления в ресурсах и вручную устанавливаете свойство Source всех привязок, чтобы использовать его в ресурсах, у которого нет обработчика событий.

Window.DataContext, который вы задаете в конструкторе, является источником по умолчанию для любой привязки в XAML-окне.Просто позволь этому делать свое дело.Я также удалил все лишние Mode=TwoWay вещи из Привязок к TextBox.Text, так как это свойство определено так, чтобы все привязки на нем были по умолчанию TwoWay.Я не думаю, что UpdateSourceTrigger=PropertyChanged делает что-то необходимое или полезное: это приводит к тому, что Binding обновляет свойство viewmodel при каждом нажатии клавиши, а не только когда TextBox теряет фокус.Но я не думаю, что вы делаете что-то со свойствами, где это будет иметь значение;Там нет проверки или что-нибудь.Но 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>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...