UI not update when ItemsSource change in MVVM

So my app has a main window displaying a list of recipes, and a second window (opened from main window) adding new recipe to the list (with information). Both windows have the same view model. After testing the new recipe is successfully added to the list, however the UI did not update new recipe. I also have a delete button in the main window for each recipe and it works normally. The deleted recipe is deleted in the view model and the UI. My code is quite long. I hope that it’s readable. Here is my code (there are lots of other UI so I just show only the relevant code) :

Main window view

<!--Button to open second window-->
<Button Command="{Binding OpenAddRecipeWindowCommand}" Name="AddRecipeButton" Grid.Column="0" Margin="5" Padding="5" FontSize="15" Content="Add new recipe"/>

<!--The list-->
<ListView Name="RecipeList" Grid.Row="2" Margin="5" ItemsSource="{Binding RecipeModels}">
            <!-- Set the style for item container to stretch to full width-->
            <ListView.ItemContainerStyle>
                <Style TargetType="ListViewItem">
                    <Setter Property="HorizontalContentAlignment" Value="Stretch"/>
                </Style>
            </ListView.ItemContainerStyle>

            <!-- Template of each item in the ListView -->
            <ListView.ItemTemplate>
                <DataTemplate>
                    <Grid>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition></ColumnDefinition>
                            <ColumnDefinition Width="Auto"></ColumnDefinition>
                        </Grid.ColumnDefinitions>

                        <TextBlock Name="RecipeName" Grid.Column="0" FontSize="15" Margin="5" VerticalAlignment="Center" Text="{Binding Name}"/>
                        <Button Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}},
                                            Path=DataContext.DeleteRecipeCommand}"
                                CommandParameter="{Binding}"
                                Name="Delete" Grid.Column="1" FontSize="15" Margin="5" Padding="5" HorizontalAlignment="Right" Content="Delete"/>
                    </Grid>
                </DataTemplate>
            </ListView.ItemTemplate>
            
        </ListView>

Second window view

<DockPanel Margin="5">
        <!-- Recipe information -->
        <StackPanel DockPanel.Dock="Top">
            <!-- Name, amount, price input-->
            <Grid>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="Auto"></ColumnDefinition>
                    <ColumnDefinition Width="2*"></ColumnDefinition>
                    <ColumnDefinition Width="Auto"></ColumnDefinition>
                    <ColumnDefinition></ColumnDefinition>
                    <ColumnDefinition Width="Auto"></ColumnDefinition>
                    <ColumnDefinition></ColumnDefinition>
                </Grid.ColumnDefinitions>

                <TextBlock Grid.Column="0" Margin="5 5 0 5" Padding="5" FontSize="15" VerticalAlignment="Center" Text="Tên"/>
                <TextBox Grid.Column="1" Margin="0 5 5 5" Padding="2" VerticalAlignment="Center" Text="{Binding NewRecipe.Name}" FontSize="13"/>

                <TextBlock Grid.Column="2" Margin="5 5 0 5" Padding="5" FontSize="15" VerticalAlignment="Center" Text="Số lượng"/>
                <TextBox Grid.Column="3" Margin="0 5 5 5" Padding="2" VerticalAlignment="Center" Text="{Binding NewRecipe.Amount}" FontSize="13" MaxLength="9" PreviewTextInput="NumberValidationTextBox" />

                <TextBlock Grid.Column="4" Margin="5 5 0 5" Padding="5" FontSize="15" VerticalAlignment="Center" Text="Giá thành"/>
                <TextBox Grid.Column="5" Margin="0 5 5 5" Padding="2" VerticalAlignment="Center" Text="{Binding NewRecipe.Price}" FontSize="13" MaxLength="9" PreviewTextInput="NumberValidationTextBox" />
            </Grid>
            
            <!-- Checkbox and add ingredient button -->
            <Grid>
                <Grid.RowDefinitions>
                    <RowDefinition></RowDefinition>
                    <RowDefinition></RowDefinition>
                </Grid.RowDefinitions>
                
                <CheckBox Grid.Row="0" Margin="10 5 10 5" VerticalContentAlignment="Center" VerticalAlignment="Center" FontSize="15" Content="Use as ingredient"/>
                <Button Grid.Row="1" Margin="10 5 10 5" Padding="5" FontSize="15" HorizontalAlignment="Left" Command="{Binding AddIngredientCommand}"  Content="Add new ingredient"/>
            </Grid>
        </StackPanel>

        <!-- ListView and buttons -->
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition></RowDefinition>
                <RowDefinition Height="Auto"></RowDefinition>
            </Grid.RowDefinitions>

            <ListView Name="IngredientList" ItemsSource="{Binding Ingredients}" Grid.Row="0" Margin="5" >
                <ListView.ItemContainerStyle>
                    <Style TargetType="ListViewItem">
                        <Setter Property="HorizontalContentAlignment" Value="Stretch"/>
                    </Style>
                </ListView.ItemContainerStyle>

                <!-- ListViewItem template -->
                <ListView.ItemTemplate>
                    <DataTemplate>
                        <Grid>
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="Auto"></ColumnDefinition>
                                <ColumnDefinition Width="2*"></ColumnDefinition>
                                <ColumnDefinition Width="Auto"></ColumnDefinition>
                                <ColumnDefinition></ColumnDefinition>
                                <ColumnDefinition Width="Auto"></ColumnDefinition>
                                <ColumnDefinition></ColumnDefinition>
                                <ColumnDefinition Width="Auto"></ColumnDefinition>
                                <ColumnDefinition></ColumnDefinition>
                                <ColumnDefinition Width="Auto"></ColumnDefinition>
                            </Grid.ColumnDefinitions>

                            <TextBlock Grid.Column="0" Margin="5 5 0 5" Padding="5" FontSize="13" Text="Name"/>
                            <TextBox Grid.Column="1" Margin="0 5 5 5" Padding="5" MaxLength="37" FontSize="13"/>

                            <TextBlock Grid.Column="2" Margin="5 5 0 5" Padding="5" FontSize="13" Text="Amount"/>
                            <TextBox Grid.Column="3" Margin="0 5 5 5" Padding="5" MaxLength="9" FontSize="13"/>

                            <TextBlock Grid.Column="4" Margin="5 5 0 5" Padding="5" FontSize="13" Text="Unit"/>
                            <TextBox Grid.Column="5" Margin="0 5 5 5" Padding="5" MaxLength="38" FontSize="13"/>

                            <TextBlock Grid.Column="6" Margin="5 5 0 5" Padding="5" FontSize="13" Text="Price"/>
                            <TextBox Grid.Column="7" Margin="0 5 5 5" Padding="5" MaxLength="9" FontSize="13"/>

                            <Button Grid.Column="8" Name="DeleteItemButton" Margin="5" Padding="5"
                                    Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}},
                                            Path=DataContext.DeleteIngredientCommand}"
                                    CommandParameter="{Binding}"
                                    FontSize="13" Content="Delete"/>
                        </Grid>
                    </DataTemplate>
                </ListView.ItemTemplate>
            </ListView>
            
            <!-- Buttons -->
            <DockPanel Grid.Row="1">
                <Button DockPanel.Dock="Right" Name="SaveButton" Margin="5" Padding="20 5 20 5" Command="{Binding SaveRecipeCommand}" FontSize="15" Content="Save"/>
                <Button DockPanel.Dock="Left" Name="CancelButton" Margin="5" Padding="20 5 20 5" HorizontalAlignment="Left" Command="{Binding CancelCommand}" FontSize="15" Content="Cancel"/>
            </DockPanel>
        </Grid>
    </DockPanel>

View Model

    public class MainWindowViewModel : INotifyPropertyChanged
    {
        /// <summary>
        /// Implementation for Main window UI. 
        /// </summary>
        #region Main Window View Model
        // List of recipes which is the ItemsSource for ListViewItem
        #region ObservableCollection<RecipeModel> recipeModels

        private ObservableCollection<RecipeModel> recipeModels;

        public ObservableCollection<RecipeModel> RecipeModels
        {
            get
            {
                return recipeModels;
            }

            set
            {
                if (recipeModels != value)
                {
                    recipeModels = value;
                    RaisePropertyChanged(nameof(recipeModels));
                }
            }
        }
        #endregion

        #region Buttons
        // Add Recipe Window Button
        public RelayCommand OpenAddRecipeWindowCommand { get; set; }

        private void ExecuteOpenAddRecipeWindowCommand()
        {
            RecipeWindow newWindow = new RecipeWindow();
            newWindow.ShowDialog();
        }

        // Add Ingredient Window Button 
        public RelayCommand OpenAddIngredientWindowCommand { get; set; }

        private void ExecuteOpenAddIngredientWindowCommand()
        {
            RecipeWindow newWindow = new RecipeWindow();
            newWindow.ShowDialog();
        }

        // Product Calculation Window Button 
        public RelayCommand OpenProductCalculationWindowCommand { get; set; }

        private void ExecuteOpenProductCalculationWindowCommand()
        {
            RecipeWindow newWindow = new RecipeWindow();
            newWindow.ShowDialog();
        }

        // Product Calculation Window Button 
        public RelayCommand OpenIngredientCalculationWindowCommand { get; set; }

        private void ExecuteOpenIngredientCalculationWindowCommand()
        {
            RecipeWindow newWindow = new RecipeWindow();
            newWindow.ShowDialog();
        }

        // Delete Recipe Command
        public RelayCommand<object> DeleteRecipeCommand { get; set; }

        private void ExecuteDeleteRecipeCommand(object recipe)
        {
            RecipeModels.Remove((RecipeModel)recipe);
            Save();
        }

        #endregion

        #endregion

        /*------------------------------------------------------------------------------------------------------------------------------------*/
        /*------------------------------------------------------------------------------------------------------------------------------------*/
        /// <summary>
        /// Implementation for Recipe window UI
        /// </summary>
        #region Recipe Window View Model
        // List of ingredients which is ItemsSource for ListView
        #region ObservableCollection<RecipeModel> ingredients

        private ObservableCollection<IngredientModel> ingredients = new ObservableCollection<IngredientModel>();

        public ObservableCollection<IngredientModel> Ingredients
        {
            get
            {
                return ingredients;
            }

            set
            {
                if (ingredients != value)
                {
                    ingredients = value;
                    RaisePropertyChanged(nameof(ingredients));
                }
            }
        }
        #endregion

        // The recipe to be newly created, or edited.
        #region RecipeModel newRecipe
        private RecipeModel newRecipe = new RecipeModel();

        public RecipeModel NewRecipe
        {
            get
            {
                return newRecipe;
            }

            set
            {
                if (newRecipe != value)
                {
                    newRecipe = value;
                }
                RaisePropertyChanged(nameof(newRecipe));
            }
        }
        #endregion

        #region Buttons
        // Cancel command
        public RelayCommand CancelCommand { get; set; }

        public Action CloseWindow { get; set; }

        public void ExecuteCancelCommand()
        {
            CloseWindow();
        }

        // Add ingredient command
        public RelayCommand AddIngredientCommand { get; set; }

        public void ExecuteAddIngredientCommand()
        {
            Ingredients.Add(new IngredientModel());
        }

        // Delete ingredient command
        public RelayCommand<object> DeleteIngredientCommand { get; set; }

        public void ExecuteDeleteIngredientCommand(object ingredient)
        {
            Ingredients.Remove((IngredientModel)ingredient);
        }

/* THIS IS THE PART WHERE I TRY TO SAVE THE NEW RECIPE TO THE LIST BUT UI DOES NOT CHANGE */
        // Save command
        public RelayCommand SaveRecipeCommand { get; set; }

        public void ExecuteSaveRecipeCommand()
        {
            NewRecipe.ingredients = Ingredients;

            RecipeModels.Add(NewRecipe);

        /* I TESTED WITH A MESSAGE BOX TO SHOW THAT THE NEW RECIPE IS ADDED */
            string res = "";
            foreach (var item in RecipeModels)
            {
                res += item.Name + " ";
            }

            MessageBox.Show(res);
            CloseWindow();
        }

        #endregion

        #endregion

        /*------------------------------------------------------------------------------------------------------------------------------------*/
        /*------------------------------------------------------------------------------------------------------------------------------------*/
        /// <summary>
        /// Mutual Section
        /// </summary>
        #region Constructor
        public MainWindowViewModel(Action action)
        {
            // Load data from file
            Load();

            CloseWindow = action;

            #region Main Window buttons
            OpenAddRecipeWindowCommand = new RelayCommand(ExecuteOpenAddRecipeWindowCommand);

            OpenAddIngredientWindowCommand = new RelayCommand(ExecuteOpenAddIngredientWindowCommand);

            OpenProductCalculationWindowCommand = new RelayCommand(ExecuteOpenProductCalculationWindowCommand);

            OpenIngredientCalculationWindowCommand = new RelayCommand(ExecuteOpenIngredientCalculationWindowCommand);

            DeleteRecipeCommand = new RelayCommand<object>(ExecuteDeleteRecipeCommand);
            #endregion

            #region Add Ingredient Window buttons
            CancelCommand = new RelayCommand(ExecuteCancelCommand);

            SaveRecipeCommand = new RelayCommand(ExecuteSaveRecipeCommand);

            AddIngredientCommand = new RelayCommand(ExecuteAddIngredientCommand);

            DeleteIngredientCommand = new RelayCommand<object>(ExecuteDeleteIngredientCommand);
            #endregion
        }

        #endregion

        #region Property Change Notification
        public event PropertyChangedEventHandler PropertyChanged;

        private void RaisePropertyChanged(string property)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
        }

        #endregion

EDIT: Sorry I forget to include the Code behind. So this is how I set the DataContext of the windows:

Main Window

public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            DataContext = new MainWindowViewModel(null);
        }
    }

Second Window

public partial class RecipeWindow : Window
    {
        public RecipeWindow()
        {
            InitializeComponent();
            this.DataContext = new MainWindowViewModel(this.Close);
        }
    }

Maybe I’ve created two instance of the view model to each window. From here how can I fix it?

Answer

Both windows have the same view model.

I doubt they do. I think they have their own instance of the same class.

You could try to set the DataContext of the window when you open it:

private void ExecuteOpenAddRecipeWindowCommand()
{
    RecipeWindow newWindow = new RecipeWindow();
    newWindow.DataContext = this;
    newWindow.ShowDialog();
}

Then both windows should have the same DataContext.

Also note that directly creating windows in a view model effectively breaks the MVVM design pattern.