WPF Close Event Handler Not Working After Dependancy Inject Was Added

madaxe2020

Well-known member
Joined
Sep 7, 2020
Messages
50
Programming Experience
5-10
Doe anybody Know why my close event handler is no longer working after adding dependency injection

thanks

Madaxe

StartUp:
namespace CATIAAppsSideWidget
{
    /// <summary>
    /// Interaction logic for App.xaml
    /// </summary>
    public partial class App : Application
    {
        private MainWindow _mainwindow;
        private ILogger _logger;

        private void OnStartup(object sender, StartupEventArgs e)
        {
            IConfigurationBuilder builder = new ConfigurationBuilder();
            BuildConfig(builder);

            _logger = new LoggerConfiguration()
                .ReadFrom.Configuration(builder.Build())
                .Enrich.FromLogContext()
                .CreateLogger();

            _logger.Information("Application Starting");
            _logger.Information("Adding Dependancies");
            var host = Host.CreateDefaultBuilder()
                .ConfigureServices((context, services) =>
                {
                    services.AddSingleton(_logger);
                    services.AddTransient<ContextWindowMenu>();
                    services.AddTransient<Window, MainWindow>();             
                    services.AddTransient<CATIAV6Connection>();
                    services.AddTransient<ISQLiteHelper,SQLiteHelper>();
                    services.AddTransient<ICATIAAppsSideWidgetContextFactory,CATIAAppsSideWidgetContextFactory>();
                    services.AddTransient<ICATIAAppProvider,CATIAAppProvider>();
                    services.AddTransient<ICATIAAppCollectionModel,CATIAAppCollectionModel>();
                    services.AddTransient<ICATIAAppsSideWidgetModel,CATIAAppsSideWidgetModel>();
                    services.AddTransient<ICATIAAppsSideWidgetViewModel,CATIAAppsSideWidgetViewModel>();         
                })
                .UseSerilog()
                .Build();

            if (ActivatorUtilities.CreateInstance<CATIAV6Connection>(host.Services).Connected == true)
            {
                _logger.Information("Creating the Main UI.");
                _mainwindow = ActivatorUtilities.CreateInstance<MainWindow>(host.Services);

                _logger.Information("Setting the UI DataContext.");
                _mainwindow.DataContext = ActivatorUtilities.CreateInstance<CATIAAppsSideWidgetViewModel>(host.Services);

                _logger.Information("Adding Close Event Handler.");
                _mainwindow.Closed += new EventHandler(MainWidget_Closed);

                _logger.Information("Showing UI.");
                _mainwindow.Show();
            }
            else
            {
                MessageBox.Show("Failled to Connect to an Active CATIA Session, Please Ensure CATIA is Running.", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }

        public void MainWidget_Closed(object sender, EventArgs e)
        {
            _logger.Information("Main UI Closing.");
            _mainwindow.Closed -= new EventHandler(MainWidget_Closed);
            _mainwindow = null;
        }


        private static void BuildConfig(IConfigurationBuilder builder)
        {
            builder.SetBasePath(Directory.GetCurrentDirectory())
                .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
                .AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? "Production"}.json", optional: true)
                .AddEnvironmentVariables();
        }
    }
}
 
Solution
I found a work around that required minimum change to the existing code

Line 18,34 fixed the problem:
private void OnStartup(object sender, StartupEventArgs e)
        {
            IConfigurationBuilder builder = new ConfigurationBuilder();
            BuildConfig(builder);

            _logger = new LoggerConfiguration()
                .ReadFrom.Configuration(builder.Build())
                .Enrich.FromLogContext()
                .CreateLogger();

            _logger.Information("App - OnStartup - Application Starting");
            _logger.Information("App - OnStartup - Adding Dependancies");

            _host = Host.CreateDefaultBuilder()
                .ConfigureServices((context, services) =>
                {...
I would suggest going for the minimal repro case instead of having all your other classes lumped in. It will make trying to debug what is going on much easier.
 
The following seems to work for me:
MainWindow.xaml.cs:
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System;
using System.Windows;

namespace WpfCoreApp
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
            => InitializeComponent();

        private void Close_Click(object sender, RoutedEventArgs e)
            => this.Close();

        [STAThread]
        public static void Main()
        {
            var host = Host.CreateDefaultBuilder()
                           .ConfigureServices(services =>
                           {
                               services.AddTransient<Application>();
                               services.AddTransient<MainWindow>();
                           })
                           .Build();

            var app = ActivatorUtilities.CreateInstance<Application>(host.Services);
            var window = ActivatorUtilities.CreateInstance<MainWindow>(host.Services);
            window.Closed += Window_Closed;
            app.Run(window);
        }

        private static void Window_Closed(object? sender, EventArgs e)
            => MessageBox.Show("Closed");
    }
}

MainWindow.xaml:
<Window x:Class="WpfCoreApp.MainWindow"
        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:WpfCoreApp"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Button Click="Close_Click">Close</Button>
</Window>

(I deleted the app.xaml and app.xaml.cs files.)
 
I found a work around that required minimum change to the existing code

Line 18,34 fixed the problem:
private void OnStartup(object sender, StartupEventArgs e)
        {
            IConfigurationBuilder builder = new ConfigurationBuilder();
            BuildConfig(builder);

            _logger = new LoggerConfiguration()
                .ReadFrom.Configuration(builder.Build())
                .Enrich.FromLogContext()
                .CreateLogger();

            _logger.Information("App - OnStartup - Application Starting");
            _logger.Information("App - OnStartup - Adding Dependancies");

            _host = Host.CreateDefaultBuilder()
                .ConfigureServices((context, services) =>
                {
                    services.AddSingleton(_logger);
                    services.AddSingleton(m => (Window)new MainWindow());
                    services.AddSingleton<ContextWindowMenu>();          
                    services.AddSingleton<CATIAV6Connection>();
                    services.AddSingleton<ISQLiteHelper,SQLiteHelper>();
                    services.AddSingleton<ICATIAAppsSideWidgetContextFactory,CATIAAppsSideWidgetContextFactory>();
                    services.AddSingleton<ICATIAAppProvider,CATIAAppProvider>();
                    services.AddSingleton<ICATIAAppCollectionModel,CATIAAppCollectionModel>();
                    services.AddSingleton<ICATIAAppsSideWidgetModel,CATIAAppsSideWidgetModel>();
                    services.AddSingleton<ICATIAAppsSideWidgetViewModel, CATIAAppsSideWidgetViewModel>();
                })
                .UseSerilog()
                .Build();

            if (_host.Services.GetRequiredService<CATIAV6Connection>().Connected ==true)
            {
                _logger.Information("App - OnStartup - Creating the Main UI.");
                _mainwindow = (MainWindow)_host.Services.GetRequiredService<Window>();
               
                _logger.Information("App - OnStartup - Setting the UI DataContext.");
                _mainwindow.DataContext = ActivatorUtilities.CreateInstance<CATIAAppsSideWidgetViewModel>(_host.Services);

                _logger.Information("App - OnStartup - Showing UI.");
                _mainwindow.Show();
            }
            else
            {
                _logger.Warning("App - OnStartup - Failled to Connect to an Active CATIA Session, Please Ensure CATIA is Running.");
                MessageBox.Show("Failled to Connect to an Active CATIA Session, Please Ensure CATIA is Running.", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }
 
Solution
That workaround suggests that your MainWindow is a dependency elsewhere and so you are forced to register the class as a singleton to only have one instance, so that you can monitor it when closes. It would be better to clean up your dependency chain and/or lifetimes to be more rational rather than depending on the registering the main window as a singleton. As people have learned over the years, using singletons is an anti-pattern.
 
Back
Top Bottom