diff --git a/3d-viewer/dialogs/panel_preview_3d_model.cpp b/3d-viewer/dialogs/panel_preview_3d_model.cpp
index 82055706a9..5bfec8d492 100644
--- a/3d-viewer/dialogs/panel_preview_3d_model.cpp
+++ b/3d-viewer/dialogs/panel_preview_3d_model.cpp
@@ -4,7 +4,7 @@
  * Copyright (C) 2016 Mario Luzeiro <mrluzeiro@ua.pt>
  * Copyright (C) 2015 Cirilo Bernardo <cirilo.bernardo@gmail.com>
  * Copyright (C) 2017 Jean-Pierre Charras, jp.charras at wanadoo.fr
- * Copyright (C) 2015-2022 KiCad Developers, see AUTHORS.txt for contributors.
+ * Copyright (C) 2015-2023 KiCad Developers, see AUTHORS.txt for contributors.
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
@@ -144,7 +144,8 @@ PANEL_PREVIEW_3D_MODEL::PANEL_PREVIEW_3D_MODEL( wxWindow* aParent, PCB_BASE_FRAM
                  this );
     }
 
-    aFrame->Connect( UNITS_CHANGED, wxCommandEventHandler( PANEL_PREVIEW_3D_MODEL::onUnitsChanged ),
+    aFrame->Connect( EDA_EVT_UNITS_CHANGED,
+                     wxCommandEventHandler( PANEL_PREVIEW_3D_MODEL::onUnitsChanged ),
                      nullptr, this );
 
 #ifdef __WXOSX__
@@ -451,7 +452,8 @@ void PANEL_PREVIEW_3D_MODEL::doIncrementOffset( wxSpinEvent& event, double aSign
 
     double step_mm = OFFSET_INCREMENT_MM;
     double curr_value_mm =
-            EDA_UNIT_UTILS::UI::DoubleValueFromString( pcbIUScale, m_userUnits, textCtrl->GetValue() )
+            EDA_UNIT_UTILS::UI::DoubleValueFromString( pcbIUScale, m_userUnits,
+                                                       textCtrl->GetValue() )
             / pcbIUScale.IU_PER_MM;
 
     if( m_userUnits == EDA_UNITS::MILS || m_userUnits == EDA_UNITS::INCHES )
diff --git a/common/dialogs/dialog_book_reporter.cpp b/common/dialogs/dialog_book_reporter.cpp
index db65c6b441..82c5322bfa 100644
--- a/common/dialogs/dialog_book_reporter.cpp
+++ b/common/dialogs/dialog_book_reporter.cpp
@@ -26,22 +26,17 @@
 #include <wx/wxhtml.h>
 
 
-DIALOG_BOOK_REPORTER::DIALOG_BOOK_REPORTER( KIWAY_PLAYER* aParent, const wxString& aDialogName,
-                                            const wxString& aDialogTitle ) :
-        DIALOG_BOOK_REPORTER_BASE( aParent ),
-        m_frame( aParent )
+wxDEFINE_EVENT( EDA_EVT_CLOSE_DIALOG_BOOK_REPORTER, wxCommandEvent );
+
+
+DIALOG_BOOK_REPORTER::DIALOG_BOOK_REPORTER( KIWAY_PLAYER* aParent, const wxString& aName,
+                                            const wxString& aTitle ) :
+    DIALOG_BOOK_REPORTER_BASE( aParent, wxID_ANY, aTitle ),
+    m_frame( aParent )
 {
-    SetName( aDialogName );
-    SetTitle( aDialogTitle );
-
-    aParent->Bind( wxEVT_CLOSE_WINDOW,
-                   [this]( wxCloseEvent& aEvent )
-                   {
-                       Close();
-                       aEvent.Skip();
-                   } );
-
+    SetName( aName );
     SetupStandardButtons();
+    finishDialogSettings();
 }
 
 
@@ -96,3 +91,21 @@ int DIALOG_BOOK_REPORTER::GetPageCount() const
     return m_notebook->GetPageCount();
 }
 
+
+void DIALOG_BOOK_REPORTER::OnClose( wxCloseEvent& aEvent )
+{
+    // Dialog is mode-less so let the parent know that it needs to be destroyed.
+    if( !IsModal() && !IsQuasiModal() )
+    {
+        wxCommandEvent* evt = new wxCommandEvent( EDA_EVT_CLOSE_DIALOG_BOOK_REPORTER, wxID_ANY );
+
+        evt->SetEventObject( this );
+        evt->SetString( GetName() );
+        wxWindow* parent = GetParent();
+
+        if( parent )
+            wxQueueEvent( parent, evt );
+    }
+
+    aEvent.Skip();
+}
diff --git a/common/dialogs/dialog_book_reporter_base.cpp b/common/dialogs/dialog_book_reporter_base.cpp
index e3dcaa58ed..61145f52b8 100644
--- a/common/dialogs/dialog_book_reporter_base.cpp
+++ b/common/dialogs/dialog_book_reporter_base.cpp
@@ -37,12 +37,12 @@ DIALOG_BOOK_REPORTER_BASE::DIALOG_BOOK_REPORTER_BASE( wxWindow* parent, wxWindow
 	this->Centre( wxBOTH );
 
 	// Connect Events
-	m_sdbSizerOK->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_BOOK_REPORTER_BASE::OnOK ), NULL, this );
+	this->Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( DIALOG_BOOK_REPORTER_BASE::OnClose ) );
 }
 
 DIALOG_BOOK_REPORTER_BASE::~DIALOG_BOOK_REPORTER_BASE()
 {
 	// Disconnect Events
-	m_sdbSizerOK->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_BOOK_REPORTER_BASE::OnOK ), NULL, this );
+	this->Disconnect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( DIALOG_BOOK_REPORTER_BASE::OnClose ) );
 
 }
diff --git a/common/dialogs/dialog_book_reporter_base.fbp b/common/dialogs/dialog_book_reporter_base.fbp
index 3ba1437cbe..0520c8bb7f 100644
--- a/common/dialogs/dialog_book_reporter_base.fbp
+++ b/common/dialogs/dialog_book_reporter_base.fbp
@@ -56,6 +56,7 @@
             <property name="window_extra_style"></property>
             <property name="window_name"></property>
             <property name="window_style"></property>
+            <event name="OnClose">OnClose</event>
             <object class="wxBoxSizer" expanded="1">
                 <property name="minimum_size"></property>
                 <property name="name">bMainSizer</property>
@@ -136,7 +137,6 @@
                         <property name="minimum_size"></property>
                         <property name="name">m_sdbSizer</property>
                         <property name="permission">protected</property>
-                        <event name="OnOKButtonClick">OnOK</event>
                     </object>
                 </object>
             </object>
diff --git a/common/dialogs/dialog_book_reporter_base.h b/common/dialogs/dialog_book_reporter_base.h
index 53dbd31d76..36d38fbc6a 100644
--- a/common/dialogs/dialog_book_reporter_base.h
+++ b/common/dialogs/dialog_book_reporter_base.h
@@ -37,7 +37,7 @@ class DIALOG_BOOK_REPORTER_BASE : public DIALOG_SHIM
 		wxButton* m_sdbSizerOK;
 
 		// Virtual event handlers, override them in your derived class
-		virtual void OnOK( wxCommandEvent& event ) { event.Skip(); }
+		virtual void OnClose( wxCloseEvent& event ) { event.Skip(); }
 
 
 	public:
diff --git a/common/dialogs/panel_setup_netclasses.cpp b/common/dialogs/panel_setup_netclasses.cpp
index d82ab7640e..d58e50d494 100644
--- a/common/dialogs/panel_setup_netclasses.cpp
+++ b/common/dialogs/panel_setup_netclasses.cpp
@@ -218,7 +218,7 @@ PANEL_SETUP_NETCLASSES::PANEL_SETUP_NETCLASSES( PAGED_DIALOG* aParent, EDA_DRAW_
                                                    &PANEL_SETUP_NETCLASSES::OnNetclassGridMouseEvent,
                                                    this );
 
-    m_frame->Bind( UNITS_CHANGED, &PANEL_SETUP_NETCLASSES::onUnitsChanged, this );
+    m_frame->Bind( EDA_EVT_UNITS_CHANGED, &PANEL_SETUP_NETCLASSES::onUnitsChanged, this );
 
     m_netclassGrid->EndBatch();
     m_assignmentGrid->EndBatch();
@@ -243,7 +243,7 @@ PANEL_SETUP_NETCLASSES::~PANEL_SETUP_NETCLASSES()
                                 wxGridEventHandler( PANEL_SETUP_NETCLASSES::OnNetclassGridCellChanging ),
                                 nullptr, this );
 
-    m_frame->Unbind( UNITS_CHANGED, &PANEL_SETUP_NETCLASSES::onUnitsChanged, this );
+    m_frame->Unbind( EDA_EVT_UNITS_CHANGED, &PANEL_SETUP_NETCLASSES::onUnitsChanged, this );
 }
 
 
@@ -287,8 +287,8 @@ bool PANEL_SETUP_NETCLASSES::TransferDataToWindow()
                 if( lineStyleIdx >= (int) g_lineStyleNames.size() )
                     lineStyleIdx = 0;
 
-                m_netclassGrid->SetCellValue( aRow, GRID_LINESTYLE, g_lineStyleNames[ lineStyleIdx ] );
-
+                m_netclassGrid->SetCellValue( aRow, GRID_LINESTYLE,
+                                              g_lineStyleNames[ lineStyleIdx ] );
                 m_netclassGrid->SetUnitValue( aRow, GRID_CLEARANCE, nc->GetClearance() );
                 m_netclassGrid->SetUnitValue( aRow, GRID_TRACKSIZE, nc->GetTrackWidth() );
                 m_netclassGrid->SetUnitValue( aRow, GRID_VIASIZE, nc->GetViaDiameter() );
@@ -331,9 +331,6 @@ bool PANEL_SETUP_NETCLASSES::TransferDataToWindow()
 }
 
 
-/*
- * Populates drop-downs with the list of net classes
- */
 void PANEL_SETUP_NETCLASSES::rebuildNetclassDropdowns()
 {
     m_assignmentGrid->CommitPendingChanges( true );
diff --git a/common/eda_base_frame.cpp b/common/eda_base_frame.cpp
index 1fd1d4c9bf..0602a8947a 100644
--- a/common/eda_base_frame.cpp
+++ b/common/eda_base_frame.cpp
@@ -3,7 +3,7 @@
  *
  * Copyright (C) 2017 Jean-Pierre Charras, jp.charras at wanadoo.fr
  * Copyright (C) 2013 Wayne Stambaugh <stambaughw@gmail.com>
- * Copyright (C) 1992-2022 KiCad Developers, see AUTHORS.txt for contributors.
+ * Copyright (C) 1992-2023 KiCad Developers, see AUTHORS.txt for contributors.
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
@@ -67,7 +67,7 @@
 #include <functional>
 #include <kiface_ids.h>
 
-wxDEFINE_EVENT( UNITS_CHANGED, wxCommandEvent );
+wxDEFINE_EVENT( EDA_EVT_UNITS_CHANGED, wxCommandEvent );
 
 
 // Minimum window size
@@ -664,7 +664,8 @@ void EDA_BASE_FRAME::ensureWindowIsOnScreen()
     wxSize    size       = GetWindowSize();
 
     wxLogTrace( traceDisplayLocation,
-                wxS( "ensureWindowIsOnScreen: clientArea (%d, %d) w %d h %d" ), clientSize.x, clientSize.y,
+                wxS( "ensureWindowIsOnScreen: clientArea (%d, %d) w %d h %d" ),
+                clientSize.x, clientSize.y,
                 clientSize.width, clientSize.height );
 
     if( pos.y < clientSize.y )
@@ -678,8 +679,8 @@ void EDA_BASE_FRAME::ensureWindowIsOnScreen()
     if( pos.x < clientSize.x )
     {
         wxLogTrace( traceDisplayLocation,
-                    wxS( "ensureWindowIsOnScreen: x pos %d is off the client rect, setting to %d" ), pos.x,
-                    clientSize.x );
+                    wxS( "ensureWindowIsOnScreen: x pos %d is off the client rect, setting to %d" ),
+                    pos.x, clientSize.x );
         pos.x = clientSize.x;
     }
 
@@ -687,8 +688,8 @@ void EDA_BASE_FRAME::ensureWindowIsOnScreen()
     {
         int newWidth = clientSize.width - ( pos.x - clientSize.x );
         wxLogTrace( traceDisplayLocation,
-                    wxS( "ensureWindowIsOnScreen: effective width %d above available %d, setting to %d" ),
-                    pos.x + size.x, clientSize.width, newWidth );
+                    wxS( "ensureWindowIsOnScreen: effective width %d above available %d, setting "
+                         "to %d" ), pos.x + size.x, clientSize.width, newWidth );
         size.x = newWidth;
     }
 
@@ -696,8 +697,8 @@ void EDA_BASE_FRAME::ensureWindowIsOnScreen()
     {
         int newHeight = clientSize.height - ( pos.y - clientSize.y );
         wxLogTrace( traceDisplayLocation,
-                    wxS( "ensureWindowIsOnScreen: effective height %d above available %d, setting to %d" ),
-                    pos.y + size.y, clientSize.height, newHeight );
+                    wxS( "ensureWindowIsOnScreen: effective height %d above available %d, setting "
+                         "to %d" ), pos.y + size.y, clientSize.height, newHeight );
         size.y = newHeight;
     }
 
@@ -1354,7 +1355,8 @@ void EDA_BASE_FRAME::ChangeUserUnits( EDA_UNITS aUnits )
     SetUserUnits( aUnits );
     unitsChangeRefresh();
 
-    wxCommandEvent e( UNITS_CHANGED );
+    wxCommandEvent e( EDA_EVT_UNITS_CHANGED );
+    e.SetInt( static_cast<int>( aUnits ) );
     e.SetClientData( this );
     ProcessEventLocally( e );
 }
diff --git a/common/eda_draw_frame.cpp b/common/eda_draw_frame.cpp
index 9912e5f048..67499d6cf7 100644
--- a/common/eda_draw_frame.cpp
+++ b/common/eda_draw_frame.cpp
@@ -284,7 +284,7 @@ void EDA_DRAW_FRAME::ToggleUserUnits()
                                                           : EDA_UNITS::INCHES );
         unitsChangeRefresh();
 
-        wxCommandEvent e( UNITS_CHANGED );
+        wxCommandEvent e( EDA_EVT_UNITS_CHANGED );
         ProcessEventLocally( e );
     }
 }
diff --git a/common/widgets/unit_binder.cpp b/common/widgets/unit_binder.cpp
index 176ad6e90e..e0808c2017 100644
--- a/common/widgets/unit_binder.cpp
+++ b/common/widgets/unit_binder.cpp
@@ -2,7 +2,7 @@
  * This program source code file is part of KiCad, a free EDA CAD application.
  *
  * Copyright (C) 2014-2015 CERN
- * Copyright (C) 2020-2022 KiCad Developers, see AUTHORS.txt for contributors.
+ * Copyright (C) 2020-2023 KiCad Developers, see AUTHORS.txt for contributors.
  * Author: Maciej Suminski <maciej.suminski@cern.ch>
  *
  * This program is free software; you can redistribute it and/or
@@ -101,7 +101,8 @@ UNIT_BINDER::UNIT_BINDER( UNITS_PROVIDER* aUnitsProvider, wxWindow* aEventSource
 
     if( m_eventSource )
     {
-        m_eventSource->Connect( UNITS_CHANGED, wxCommandEventHandler( UNIT_BINDER::onUnitsChanged ),
+        m_eventSource->Connect( EDA_EVT_UNITS_CHANGED,
+                                wxCommandEventHandler( UNIT_BINDER::onUnitsChanged ),
                                 nullptr, this );
     }
 }
@@ -117,7 +118,8 @@ UNIT_BINDER::~UNIT_BINDER()
 
     if( m_eventSource )
     {
-        m_eventSource->Disconnect( UNITS_CHANGED, wxCommandEventHandler( UNIT_BINDER::onUnitsChanged ),
+        m_eventSource->Disconnect( EDA_EVT_UNITS_CHANGED,
+                                   wxCommandEventHandler( UNIT_BINDER::onUnitsChanged ),
                                    nullptr, this );
     }
 
@@ -126,9 +128,9 @@ UNIT_BINDER::~UNIT_BINDER()
         m_valueCtrl->Disconnect( wxEVT_SET_FOCUS, wxFocusEventHandler( UNIT_BINDER::onSetFocus ),
                               nullptr, this );
         m_valueCtrl->Disconnect( wxEVT_KILL_FOCUS, wxFocusEventHandler( UNIT_BINDER::onKillFocus ),
-                              nullptr, this );
+                                 nullptr, this );
         m_valueCtrl->Disconnect( wxEVT_LEFT_UP, wxMouseEventHandler( UNIT_BINDER::onClick ),
-                              nullptr, this );
+                                 nullptr, this );
     }
 }
 
@@ -318,7 +320,8 @@ bool UNIT_BINDER::Validate( double aMin, double aMax, EDA_UNITS aUnits )
         m_errorMessage = wxString::Format( _( "%s must be at least %s." ),
                                            valueDescriptionFromLabel( m_label ),
                                            EDA_UNIT_UTILS::UI::StringFromValue( *m_iuScale, m_units,
-                                                                                val_min_iu,  true ) );
+                                                                                val_min_iu,
+                                                                                true ) );
 
         textEntry->SelectAll();
 
@@ -334,7 +337,8 @@ bool UNIT_BINDER::Validate( double aMin, double aMax, EDA_UNITS aUnits )
         m_errorMessage = wxString::Format( _( "%s must be less than %s." ),
                                            valueDescriptionFromLabel( m_label ),
                                            EDA_UNIT_UTILS::UI::StringFromValue( *m_iuScale, m_units,
-                                                                                val_max_iu, true ) );
+                                                                                val_max_iu,
+                                                                                true ) );
 
         textEntry->SelectAll();
 
@@ -395,7 +399,7 @@ void UNIT_BINDER::SetValue( const wxString& aValue )
 
         value += EDA_UNIT_UTILS::GetLabel( m_units, m_dataType );
     }
- 
+
     if( textEntry )
         textEntry->SetValue( value );
     else if( staticText )
diff --git a/eeschema/dialogs/dialog_erc.cpp b/eeschema/dialogs/dialog_erc.cpp
index 7a24b1e966..c8c0f0a532 100644
--- a/eeschema/dialogs/dialog_erc.cpp
+++ b/eeschema/dialogs/dialog_erc.cpp
@@ -49,6 +49,10 @@
 #include <string_utils.h>
 #include <kiplatform/ui.h>
 
+
+wxDEFINE_EVENT( EDA_EVT_CLOSE_ERC_DIALOG, wxCommandEvent );
+
+
 // wxWidgets spends *far* too long calcuating column widths (most of it, believe it or
 // not, in repeatedly creating/destroying a wxDC to do the measurement in).
 // Use default column widths instead.
@@ -177,8 +181,6 @@ void DIALOG_ERC::UpdateAnnotationWarning()
 }
 
 
-// PROGRESS_REPORTER calls
-
 bool DIALOG_ERC::updateUI()
 {
     // If ERC checks ever get slow enough we'll want a progress indicator...
@@ -285,8 +287,6 @@ void DIALOG_ERC::OnDeleteOneClick( wxCommandEvent& aEvent )
 }
 
 
-/* Delete the old ERC markers, over the whole hierarchy
- */
 void DIALOG_ERC::OnDeleteAllClick( wxCommandEvent& event )
 {
     bool includeExclusions = false;
@@ -318,7 +318,6 @@ void DIALOG_ERC::OnDeleteAllClick( wxCommandEvent& event )
 }
 
 
-// This is a modeless dialog so we have to handle these ourselves.
 void DIALOG_ERC::OnCancelClick( wxCommandEvent& aEvent )
 {
     if( m_running )
@@ -329,15 +328,26 @@ void DIALOG_ERC::OnCancelClick( wxCommandEvent& aEvent )
 
     m_parent->FocusOnItem( nullptr );
 
-    Close();
+    aEvent.Skip();
 }
 
 
-void DIALOG_ERC::OnCloseErcDialog( wxCloseEvent& event )
+void DIALOG_ERC::OnCloseErcDialog( wxCloseEvent& aEvent )
 {
     m_parent->FocusOnItem( nullptr );
 
-    m_parent->GetToolManager()->GetTool<EE_INSPECTION_TOOL>()->DestroyERCDialog();
+    // Dialog is mode-less so let the parent know that it needs to be destroyed.
+    if( !IsModal() && !IsQuasiModal() )
+    {
+        wxCommandEvent* evt = new wxCommandEvent( EDA_EVT_CLOSE_ERC_DIALOG, wxID_ANY );
+
+        wxWindow* parent = GetParent();
+
+        if( parent )
+            wxQueueEvent( parent, evt );
+    }
+
+    aEvent.Skip();
 }
 
 
@@ -511,8 +521,8 @@ void DIALOG_ERC::testErc()
 
     // Test pins on each net against the pin connection table
     if( settings.IsTestEnabled( ERCE_PIN_TO_PIN_ERROR )
-            || settings.IsTestEnabled( ERCE_POWERPIN_NOT_DRIVEN )
-            || settings.IsTestEnabled( ERCE_PIN_NOT_DRIVEN ) )
+      || settings.IsTestEnabled( ERCE_POWERPIN_NOT_DRIVEN )
+      || settings.IsTestEnabled( ERCE_PIN_NOT_DRIVEN ) )
     {
          tester.TestPinToPin();
     }
@@ -678,7 +688,7 @@ void DIALOG_ERC::OnERCItemRClick( wxDataViewEvent& aEvent )
     menu.AppendSeparator();
 
     if( rcItem->GetErrorCode() == ERCE_PIN_TO_PIN_WARNING
-        || rcItem->GetErrorCode() == ERCE_PIN_TO_PIN_ERROR )
+      || rcItem->GetErrorCode() == ERCE_PIN_TO_PIN_ERROR )
     {
         // Pin to pin severities edited through pin conflict map
     }
diff --git a/eeschema/dialogs/dialog_erc.h b/eeschema/dialogs/dialog_erc.h
index 0482f73300..a0c56eb143 100644
--- a/eeschema/dialogs/dialog_erc.h
+++ b/eeschema/dialogs/dialog_erc.h
@@ -34,6 +34,12 @@
 #include <erc_settings.h>
 
 
+/**
+ * Event sent to parent when dialog is mode-less.
+ */
+wxDECLARE_EVENT( EDA_EVT_CLOSE_ERC_DIALOG, wxCommandEvent );
+
+
 #define DIALOG_ERC_WINDOW_NAME "DialogErcWindowName"
 
 
diff --git a/eeschema/dialogs/dialog_lib_edit_pin_table.cpp b/eeschema/dialogs/dialog_lib_edit_pin_table.cpp
index 1f262fa878..df89f772c5 100644
--- a/eeschema/dialogs/dialog_lib_edit_pin_table.cpp
+++ b/eeschema/dialogs/dialog_lib_edit_pin_table.cpp
@@ -91,12 +91,12 @@ public:
     {
         m_eval = std::make_unique<NUMERIC_EVALUATOR>( m_frame->GetUserUnits() );
 
-        m_frame->Bind( UNITS_CHANGED, &PIN_TABLE_DATA_MODEL::onUnitsChanged, this );
+        m_frame->Bind( EDA_EVT_UNITS_CHANGED, &PIN_TABLE_DATA_MODEL::onUnitsChanged, this );
     }
 
     ~PIN_TABLE_DATA_MODEL()
     {
-        m_frame->Unbind( UNITS_CHANGED, &PIN_TABLE_DATA_MODEL::onUnitsChanged, this );
+        m_frame->Unbind( EDA_EVT_UNITS_CHANGED, &PIN_TABLE_DATA_MODEL::onUnitsChanged, this );
     }
 
     void onUnitsChanged( wxCommandEvent& aEvent )
diff --git a/eeschema/fields_grid_table.cpp b/eeschema/fields_grid_table.cpp
index 5e67968115..5376f2a3dc 100644
--- a/eeschema/fields_grid_table.cpp
+++ b/eeschema/fields_grid_table.cpp
@@ -293,7 +293,7 @@ void FIELDS_GRID_TABLE<T>::initGrid( WX_GRID* aGrid )
 
     m_eval = std::make_unique<NUMERIC_EVALUATOR>( m_frame->GetUserUnits() );
 
-    m_frame->Bind( UNITS_CHANGED, &FIELDS_GRID_TABLE<T>::onUnitsChanged, this );
+    m_frame->Bind( EDA_EVT_UNITS_CHANGED, &FIELDS_GRID_TABLE<T>::onUnitsChanged, this );
 }
 
 
@@ -316,7 +316,7 @@ FIELDS_GRID_TABLE<T>::~FIELDS_GRID_TABLE()
     m_fontAttr->DecRef();
     m_colorAttr->DecRef();
 
-    m_frame->Unbind( UNITS_CHANGED, &FIELDS_GRID_TABLE<T>::onUnitsChanged, this );
+    m_frame->Unbind( EDA_EVT_UNITS_CHANGED, &FIELDS_GRID_TABLE<T>::onUnitsChanged, this );
 }
 
 
diff --git a/eeschema/sch_edit_frame.cpp b/eeschema/sch_edit_frame.cpp
index 85356fbcbe..386621c04b 100644
--- a/eeschema/sch_edit_frame.cpp
+++ b/eeschema/sch_edit_frame.cpp
@@ -27,7 +27,9 @@
 #include <symbol_library.h>
 #include <confirm.h>
 #include <connection_graph.h>
+#include <dialogs/dialog_erc.h>
 #include <dialogs/dialog_schematic_find.h>
+#include <dialogs/dialog_book_reporter.h>
 #include <eeschema_id.h>
 #include <executable_names.h>
 #include <gestfich.h>
@@ -90,6 +92,9 @@
 #include <drawing_sheet/ds_proxy_view_item.h>
 
 
+#define DIFF_SYMBOLS_DIALOG_NAME wxT( "DiffSymbolsDialog" )
+
+
 BEGIN_EVENT_TABLE( SCH_EDIT_FRAME, SCH_BASE_FRAME )
     EVT_SOCKET( ID_EDA_SOCKET_EVENT_SERV, EDA_DRAW_FRAME::OnSockRequestServer )
     EVT_SOCKET( ID_EDA_SOCKET_EVENT, EDA_DRAW_FRAME::OnSockRequest )
@@ -115,7 +120,9 @@ END_EVENT_TABLE()
 SCH_EDIT_FRAME::SCH_EDIT_FRAME( KIWAY* aKiway, wxWindow* aParent ) :
         SCH_BASE_FRAME( aKiway, aParent, FRAME_SCH, wxT( "Eeschema" ), wxDefaultPosition,
                         wxDefaultSize, KICAD_DEFAULT_DRAWFRAME_STYLE, SCH_EDIT_FRAME_NAME ),
-        m_highlightedConn( nullptr )
+    m_highlightedConn( nullptr ),
+    m_ercDialog( nullptr ),
+    m_diffSymbolDialog( nullptr )
 {
     m_maximizeByDefault = true;
     m_schematic = new SCHEMATIC( nullptr );
@@ -286,6 +293,9 @@ SCH_EDIT_FRAME::SCH_EDIT_FRAME( KIWAY* aKiway, wxWindow* aParent ) :
     // top-left corner of the canvas
     wxPoint canvas_pos = GetCanvas()->GetScreenPosition();
     hierarchy_pane.FloatingPosition( canvas_pos.x + 10, canvas_pos.y + 10 );
+
+    Bind( EDA_EVT_CLOSE_DIALOG_BOOK_REPORTER, &SCH_EDIT_FRAME::onCloseSymbolDiffDialog, this );
+    Bind( EDA_EVT_CLOSE_ERC_DIALOG, &SCH_EDIT_FRAME::onCloseErcDialog, this );
 }
 
 
@@ -294,23 +304,10 @@ SCH_EDIT_FRAME::~SCH_EDIT_FRAME()
     m_hierarchy->Disconnect( wxEVT_SIZE,
                              wxSizeEventHandler( SCH_EDIT_FRAME::OnResizeHierarchyNavigator ),
                              NULL, this );
+
     // Ensure m_canvasType is up to date, to save it in config
     m_canvasType = GetCanvas()->GetBackend();
 
-    // Close modeless dialogs
-    wxWindow* open_dlg = wxWindow::FindWindowByName( DIALOG_ERC_WINDOW_NAME );
-
-    if( open_dlg )
-        open_dlg->Close( true );
-
-    // Shutdown all running tools
-    if( m_toolManager )
-    {
-        m_toolManager->ShutdownAllTools();
-        delete m_toolManager;
-        m_toolManager = nullptr;
-    }
-
     SetScreen( nullptr );
 
     delete m_schematic;
@@ -439,9 +436,12 @@ void SCH_EDIT_FRAME::setupUIConditions()
     mgr->SetConditions( ACTIONS::inchesUnits,         CHECK( cond.Units( EDA_UNITS::INCHES ) ) );
     mgr->SetConditions( ACTIONS::milsUnits,           CHECK( cond.Units( EDA_UNITS::MILS ) ) );
 
-    mgr->SetConditions( EE_ACTIONS::lineModeFree,     CHECK( cond.LineMode( LINE_MODE::LINE_MODE_FREE ) ) );
-    mgr->SetConditions( EE_ACTIONS::lineMode90,       CHECK( cond.LineMode( LINE_MODE::LINE_MODE_90 ) ) );
-    mgr->SetConditions( EE_ACTIONS::lineMode45,       CHECK( cond.LineMode( LINE_MODE::LINE_MODE_45 ) ) );
+    mgr->SetConditions( EE_ACTIONS::lineModeFree,
+                        CHECK( cond.LineMode( LINE_MODE::LINE_MODE_FREE ) ) );
+    mgr->SetConditions( EE_ACTIONS::lineMode90,
+                        CHECK( cond.LineMode( LINE_MODE::LINE_MODE_90 ) ) );
+    mgr->SetConditions( EE_ACTIONS::lineMode45,
+                        CHECK( cond.LineMode( LINE_MODE::LINE_MODE_45 ) ) );
 
     mgr->SetConditions( ACTIONS::cut,                 ENABLE( hasElements ) );
     mgr->SetConditions( ACTIONS::copy,                ENABLE( hasElements ) );
@@ -822,13 +822,6 @@ bool SCH_EDIT_FRAME::canCloseWindow( wxCloseEvent& aEvent )
         }
     }
 
-    // Close modeless dialogs.  They're trouble when they get destroyed after the frame and/or
-    // board.
-    wxWindow* open_dlg = wxWindow::FindWindowByName( DIALOG_ERC_WINDOW_NAME );
-
-    if( open_dlg )
-        open_dlg->Close( true );
-
     return true;
 }
 
@@ -841,6 +834,15 @@ void SCH_EDIT_FRAME::doCloseWindow()
     if( m_toolManager )
         m_toolManager->ShutdownAllTools();
 
+    // Close modeless dialogs.  They're trouble when they get destroyed after the frame.
+    Unbind( EDA_EVT_CLOSE_DIALOG_BOOK_REPORTER, &SCH_EDIT_FRAME::onCloseSymbolDiffDialog, this );
+    Unbind( EDA_EVT_CLOSE_ERC_DIALOG, &SCH_EDIT_FRAME::onCloseErcDialog, this );
+
+    wxWindow* open_dlg = wxWindow::FindWindowByName( DIALOG_ERC_WINDOW_NAME );
+
+    if( open_dlg )
+        open_dlg->Destroy();
+
     // Close the find dialog and preserve its setting if it is displayed.
     if( m_findReplaceDialog )
     {
@@ -851,6 +853,26 @@ void SCH_EDIT_FRAME::doCloseWindow()
         m_findReplaceDialog = nullptr;
     }
 
+    if( m_diffSymbolDialog )
+    {
+        m_diffSymbolDialog->Destroy();
+        m_diffSymbolDialog = nullptr;
+    }
+
+    if( m_ercDialog )
+    {
+        m_ercDialog->Destroy();
+        m_ercDialog = nullptr;
+    }
+
+    // Shutdown all running tools
+    if( m_toolManager )
+    {
+        m_toolManager->ShutdownAllTools();
+        delete m_toolManager;
+        m_toolManager = nullptr;
+    }
+
     wxAuiPaneInfo& hierarchy_pane = m_auimgr.GetPane( SchematicHierarchyPaneName() );
 
     if( hierarchy_pane.IsShown() && hierarchy_pane.IsFloating() )
@@ -2075,3 +2097,42 @@ void SCH_EDIT_FRAME::DisplayCurrentSheet()
 
     m_hierarchy->UpdateHierarchySelection();
 }
+
+
+DIALOG_BOOK_REPORTER* SCH_EDIT_FRAME::GetSymbolDiffDialog()
+{
+    if( !m_diffSymbolDialog )
+        m_diffSymbolDialog = new DIALOG_BOOK_REPORTER( this, DIFF_SYMBOLS_DIALOG_NAME,
+                                                       _( "Diff Symbol with Library" ) );
+
+    return m_diffSymbolDialog;
+}
+
+
+void SCH_EDIT_FRAME::onCloseSymbolDiffDialog( wxCommandEvent& aEvent )
+{
+    if( m_diffSymbolDialog && aEvent.GetString() == DIFF_SYMBOLS_DIALOG_NAME )
+    {
+        m_diffSymbolDialog->Destroy();
+        m_diffSymbolDialog = nullptr;
+    }
+}
+
+
+DIALOG_ERC* SCH_EDIT_FRAME::GetErcDialog()
+{
+    if( !m_ercDialog )
+        m_ercDialog = new DIALOG_ERC( this );
+
+    return m_ercDialog;
+}
+
+
+void SCH_EDIT_FRAME::onCloseErcDialog( wxCommandEvent& aEvent )
+{
+    if( m_ercDialog )
+    {
+        m_ercDialog->Destroy();
+        m_ercDialog = nullptr;
+    }
+}
diff --git a/eeschema/sch_edit_frame.h b/eeschema/sch_edit_frame.h
index 6c3878bb5f..dc3ad0b780 100644
--- a/eeschema/sch_edit_frame.h
+++ b/eeschema/sch_edit_frame.h
@@ -3,7 +3,7 @@
  *
  * Copyright (C) 2015 Jean-Pierre Charras, jp.charras wanadoo.fr
  * Copyright (C) 2008 Wayne Stambaugh <stambaughw@gmail.com>
- * Copyright (C) 2004-2022 KiCad Developers, see AUTHORS.txt for contributors.
+ * Copyright (C) 2004-2023 KiCad Developers, see AUTHORS.txt for contributors.
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
@@ -54,6 +54,8 @@ class SCH_SYMBOL;
 class SCH_FIELD;
 class SCH_JUNCTION;
 class SCHEMATIC;
+class DIALOG_BOOK_REPORTER;
+class DIALOG_ERC;
 class DIALOG_SCH_FIND;
 class wxFindReplaceData;
 class RESCUER;
@@ -271,16 +273,18 @@ public:
     void TestDanglingEnds();
 
     /**
-     * Sends items to Pcbnew for selection
+     * Send items to board editor for selection.
+     *
+     * This is used for when the eeschema user is using the cross-probe tool.
      *
      * @param aItems are the items to select
-     * @param aForce select the element in pcbnew whether or not the user has the select option chosen
-     * This is used for when the eeschema user is using the cross-probe tool
+     * @param aForce select the element in pcbnew whether or not the user has the select option
+     *        chosen
      */
     void SendSelectItemsToPcb( const std::vector<EDA_ITEM*>& aItems, bool aForce );
 
     /**
-     * Sends a net name to Pcbnew for highlighting
+     * Send a net name to Pcbnew for highlighting.
      *
      * @param aNetName is the name of a net, or empty string to clear highlight
      */
@@ -814,6 +818,10 @@ public:
      */
     void ToggleSchematicHierarchy();
 
+    DIALOG_BOOK_REPORTER* GetSymbolDiffDialog();
+
+    DIALOG_ERC* GetErcDialog();
+
     /**
      * @return the name of the wxAuiPaneInfo managing the Hierarchy Navigator panel
      */
@@ -843,6 +851,10 @@ protected:
 
     void saveProjectSettings() override;
 
+    void onCloseSymbolDiffDialog( wxCommandEvent& aEvent );
+
+    void onCloseErcDialog( wxCommandEvent& aEvent );
+
 private:
     // Called when resizing the Hierarchy Navigator panel
     void OnResizeHierarchyNavigator( wxSizeEvent& aEvent );
@@ -932,7 +944,8 @@ private:
                                                   ///< to call a custom net list generator.
 
     DIALOG_SCH_FIND*        m_findReplaceDialog;
-
+    DIALOG_ERC*             m_ercDialog;
+    DIALOG_BOOK_REPORTER*   m_diffSymbolDialog;
     HIERARCHY_PANE*         m_hierarchy;
 
 	bool m_syncingPcbToSchSelection; // Recursion guard when synchronizing selection from PCB
diff --git a/eeschema/tools/ee_inspection_tool.cpp b/eeschema/tools/ee_inspection_tool.cpp
index 0a2d1342a3..e9ee3ad3e8 100644
--- a/eeschema/tools/ee_inspection_tool.cpp
+++ b/eeschema/tools/ee_inspection_tool.cpp
@@ -48,12 +48,8 @@
 #include <math/util.h>      // for KiROUND
 
 
-#define DIFF_SYMBOLS_DIALOG_NAME wxT( "DiffSymbolsDialog" )
-
-
 EE_INSPECTION_TOOL::EE_INSPECTION_TOOL() :
-    EE_TOOL_BASE<SCH_BASE_FRAME>( "eeschema.InspectionTool" ),
-    m_ercDialog( nullptr )
+    EE_TOOL_BASE<SCH_BASE_FRAME>( "eeschema.InspectionTool" )
 {
 }
 
@@ -79,9 +75,11 @@ void EE_INSPECTION_TOOL::Reset( RESET_REASON aReason )
 {
     EE_TOOL_BASE::Reset( aReason );
 
-    if( aReason == MODEL_RELOAD )
+    if( aReason == MODEL_RELOAD  && m_frame )
     {
-        DestroyERCDialog();
+        wxCommandEvent* evt = new wxCommandEvent( EDA_EVT_CLOSE_ERC_DIALOG, wxID_ANY );
+
+        wxQueueEvent( m_frame, evt );
     }
 }
 
@@ -95,46 +93,35 @@ int EE_INSPECTION_TOOL::RunERC( const TOOL_EVENT& aEvent )
 
 void EE_INSPECTION_TOOL::ShowERCDialog()
 {
-    if( m_frame->IsType( FRAME_SCH ) )
-    {
-        if( m_ercDialog )
-        {
-            // Needed at least on Windows. Raise() is not enough
-            m_ercDialog->Show( true );
-            // Bring it to the top if already open.  Dual monitor users need this.
-            m_ercDialog->Raise();
-        }
-        else
-        {
-            // This is a modeless dialog, so new it rather than instantiating on stack.
-            m_ercDialog = new DIALOG_ERC( static_cast<SCH_EDIT_FRAME*>( m_frame ) );
+    SCH_EDIT_FRAME* frame = dynamic_cast<SCH_EDIT_FRAME*>( m_frame );
 
-            m_ercDialog->Show( true );
-        }
-    }
-}
+    wxCHECK( frame, /* void */ );
 
+    DIALOG_ERC* dlg = frame->GetErcDialog();
 
-void EE_INSPECTION_TOOL::DestroyERCDialog()
-{
-    if( m_ercDialog )
-        m_ercDialog->Destroy();
+    wxCHECK( dlg, /* void */ );
 
-    m_ercDialog = nullptr;
+    // Needed at least on Windows. Raise() is not enough
+    dlg->Show( true );
+
+    // Bring it to the top if already open.  Dual monitor users need this.
+    dlg->Raise();
 }
 
 
 int EE_INSPECTION_TOOL::PrevMarker( const TOOL_EVENT& aEvent )
 {
-    if( m_ercDialog )
+    SCH_EDIT_FRAME* frame = dynamic_cast<SCH_EDIT_FRAME*>( m_frame );
+
+    wxCHECK( frame, 0 );
+
+    DIALOG_ERC* dlg = frame->GetErcDialog();
+
+    if( dlg )
     {
-        m_ercDialog->Show( true );
-        m_ercDialog->Raise();
-        m_ercDialog->PrevMarker();
-    }
-    else
-    {
-        ShowERCDialog();
+        dlg->Show( true );
+        dlg->Raise();
+        dlg->PrevMarker();
     }
 
     return 0;
@@ -143,16 +130,17 @@ int EE_INSPECTION_TOOL::PrevMarker( const TOOL_EVENT& aEvent )
 
 int EE_INSPECTION_TOOL::NextMarker( const TOOL_EVENT& aEvent )
 {
-    if( m_ercDialog )
-    {
-        m_ercDialog->Show( true );
-        m_ercDialog->Raise();
-        m_ercDialog->NextMarker();
-    }
-    else
-    {
-        ShowERCDialog();
-    }
+    SCH_EDIT_FRAME* frame = dynamic_cast<SCH_EDIT_FRAME*>( m_frame );
+
+    wxCHECK( frame, 0 );
+
+    DIALOG_ERC* dlg = frame->GetErcDialog();
+
+    wxCHECK( dlg, 0 );
+
+    dlg->Show( true );
+    dlg->Raise();
+    dlg->NextMarker();
 
     return 0;
 }
@@ -160,18 +148,26 @@ int EE_INSPECTION_TOOL::NextMarker( const TOOL_EVENT& aEvent )
 
 int EE_INSPECTION_TOOL::CrossProbe( const TOOL_EVENT& aEvent )
 {
-    if( m_ercDialog )
+    SCH_EDIT_FRAME* frame = dynamic_cast<SCH_EDIT_FRAME*>( m_frame );
+
+    wxCHECK( frame, 0 );
+
+    DIALOG_ERC* dlg = frame->GetErcDialog();
+
+    wxCHECK( dlg, 0 );
+
+    EE_SELECTION_TOOL* selectionTool = m_toolMgr->GetTool<EE_SELECTION_TOOL>();
+
+    wxCHECK( selectionTool, 0 );
+
+    EE_SELECTION&      selection = selectionTool->GetSelection();
+
+    if( selection.GetSize() == 1 && selection.Front()->Type() == SCH_MARKER_T )
     {
-        EE_SELECTION_TOOL* selectionTool = m_toolMgr->GetTool<EE_SELECTION_TOOL>();
-        EE_SELECTION&      selection = selectionTool->GetSelection();
+        if( !dlg->IsShown() )
+            dlg->Show( true );
 
-        if( selection.GetSize() == 1 && selection.Front()->Type() == SCH_MARKER_T )
-        {
-            if( !m_ercDialog->IsShown() )
-                m_ercDialog->Show( true );
-
-            m_ercDialog->SelectMarker( static_cast<SCH_MARKER*>( selection.Front() ) );
-        }
+        dlg->SelectMarker( static_cast<SCH_MARKER*>( selection.Front() ) );
     }
 
     // Show the item info on a left click on this item
@@ -190,14 +186,20 @@ int EE_INSPECTION_TOOL::ExcludeMarker( const TOOL_EVENT& aEvent )
     if( selection.GetSize() == 1 && selection.Front()->Type() == SCH_MARKER_T )
         marker = static_cast<SCH_MARKER*>( selection.Front() );
 
-    if( m_ercDialog )
-    {
-        // Let the ERC dialog handle it since it has more update hassles to worry about
-        // Note that if marker is nullptr the dialog will exclude whichever marker is selected
-        // in the dialog itself
-        m_ercDialog->ExcludeMarker( marker );
-    }
-    else if( marker != nullptr )
+    SCH_EDIT_FRAME* frame = dynamic_cast<SCH_EDIT_FRAME*>( m_frame );
+
+    wxCHECK( frame, 0 );
+
+    DIALOG_ERC* dlg = frame->GetErcDialog();
+
+    wxCHECK( dlg, 0 );
+
+    // Let the ERC dialog handle it since it has more update hassles to worry about
+    // Note that if marker is nullptr the dialog will exclude whichever marker is selected
+    // in the dialog itself
+    dlg->ExcludeMarker( marker );
+
+    if( marker != nullptr )
     {
         marker->SetExcluded( true );
         m_frame->GetCanvas()->GetView()->Update( marker );
@@ -244,8 +246,9 @@ int EE_INSPECTION_TOOL::CheckSymbol( const TOOL_EVENT& aEvent )
 
 int EE_INSPECTION_TOOL::DiffSymbol( const TOOL_EVENT& aEvent )
 {
-    if( !m_frame->IsType( FRAME_SCH ) )
-        return 0;
+    SCH_EDIT_FRAME* schEditorFrame = dynamic_cast<SCH_EDIT_FRAME*>( m_frame );
+
+    wxCHECK( schEditorFrame, 0 );
 
     EE_SELECTION& selection = m_selectionTool->RequestSelection( { SCH_SYMBOL_T } );
 
@@ -255,14 +258,9 @@ int EE_INSPECTION_TOOL::DiffSymbol( const TOOL_EVENT& aEvent )
         return 0;
     }
 
-    wxWindow*             window = wxWindow::FindWindowByName( DIFF_SYMBOLS_DIALOG_NAME );
-    DIALOG_BOOK_REPORTER* dialog = dynamic_cast<DIALOG_BOOK_REPORTER*>( window );
+    DIALOG_BOOK_REPORTER* dialog = schEditorFrame->GetSymbolDiffDialog();
 
-    if( !dialog )
-    {
-        dialog = new DIALOG_BOOK_REPORTER( m_frame, DIFF_SYMBOLS_DIALOG_NAME,
-                                           _( "Diff Symbol with Library" ) );
-    }
+    wxCHECK( dialog, 0 );
 
     dialog->DeleteAllPages();
 
@@ -278,7 +276,8 @@ int EE_INSPECTION_TOOL::DiffSymbol( const TOOL_EVENT& aEvent )
     r->Report( wxS( "<h7>" ) + _( "Schematic vs library diff for:" ) + wxS( "</h7>" ) );
     r->Report( wxS( "<ul><li>" ) + EscapeHTML( symbolDesc ) + wxS( "</li>" )
              + wxS( "<li>" ) + _( "Library: " ) + EscapeHTML( libName ) + wxS( "</li>" )
-             + wxS( "<li>" ) + _( "Library item: " ) + EscapeHTML( symbolName ) + wxS( "</li></ul>" ) );
+             + wxS( "<li>" ) + _( "Library item: " ) + EscapeHTML( symbolName )
+             + wxS( "</li></ul>" ) );
 
     r->Report( "" );
 
diff --git a/eeschema/tools/ee_inspection_tool.h b/eeschema/tools/ee_inspection_tool.h
index 1842961299..73e2df7cef 100644
--- a/eeschema/tools/ee_inspection_tool.h
+++ b/eeschema/tools/ee_inspection_tool.h
@@ -49,7 +49,6 @@ public:
 
     int RunERC( const TOOL_EVENT& aEvent );
     void ShowERCDialog();
-    void DestroyERCDialog();
 
     int PrevMarker( const TOOL_EVENT& aEvent );
     int NextMarker( const TOOL_EVENT& aEvent );
@@ -76,7 +75,6 @@ private:
     void setTransitions() override;
 
 private:
-    DIALOG_ERC* m_ercDialog;
 };
 
 #endif /* EE_INSPECTION_TOOL_H */
diff --git a/include/dialogs/dialog_book_reporter.h b/include/dialogs/dialog_book_reporter.h
index d8c90f6f53..5a47b9263d 100644
--- a/include/dialogs/dialog_book_reporter.h
+++ b/include/dialogs/dialog_book_reporter.h
@@ -30,16 +30,18 @@ class KIWAY_PLAYER;
 class WX_HTML_REPORT_BOX;
 class wxHtmlLinkEvent;
 
+/**
+ * Event sent to parent when dialog is mode-less.
+ */
+wxDECLARE_EVENT( EDA_EVT_CLOSE_DIALOG_BOOK_REPORTER, wxCommandEvent );
+
 class DIALOG_BOOK_REPORTER : public DIALOG_BOOK_REPORTER_BASE
 {
 public:
-    DIALOG_BOOK_REPORTER( KIWAY_PLAYER* aParent, const wxString& aDialogName,
+    DIALOG_BOOK_REPORTER( KIWAY_PLAYER* aParent, const wxString& aName,
                           const wxString& aDialogTitle );
 
-    void OnOK( wxCommandEvent& aEvent ) override
-    {
-        Destroy();
-    }
+    void OnClose( wxCloseEvent& aEvent ) override;
 
     void OnErrorLinkClicked( wxHtmlLinkEvent& aEvent );
 
diff --git a/include/eda_base_frame.h b/include/eda_base_frame.h
index 7105ab12c3..e45b5566c8 100644
--- a/include/eda_base_frame.h
+++ b/include/eda_base_frame.h
@@ -3,7 +3,7 @@
  *
  * Copyright (C) 2009-2015 Jean-Pierre Charras, jp.charras wanadoo.fr
  * Copyright (C) 2011 Wayne Stambaugh <stambaughw@gmail.com>
- * Copyright (C) 1992-2022 KiCad Developers, see AUTHORS.txt for contributors.
+ * Copyright (C) 1992-2023 KiCad Developers, see AUTHORS.txt for contributors.
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
@@ -89,7 +89,7 @@ struct WINDOW_STATE;
 /// This is the handler functor for the update UI events
 typedef std::function< void( wxUpdateUIEvent& ) > UIUpdateHandler;
 
-wxDECLARE_EVENT( UNITS_CHANGED, wxCommandEvent );
+wxDECLARE_EVENT( EDA_EVT_UNITS_CHANGED, wxCommandEvent );
 
 
 /**
diff --git a/include/pcb_base_frame.h b/include/pcb_base_frame.h
index 5b8b16d60a..cc95b80552 100644
--- a/include/pcb_base_frame.h
+++ b/include/pcb_base_frame.h
@@ -3,7 +3,7 @@
  *
  * Copyright (C) 2018 Jean-Pierre Charras, jp.charras at wanadoo.fr
  * Copyright (C) 2008-2016 Wayne Stambaugh <stambaughw@gmail.com>
- * Copyright (C) 1992-2022 KiCad Developers, see AUTHORS.txt for contributors.
+ * Copyright (C) 1992-2023 KiCad Developers, see AUTHORS.txt for contributors.
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
@@ -61,7 +61,7 @@ struct MAGNETIC_SETTINGS;
 class NL_PCBNEW_PLUGIN;
 class PROGRESS_REPORTER;
 
-wxDECLARE_EVENT( BOARD_CHANGED, wxCommandEvent );
+wxDECLARE_EVENT( EDA_EVT_BOARD_CHANGED, wxCommandEvent );
 
 /**
  * Base PCB main window class for Pcbnew, Gerbview, and CvPcb footprint viewer.
@@ -382,6 +382,10 @@ public:
 
     virtual void ActivateGalCanvas() override;
 
+    void AddBoardChangeListener( wxEvtHandler* aListener );
+
+    void RemoveBoardChangeListener( wxEvtHandler* aListener );
+
 protected:
     bool canCloseWindow( wxCloseEvent& aCloseEvent ) override;
 
@@ -413,6 +417,8 @@ protected:
 
 private:
     NL_PCBNEW_PLUGIN*       m_spaceMouse;
+
+    std::vector<wxEvtHandler*> m_boardChangeListeners;
 };
 
 #endif  // PCB_BASE_FRAME_H
diff --git a/pcbnew/dialogs/dialog_global_edit_tracks_and_vias.cpp b/pcbnew/dialogs/dialog_global_edit_tracks_and_vias.cpp
index c8a7cb4921..ae28f51f0f 100644
--- a/pcbnew/dialogs/dialog_global_edit_tracks_and_vias.cpp
+++ b/pcbnew/dialogs/dialog_global_edit_tracks_and_vias.cpp
@@ -2,7 +2,7 @@
  * This program source code file is part of KiCad, a free EDA CAD application.
  *
  * Copyright (C) 2009-2016 Jean-Pierre Charras, jean-pierre.charras at wanadoo.fr
- * Copyright (C) 1992-2022 KiCad Developers, see AUTHORS.txt for contributors.
+ * Copyright (C) 1992-2023 KiCad Developers, see AUTHORS.txt for contributors.
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
@@ -143,7 +143,8 @@ DIALOG_GLOBAL_EDIT_TRACKS_AND_VIAS::DIALOG_GLOBAL_EDIT_TRACKS_AND_VIAS( PCB_EDIT
                           wxCommandEventHandler( DIALOG_GLOBAL_EDIT_TRACKS_AND_VIAS::OnNetFilterSelect ),
                           nullptr, this );
 
-    m_parent->Bind( UNITS_CHANGED, &DIALOG_GLOBAL_EDIT_TRACKS_AND_VIAS::onUnitsChanged, this );
+    m_parent->Bind( EDA_EVT_UNITS_CHANGED, &DIALOG_GLOBAL_EDIT_TRACKS_AND_VIAS::onUnitsChanged,
+                    this );
 
     finishDialogSettings();
 }
@@ -165,7 +166,8 @@ DIALOG_GLOBAL_EDIT_TRACKS_AND_VIAS::~DIALOG_GLOBAL_EDIT_TRACKS_AND_VIAS()
                              wxCommandEventHandler( DIALOG_GLOBAL_EDIT_TRACKS_AND_VIAS::OnNetFilterSelect ),
                              nullptr, this );
 
-    m_parent->Unbind( UNITS_CHANGED, &DIALOG_GLOBAL_EDIT_TRACKS_AND_VIAS::onUnitsChanged, this );
+    m_parent->Unbind( EDA_EVT_UNITS_CHANGED,
+                      &DIALOG_GLOBAL_EDIT_TRACKS_AND_VIAS::onUnitsChanged, this );
 
     delete[] m_originalColWidths;
 }
diff --git a/pcbnew/dialogs/dialog_net_inspector.cpp b/pcbnew/dialogs/dialog_net_inspector.cpp
index 996067809d..9756283c04 100644
--- a/pcbnew/dialogs/dialog_net_inspector.cpp
+++ b/pcbnew/dialogs/dialog_net_inspector.cpp
@@ -50,6 +50,9 @@
 #include <vector>
 
 
+wxDEFINE_EVENT( EDA_EVT_CLOSE_NET_INSPECTOR_DIALOG, wxCommandEvent );
+
+
 static DIALOG_NET_INSPECTOR::SETTINGS g_settings;
 
 
@@ -62,9 +65,10 @@ enum class CSV_COLUMN_DESC : int
 
 struct DIALOG_NET_INSPECTOR::COLUMN_DESC
 {
-    COLUMN_DESC( unsigned aNum, PCB_LAYER_ID aLayer, const wxString& aDisp, const wxString& aCsv, CSV_COLUMN_DESC aFlags ) :
-            num( aNum ), layer( aLayer ), display_name( aDisp ), csv_name( aCsv ), csv_flags( aFlags )
-            {}
+    COLUMN_DESC( unsigned aNum, PCB_LAYER_ID aLayer, const wxString& aDisp, const wxString& aCsv,
+                 CSV_COLUMN_DESC aFlags ) :
+        num( aNum ), layer( aLayer ), display_name( aDisp ), csv_name( aCsv ), csv_flags( aFlags )
+    {}
 
     unsigned int num;
     PCB_LAYER_ID layer;
@@ -94,30 +98,6 @@ enum
 
 class DIALOG_NET_INSPECTOR::LIST_ITEM
 {
-private:
-    // an item can be the child of only one parent at a time.
-    // FIXME: could use a more lightweight container like intrusive forward list.
-    LIST_ITEM*              m_parent = nullptr;
-    std::vector<LIST_ITEM*> m_children;
-
-    bool          m_is_group          = false;
-    unsigned int  m_group_number      = 0;
-    NETINFO_ITEM* m_net               = nullptr;
-    unsigned int  m_pad_count         = 0;
-    unsigned int  m_via_count         = 0;
-    uint64_t      m_via_length        = 0;
-    uint64_t      m_chip_wire_length  = 0;
-
-    std::array<uint64_t, MAX_CU_LAYERS> m_layer_wire_length{};
-
-
-    // dirty bits to record when some attribute has changed.  this is to
-    // avoid unnecessary resort operations.
-    std::vector<char> m_column_changed;
-
-    // cached formatted net name for faster display sorting.
-    wxString m_net_name;
-
 public:
     LIST_ITEM( unsigned int aGroupNumber, const wxString& aGroupName ) :
             m_is_group( true ),
@@ -162,7 +142,7 @@ public:
         std::fill( m_column_changed.begin(), m_column_changed.end(), 0 );
     }
 
-    decltype( m_pad_count ) GetPadCount() const
+    unsigned int GetPadCount() const
     {
         return m_pad_count;
     }
@@ -172,7 +152,7 @@ public:
         return m_column_changed[COLUMN_PAD_COUNT];
     }
 
-    void SetPadCount( const decltype( m_pad_count ) &aValue )
+    void SetPadCount( unsigned int aValue )
     {
         if( m_parent )
             m_parent->SetPadCount( m_parent->GetPadCount() - m_pad_count + aValue );
@@ -181,7 +161,7 @@ public:
         m_pad_count = aValue;
     }
 
-    void AddPadCount( const decltype( m_pad_count ) &aValue )
+    void AddPadCount( unsigned int aValue )
     {
         if( m_parent )
             m_parent->AddPadCount( aValue );
@@ -190,7 +170,7 @@ public:
         m_pad_count += aValue;
     }
 
-    void SubPadCount( const decltype( m_pad_count ) &aValue )
+    void SubPadCount( unsigned int aValue )
     {
         if( m_parent )
             m_parent->SubPadCount( aValue );
@@ -209,7 +189,7 @@ public:
         return m_column_changed[COLUMN_VIA_COUNT];
     }
 
-    void SetViaCount( const decltype( m_via_count ) &aValue )
+    void SetViaCount( unsigned int aValue )
     {
         if( m_parent )
             m_parent->SetViaCount( m_parent->GetViaCount() - m_via_count + aValue );
@@ -218,7 +198,7 @@ public:
         m_via_count = aValue;
     }
 
-    void AddViaCount( const decltype( m_via_count ) &aValue )
+    void AddViaCount( unsigned int aValue )
     {
         if( m_parent )
             m_parent->AddViaCount( aValue );
@@ -227,7 +207,7 @@ public:
         m_via_count += aValue;
     }
 
-    void SubViaCount( const decltype( m_via_count ) &aValue )
+    void SubViaCount( unsigned int aValue )
     {
         if( m_parent )
             m_parent->SubViaCount( aValue );
@@ -246,7 +226,7 @@ public:
         return m_column_changed[COLUMN_VIA_LENGTH];
     }
 
-    void SetViaLength( const decltype( m_via_length ) &aValue )
+    void SetViaLength( unsigned int aValue )
     {
         if( m_parent )
             m_parent->SetViaLength( m_parent->GetViaLength() - m_via_length + aValue );
@@ -255,7 +235,7 @@ public:
         m_via_length = aValue;
     }
 
-    void AddViaLength( const decltype( m_via_length ) &aValue )
+    void AddViaLength( unsigned int aValue )
     {
         if( m_parent )
             m_parent->AddViaLength( aValue );
@@ -264,7 +244,7 @@ public:
         m_via_length += aValue;
     }
 
-    void SubViaLength( const decltype( m_via_length ) &aValue )
+    void SubViaLength( uint64_t aValue )
     {
         if( m_parent )
             m_parent->SubViaLength( aValue );
@@ -335,7 +315,7 @@ public:
         return m_column_changed[COLUMN_CHIP_LENGTH];
     }
 
-    void SetChipWireLength( const decltype( m_chip_wire_length ) &aValue )
+    void SetChipWireLength( uint64_t aValue )
     {
         if( m_parent )
             m_parent->SetChipWireLength(
@@ -345,7 +325,7 @@ public:
         m_chip_wire_length = aValue;
     }
 
-    void AddChipWireLength( const decltype( m_chip_wire_length ) &aValue )
+    void AddChipWireLength( uint64_t aValue )
     {
         if( m_parent )
             m_parent->AddChipWireLength( aValue );
@@ -354,7 +334,7 @@ public:
         m_chip_wire_length += aValue;
     }
 
-    void SubChipWireLength( const decltype( m_chip_wire_length ) &aValue )
+    void SubChipWireLength( uint64_t aValue )
     {
         if( m_parent )
             m_parent->SubChipWireLength( aValue );
@@ -362,7 +342,6 @@ public:
         m_column_changed[COLUMN_CHIP_LENGTH] |= ( aValue != 0 );
         m_chip_wire_length -= aValue;
     }
-    ;
 
     // the total length column is always computed, never stored.
     unsigned long long int GetTotalLength() const
@@ -416,6 +395,29 @@ public:
             m_parent->m_children.push_back( this );
         }
     }
+
+private:
+    // an item can be the child of only one parent at a time.
+    // FIXME: could use a more lightweight container like intrusive forward list.
+    LIST_ITEM*              m_parent = nullptr;
+    std::vector<LIST_ITEM*> m_children;
+
+    bool          m_is_group          = false;
+    unsigned int  m_group_number      = 0;
+    NETINFO_ITEM* m_net               = nullptr;
+    unsigned int  m_pad_count         = 0;
+    unsigned int  m_via_count         = 0;
+    uint64_t      m_via_length        = 0;
+    uint64_t      m_chip_wire_length  = 0;
+
+    std::array<uint64_t, MAX_CU_LAYERS> m_layer_wire_length{};
+
+    // dirty bits to record when some attribute has changed.  this is to
+    // avoid unnecessary resort operations.
+    std::vector<char> m_column_changed;
+
+    // cached formatted net name for faster display sorting.
+    wxString m_net_name;
 };
 
 
@@ -443,15 +445,6 @@ struct DIALOG_NET_INSPECTOR::LIST_ITEM_NETCODE_CMP_LESS
 
 class DIALOG_NET_INSPECTOR::DATA_MODEL : public wxDataViewModel
 {
-private:
-    DIALOG_NET_INSPECTOR& m_parent;
-
-    // primary container, sorted by netcode number.
-    // groups have netcode < 0, so they always come first, in the order
-    // of the filter strings as input by the user (group mode 0, 1) or
-    // in order of occurrence (group mode 2, 3).
-    std::vector<std::unique_ptr<LIST_ITEM>> m_items;
-
 public:
 
     DATA_MODEL( DIALOG_NET_INSPECTOR& parent ) : m_parent( parent )
@@ -618,7 +611,8 @@ public:
 
                         if( match )
                         {
-                            wxString match_str = i->GetNetName().substr( match.start, match.length );
+                            wxString match_str = i->GetNetName().substr( match.start,
+                                                                         match.length );
 
                             auto group = std::find_if( groups.begin(), groups.end(),
                                     [&]( const std::unique_ptr<LIST_ITEM>& x )
@@ -956,24 +950,39 @@ protected:
     {
         return wxS( "string" );
     }
+
+private:
+    DIALOG_NET_INSPECTOR& m_parent;
+
+    // primary container, sorted by netcode number.
+    // groups have netcode < 0, so they always come first, in the order
+    // of the filter strings as input by the user (group mode 0, 1) or
+    // in order of occurrence (group mode 2, 3).
+    std::vector<std::unique_ptr<LIST_ITEM>> m_items;
 };
 
 
-DIALOG_NET_INSPECTOR::DIALOG_NET_INSPECTOR( PCB_EDIT_FRAME* aParent, const wxString& aDialogName ) :
+DIALOG_NET_INSPECTOR::DIALOG_NET_INSPECTOR( PCB_EDIT_FRAME* aParent ) :
         DIALOG_NET_INSPECTOR_BASE( aParent ),
         m_zero_netitem( nullptr ),
         m_frame( aParent )
 {
-    SetName( aDialogName );
-
-    m_columns.emplace_back(  0u, UNDEFINED_LAYER, _( "Net" ), _( "Net Code" ), CSV_COLUMN_DESC::CSV_NONE  );
-    m_columns.emplace_back(  1u, UNDEFINED_LAYER, _( "Name" ), _( "Net Name" ), CSV_COLUMN_DESC::CSV_QUOTE  );
-    m_columns.emplace_back(  2u, UNDEFINED_LAYER, _( "Pad Count" ), _( "Pad Count" ), CSV_COLUMN_DESC::CSV_NONE  );
-    m_columns.emplace_back(  3u, UNDEFINED_LAYER, _( "Via Count" ), _( "Via Count" ), CSV_COLUMN_DESC::CSV_NONE  );
-    m_columns.emplace_back(  4u, UNDEFINED_LAYER, _( "Via Length" ), _( "Via Length" ), CSV_COLUMN_DESC::CSV_NONE  );
-    m_columns.emplace_back(  5u, UNDEFINED_LAYER, _( "Track Length" ), _( "Track Length" ), CSV_COLUMN_DESC::CSV_NONE  );
-    m_columns.emplace_back(  6u, UNDEFINED_LAYER, _( "Die Length" ), _( "Die Length" ), CSV_COLUMN_DESC::CSV_NONE  );
-    m_columns.emplace_back(  7u, UNDEFINED_LAYER, _( "Total Length" ), _( "Net Length" ), CSV_COLUMN_DESC::CSV_NONE );
+    m_columns.emplace_back( 0u, UNDEFINED_LAYER, _( "Net" ), _( "Net Code" ),
+                            CSV_COLUMN_DESC::CSV_NONE  );
+    m_columns.emplace_back( 1u, UNDEFINED_LAYER, _( "Name" ), _( "Net Name" ),
+                            CSV_COLUMN_DESC::CSV_QUOTE );
+    m_columns.emplace_back( 2u, UNDEFINED_LAYER, _( "Pad Count" ), _( "Pad Count" ),
+                            CSV_COLUMN_DESC::CSV_NONE );
+    m_columns.emplace_back( 3u, UNDEFINED_LAYER, _( "Via Count" ), _( "Via Count" ),
+                            CSV_COLUMN_DESC::CSV_NONE );
+    m_columns.emplace_back( 4u, UNDEFINED_LAYER, _( "Via Length" ), _( "Via Length" ),
+                            CSV_COLUMN_DESC::CSV_NONE );
+    m_columns.emplace_back( 5u, UNDEFINED_LAYER, _( "Track Length" ), _( "Track Length" ),
+                            CSV_COLUMN_DESC::CSV_NONE );
+    m_columns.emplace_back( 6u, UNDEFINED_LAYER, _( "Die Length" ), _( "Die Length" ),
+                            CSV_COLUMN_DESC::CSV_NONE );
+    m_columns.emplace_back( 7u, UNDEFINED_LAYER, _( "Total Length" ), _( "Net Length" ),
+                            CSV_COLUMN_DESC::CSV_NONE );
 
     m_brd = aParent->GetBoard();
 
@@ -989,44 +998,51 @@ DIALOG_NET_INSPECTOR::DIALOG_NET_INSPECTOR( PCB_EDIT_FRAME* aParent, const wxStr
         },
         [&]()
         {
-            m_netsList->AppendTextColumn( m_columns[COLUMN_NAME].display_name, m_columns[COLUMN_NAME],
+            m_netsList->AppendTextColumn( m_columns[COLUMN_NAME].display_name,
+                                          m_columns[COLUMN_NAME],
                                           wxDATAVIEW_CELL_INERT, -1, wxALIGN_LEFT,
                                           wxDATAVIEW_COL_RESIZABLE | wxDATAVIEW_COL_REORDERABLE |
                                           wxDATAVIEW_COL_SORTABLE );
         },
         [&]()
         {
-            m_netsList->AppendTextColumn( m_columns[COLUMN_PAD_COUNT].display_name, m_columns[COLUMN_PAD_COUNT],
+            m_netsList->AppendTextColumn( m_columns[COLUMN_PAD_COUNT].display_name,
+                                          m_columns[COLUMN_PAD_COUNT],
                                           wxDATAVIEW_CELL_INERT, -1, wxALIGN_CENTER,
                                           wxDATAVIEW_COL_REORDERABLE | wxDATAVIEW_COL_SORTABLE );
         },
         [&]()
         {
-            m_netsList->AppendTextColumn( m_columns[COLUMN_VIA_COUNT].display_name, m_columns[COLUMN_VIA_COUNT],
+            m_netsList->AppendTextColumn( m_columns[COLUMN_VIA_COUNT].display_name,
+                                          m_columns[COLUMN_VIA_COUNT],
                                           wxDATAVIEW_CELL_INERT, -1, wxALIGN_CENTER,
                                           wxDATAVIEW_COL_REORDERABLE | wxDATAVIEW_COL_SORTABLE );
         },
         [&]()
         {
-            m_netsList->AppendTextColumn( m_columns[COLUMN_VIA_LENGTH].display_name, m_columns[COLUMN_VIA_LENGTH],
+            m_netsList->AppendTextColumn( m_columns[COLUMN_VIA_LENGTH].display_name,
+                                          m_columns[COLUMN_VIA_LENGTH],
                                           wxDATAVIEW_CELL_INERT, -1, wxALIGN_CENTER,
                                           wxDATAVIEW_COL_REORDERABLE | wxDATAVIEW_COL_SORTABLE );
         },
         [&]()
         {
-            m_netsList->AppendTextColumn( m_columns[COLUMN_BOARD_LENGTH].display_name, m_columns[COLUMN_BOARD_LENGTH],
+            m_netsList->AppendTextColumn( m_columns[COLUMN_BOARD_LENGTH].display_name,
+                                          m_columns[COLUMN_BOARD_LENGTH],
                                           wxDATAVIEW_CELL_INERT, -1, wxALIGN_CENTER,
                                           wxDATAVIEW_COL_REORDERABLE | wxDATAVIEW_COL_SORTABLE );
         },
         [&]()
         {
-            m_netsList->AppendTextColumn( m_columns[COLUMN_CHIP_LENGTH].display_name, m_columns[COLUMN_CHIP_LENGTH],
+            m_netsList->AppendTextColumn( m_columns[COLUMN_CHIP_LENGTH].display_name,
+                                          m_columns[COLUMN_CHIP_LENGTH],
                                           wxDATAVIEW_CELL_INERT, -1, wxALIGN_CENTER,
                                           wxDATAVIEW_COL_REORDERABLE | wxDATAVIEW_COL_SORTABLE );
         },
         [&]()
         {
-            m_netsList->AppendTextColumn( m_columns[COLUMN_TOTAL_LENGTH].display_name, m_columns[COLUMN_TOTAL_LENGTH],
+            m_netsList->AppendTextColumn( m_columns[COLUMN_TOTAL_LENGTH].display_name,
+                                          m_columns[COLUMN_TOTAL_LENGTH],
                                           wxDATAVIEW_CELL_INERT, -1, wxALIGN_CENTER,
                                           wxDATAVIEW_COL_REORDERABLE | wxDATAVIEW_COL_SORTABLE );
         }
@@ -1095,19 +1111,8 @@ DIALOG_NET_INSPECTOR::DIALOG_NET_INSPECTOR( PCB_EDIT_FRAME* aParent, const wxStr
 
     finishDialogSettings();
 
-    m_frame->Bind( wxEVT_CLOSE_WINDOW,
-                   [this]( wxCloseEvent& aEvent )
-                   {
-                       Close();
-                       aEvent.Skip();
-                   } );
-
-    m_frame->Connect( UNITS_CHANGED,
-                      wxCommandEventHandler( DIALOG_NET_INSPECTOR::onUnitsChanged ),
-                      nullptr, this );
-    m_frame->Connect( BOARD_CHANGED,
-                      wxCommandEventHandler( DIALOG_NET_INSPECTOR::onBoardChanged ),
-                      nullptr, this );
+    Bind( EDA_EVT_UNITS_CHANGED, &DIALOG_NET_INSPECTOR::onUnitsChanged, this );
+    Bind( EDA_EVT_BOARD_CHANGED, &DIALOG_NET_INSPECTOR::onBoardChanged, this );
 
     if( m_brd != nullptr )
     {
@@ -1133,7 +1138,8 @@ DIALOG_NET_INSPECTOR::~DIALOG_NET_INSPECTOR()
     g_settings.group_by           = m_groupBy->IsChecked();
     g_settings.group_by_kind      = m_groupByKind->GetSelection();
     g_settings.group_by_text      = m_groupByText->GetValue();
-    g_settings.sorting_column     = sorting_column ? static_cast<int>( sorting_column->GetModelColumn() ) : -1;
+    g_settings.sorting_column     = sorting_column ?
+                                    static_cast<int>( sorting_column->GetModelColumn() ) : -1;
     g_settings.sort_order_asc     = sorting_column ? sorting_column->IsSortOrderAscending() : true;
     g_settings.column_order       = column_order;
 
@@ -1142,23 +1148,17 @@ DIALOG_NET_INSPECTOR::~DIALOG_NET_INSPECTOR()
     // from now on.  so just disassociate it.
     m_netsList->AssociateModel( nullptr );
 
-    m_frame->Disconnect( UNITS_CHANGED,
-                         wxCommandEventHandler( DIALOG_NET_INSPECTOR::onUnitsChanged ),
-                         nullptr, this );
-    m_frame->Disconnect( BOARD_CHANGED,
-                         wxCommandEventHandler( DIALOG_NET_INSPECTOR::onBoardChanged ),
-                         nullptr, this );
+    Unbind( EDA_EVT_UNITS_CHANGED, &DIALOG_NET_INSPECTOR::onUnitsChanged, this );
+    Unbind( EDA_EVT_BOARD_CHANGED, &DIALOG_NET_INSPECTOR::onBoardChanged, this );
 
     if( m_brd != nullptr )
         m_brd->RemoveListener( this );
-
-    m_frame->GetCanvas()->SetFocus();
 }
 
 
 void DIALOG_NET_INSPECTOR::onUnitsChanged( wxCommandEvent& event )
 {
-    this->m_units = m_frame->GetUserUnits();
+    m_units = m_frame->GetUserUnits();
 
     m_data_model->updateAllItems();
 
@@ -1636,7 +1636,8 @@ DIALOG_NET_INSPECTOR::buildNewItem( NETINFO_ITEM* aNet, unsigned int aPadCount,
 
         else if( PCB_TRACK* track = dynamic_cast<PCB_TRACK*>( item ) )
         {
-            new_item->AddLayerWireLength( track->GetLength(), static_cast<int>( track->GetLayer() ) );
+            new_item->AddLayerWireLength( track->GetLength(),
+                                          static_cast<int>( track->GetLayer() ) );
 
             if( item->Type() == PCB_VIA_T )
             {
@@ -2241,3 +2242,21 @@ void DIALOG_NET_INSPECTOR::onReport( wxCommandEvent& aEvent )
     f.Write();
     f.Close();
 }
+
+
+void DIALOG_NET_INSPECTOR::onClose( wxCloseEvent& aEvent )
+{
+    // Dialog is mode-less so let the parent know that it needs to be destroyed.
+    if( !IsModal() && !IsQuasiModal() )
+    {
+        wxCommandEvent* evt = new wxCommandEvent( EDA_EVT_CLOSE_NET_INSPECTOR_DIALOG, wxID_ANY );
+
+        evt->SetEventObject( this );
+        wxWindow* parent = GetParent();
+
+        if( parent )
+            wxQueueEvent( parent, evt );
+    }
+
+    aEvent.Skip();
+}
diff --git a/pcbnew/dialogs/dialog_net_inspector.h b/pcbnew/dialogs/dialog_net_inspector.h
index 8caa592283..536f9fa098 100644
--- a/pcbnew/dialogs/dialog_net_inspector.h
+++ b/pcbnew/dialogs/dialog_net_inspector.h
@@ -37,6 +37,11 @@ class CN_ITEM;
 class EDA_PATTERN_MATCH;
 class PCB_TRACK;
 
+/**
+ * Event sent to parent when dialog is mode-less.
+ */
+wxDECLARE_EVENT( EDA_EVT_CLOSE_NET_INSPECTOR_DIALOG, wxCommandEvent );
+
 class DIALOG_NET_INSPECTOR : public DIALOG_NET_INSPECTOR_BASE, public BOARD_LISTENER
 {
 public:
@@ -53,18 +58,23 @@ public:
         std::vector<int> column_order;
     };
 
-    DIALOG_NET_INSPECTOR( PCB_EDIT_FRAME* aParent, const wxString& aDialogName );
+    DIALOG_NET_INSPECTOR( PCB_EDIT_FRAME* aParent );
     ~DIALOG_NET_INSPECTOR();
 
     virtual void OnBoardItemAdded( BOARD& aBoard, BOARD_ITEM* aBoardItem ) override;
     virtual void OnBoardItemsAdded( BOARD& aBoard, std::vector<BOARD_ITEM*>& aBoardItems ) override;
     virtual void OnBoardItemRemoved( BOARD& aBoard, BOARD_ITEM* aBoardItem ) override;
-    virtual void OnBoardItemsRemoved( BOARD& aBoard, std::vector<BOARD_ITEM*>& aBoardItems ) override;
+    virtual void OnBoardItemsRemoved( BOARD& aBoard,
+                                      std::vector<BOARD_ITEM*>& aBoardItems ) override;
     virtual void OnBoardNetSettingsChanged( BOARD& aBoard ) override;
     virtual void OnBoardItemChanged( BOARD& aBoard, BOARD_ITEM* aBoardItem ) override;
-    virtual void OnBoardItemsChanged( BOARD& aBoard, std::vector<BOARD_ITEM*>& aBoardItems ) override;
+    virtual void OnBoardItemsChanged( BOARD& aBoard,
+                                      std::vector<BOARD_ITEM*>& aBoardItems ) override;
     virtual void OnBoardHighlightNetChanged( BOARD& aBoard ) override;
 
+protected:
+    virtual void onClose( wxCloseEvent& aEvent ) override;
+
 private:
     struct COLUMN_DESC;
     class LIST_ITEM;
diff --git a/pcbnew/dialogs/dialog_net_inspector_base.cpp b/pcbnew/dialogs/dialog_net_inspector_base.cpp
index 8f1799a77e..6aab43e88e 100644
--- a/pcbnew/dialogs/dialog_net_inspector_base.cpp
+++ b/pcbnew/dialogs/dialog_net_inspector_base.cpp
@@ -73,16 +73,16 @@ DIALOG_NET_INSPECTOR_BASE::DIALOG_NET_INSPECTOR_BASE( wxWindow* parent, wxWindow
 	bSizerListButtons = new wxBoxSizer( wxHORIZONTAL );
 
 	m_addNet = new STD_BITMAP_BUTTON( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, wxBU_AUTODRAW|0 );
-	bSizerListButtons->Add( m_addNet, 0, wxTOP|wxBOTTOM|wxLEFT, 5 );
+	bSizerListButtons->Add( m_addNet, 0, wxALIGN_CENTER_VERTICAL|wxLEFT, 5 );
 
 	m_renameNet = new STD_BITMAP_BUTTON( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, wxBU_AUTODRAW|0 );
-	bSizerListButtons->Add( m_renameNet, 0, wxTOP|wxBOTTOM|wxLEFT, 5 );
+	bSizerListButtons->Add( m_renameNet, 0, wxALIGN_CENTER_VERTICAL|wxLEFT, 5 );
 
 
 	bSizerListButtons->Add( 20, 0, 0, wxEXPAND, 5 );
 
 	m_deleteNet = new STD_BITMAP_BUTTON( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, wxBU_AUTODRAW|0 );
-	bSizerListButtons->Add( m_deleteNet, 0, wxALL, 5 );
+	bSizerListButtons->Add( m_deleteNet, 0, wxALIGN_CENTER_VERTICAL, 5 );
 
 
 	bSizerListButtons->Add( 0, 0, 1, wxEXPAND, 5 );
@@ -101,6 +101,7 @@ DIALOG_NET_INSPECTOR_BASE::DIALOG_NET_INSPECTOR_BASE( wxWindow* parent, wxWindow
 	this->Centre( wxBOTH );
 
 	// Connect Events
+	this->Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( DIALOG_NET_INSPECTOR_BASE::onClose ) );
 	m_textCtrlFilter->Connect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( DIALOG_NET_INSPECTOR_BASE::onFilterChange ), NULL, this );
 	m_cbShowZeroPad->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DIALOG_NET_INSPECTOR_BASE::onFilterChange ), NULL, this );
 	m_groupBy->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DIALOG_NET_INSPECTOR_BASE::onFilterChange ), NULL, this );
@@ -118,6 +119,7 @@ DIALOG_NET_INSPECTOR_BASE::DIALOG_NET_INSPECTOR_BASE( wxWindow* parent, wxWindow
 DIALOG_NET_INSPECTOR_BASE::~DIALOG_NET_INSPECTOR_BASE()
 {
 	// Disconnect Events
+	this->Disconnect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( DIALOG_NET_INSPECTOR_BASE::onClose ) );
 	m_textCtrlFilter->Disconnect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( DIALOG_NET_INSPECTOR_BASE::onFilterChange ), NULL, this );
 	m_cbShowZeroPad->Disconnect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DIALOG_NET_INSPECTOR_BASE::onFilterChange ), NULL, this );
 	m_groupBy->Disconnect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DIALOG_NET_INSPECTOR_BASE::onFilterChange ), NULL, this );
diff --git a/pcbnew/dialogs/dialog_net_inspector_base.fbp b/pcbnew/dialogs/dialog_net_inspector_base.fbp
index 6bf5f87b93..8bc77dbb7f 100644
--- a/pcbnew/dialogs/dialog_net_inspector_base.fbp
+++ b/pcbnew/dialogs/dialog_net_inspector_base.fbp
@@ -56,6 +56,7 @@
             <property name="window_extra_style"></property>
             <property name="window_name"></property>
             <property name="window_style"></property>
+            <event name="OnClose">onClose</event>
             <object class="wxBoxSizer" expanded="1">
                 <property name="minimum_size"></property>
                 <property name="name">bSizerMain</property>
@@ -522,14 +523,14 @@
                     <property name="border">5</property>
                     <property name="flag">wxEXPAND|wxALL</property>
                     <property name="proportion">0</property>
-                    <object class="wxBoxSizer" expanded="0">
+                    <object class="wxBoxSizer" expanded="1">
                         <property name="minimum_size"></property>
                         <property name="name">bSizerListButtons</property>
                         <property name="orient">wxHORIZONTAL</property>
                         <property name="permission">none</property>
                         <object class="sizeritem" expanded="0">
                             <property name="border">5</property>
-                            <property name="flag">wxTOP|wxBOTTOM|wxLEFT</property>
+                            <property name="flag">wxALIGN_CENTER_VERTICAL|wxLEFT</property>
                             <property name="proportion">0</property>
                             <object class="wxBitmapButton" expanded="0">
                                 <property name="BottomDockable">1</property>
@@ -603,7 +604,7 @@
                         </object>
                         <object class="sizeritem" expanded="0">
                             <property name="border">5</property>
-                            <property name="flag">wxTOP|wxBOTTOM|wxLEFT</property>
+                            <property name="flag">wxALIGN_CENTER_VERTICAL|wxLEFT</property>
                             <property name="proportion">0</property>
                             <object class="wxBitmapButton" expanded="0">
                                 <property name="BottomDockable">1</property>
@@ -687,7 +688,7 @@
                         </object>
                         <object class="sizeritem" expanded="0">
                             <property name="border">5</property>
-                            <property name="flag">wxALL</property>
+                            <property name="flag">wxALIGN_CENTER_VERTICAL</property>
                             <property name="proportion">0</property>
                             <object class="wxBitmapButton" expanded="0">
                                 <property name="BottomDockable">1</property>
diff --git a/pcbnew/dialogs/dialog_net_inspector_base.h b/pcbnew/dialogs/dialog_net_inspector_base.h
index 9554cdee39..9fb51343f4 100644
--- a/pcbnew/dialogs/dialog_net_inspector_base.h
+++ b/pcbnew/dialogs/dialog_net_inspector_base.h
@@ -55,6 +55,7 @@ class DIALOG_NET_INSPECTOR_BASE : public DIALOG_SHIM
 		wxButton* m_ReportButt;
 
 		// Virtual event handlers, override them in your derived class
+		virtual void onClose( wxCloseEvent& event ) { event.Skip(); }
 		virtual void onFilterChange( wxCommandEvent& event ) { event.Skip(); }
 		virtual void onSortingChanged( wxDataViewEvent& event ) { event.Skip(); }
 		virtual void onSelChanged( wxDataViewEvent& event ) { event.Skip(); }
diff --git a/pcbnew/dialogs/dialog_track_via_properties.cpp b/pcbnew/dialogs/dialog_track_via_properties.cpp
index 0c2196f067..f2c0eb3c3c 100644
--- a/pcbnew/dialogs/dialog_track_via_properties.cpp
+++ b/pcbnew/dialogs/dialog_track_via_properties.cpp
@@ -2,7 +2,7 @@
  * This program source code file is part of KiCad, a free EDA CAD application.
  *
  * Copyright (C) 2015 CERN
- * Copyright (C) 2018-2022 KiCad Developers, see AUTHORS.txt for contributors.
+ * Copyright (C) 2018-2023 KiCad Developers, see AUTHORS.txt for contributors.
  * @author Maciej Suminski <maciej.suminski@cern.ch>
  *
  * This program is free software; you can redistribute it and/or
@@ -388,7 +388,7 @@ DIALOG_TRACK_VIA_PROPERTIES::DIALOG_TRACK_VIA_PROPERTIES( PCB_BASE_FRAME* aParen
 
     SetupStandardButtons();
 
-    m_frame->Bind( UNITS_CHANGED, &DIALOG_TRACK_VIA_PROPERTIES::onUnitsChanged, this );
+    m_frame->Bind( EDA_EVT_UNITS_CHANGED, &DIALOG_TRACK_VIA_PROPERTIES::onUnitsChanged, this );
     m_netSelector->Bind( NET_SELECTED, &DIALOG_TRACK_VIA_PROPERTIES::onNetSelector, this );
 
     // Now all widgets have the size fixed, call FinishDialogSettings
@@ -398,7 +398,7 @@ DIALOG_TRACK_VIA_PROPERTIES::DIALOG_TRACK_VIA_PROPERTIES( PCB_BASE_FRAME* aParen
 
 DIALOG_TRACK_VIA_PROPERTIES::~DIALOG_TRACK_VIA_PROPERTIES()
 {
-    m_frame->Unbind( UNITS_CHANGED, &DIALOG_TRACK_VIA_PROPERTIES::onUnitsChanged, this );
+    m_frame->Unbind( EDA_EVT_UNITS_CHANGED, &DIALOG_TRACK_VIA_PROPERTIES::onUnitsChanged, this );
 }
 
 
diff --git a/pcbnew/dialogs/panel_setup_text_and_graphics.cpp b/pcbnew/dialogs/panel_setup_text_and_graphics.cpp
index a7efc6b907..3b9a8c8fd3 100644
--- a/pcbnew/dialogs/panel_setup_text_and_graphics.cpp
+++ b/pcbnew/dialogs/panel_setup_text_and_graphics.cpp
@@ -1,7 +1,7 @@
 /*
  * This program source code file is part of KiCad, a free EDA CAD application.
  *
- * Copyright (C) 2018-2022 KiCad Developers, see change_log.txt for contributors.
+ * Copyright (C) 2018-2023 KiCad Developers, see change_log.txt for contributors.
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
@@ -95,7 +95,7 @@ PANEL_SETUP_TEXT_AND_GRAPHICS::PANEL_SETUP_TEXT_AND_GRAPHICS( PAGED_DIALOG* aPar
 
     m_grid->PushEventHandler( new GRID_TRICKS( m_grid ) );
 
-    m_Frame->Bind( UNITS_CHANGED, &PANEL_SETUP_TEXT_AND_GRAPHICS::onUnitsChanged, this );
+    m_Frame->Bind( EDA_EVT_UNITS_CHANGED, &PANEL_SETUP_TEXT_AND_GRAPHICS::onUnitsChanged, this );
 }
 
 
@@ -104,7 +104,7 @@ PANEL_SETUP_TEXT_AND_GRAPHICS::~PANEL_SETUP_TEXT_AND_GRAPHICS()
     // destroy GRID_TRICKS before m_grid.
     m_grid->PopEventHandler( true );
 
-    m_Frame->Unbind( UNITS_CHANGED, &PANEL_SETUP_TEXT_AND_GRAPHICS::onUnitsChanged, this );
+    m_Frame->Unbind( EDA_EVT_UNITS_CHANGED, &PANEL_SETUP_TEXT_AND_GRAPHICS::onUnitsChanged, this );
 }
 
 
@@ -151,8 +151,10 @@ bool PANEL_SETUP_TEXT_AND_GRAPHICS::TransferDataToWindow()
             SET_MILS_CELL( i, COL_TEXT_WIDTH, m_BrdSettings->m_TextSize[ i ].x );
             SET_MILS_CELL( i, COL_TEXT_HEIGHT, m_BrdSettings->m_TextSize[ i ].y );
             SET_MILS_CELL( i, COL_TEXT_THICKNESS, m_BrdSettings->m_TextThickness[ i ] );
-            m_grid->SetCellValue( i, COL_TEXT_ITALIC, m_BrdSettings->m_TextItalic[ i ] ? wxT( "1" ) : wxT( "" ) );
-            m_grid->SetCellValue( i, COL_TEXT_UPRIGHT, m_BrdSettings->m_TextUpright[ i ] ? wxT( "1" ) : wxT( "" ) );
+            m_grid->SetCellValue( i, COL_TEXT_ITALIC,
+                                  m_BrdSettings->m_TextItalic[ i ] ? wxT( "1" ) : wxT( "" ) );
+            m_grid->SetCellValue( i, COL_TEXT_UPRIGHT,
+                                  m_BrdSettings->m_TextUpright[ i ] ? wxT( "1" ) : wxT( "" ) );
 
             auto attr = new wxGridCellAttr;
             attr->SetRenderer( new wxGridCellBoolRenderer() );
diff --git a/pcbnew/dialogs/panel_setup_tracks_and_vias.cpp b/pcbnew/dialogs/panel_setup_tracks_and_vias.cpp
index 52e1016d71..71656d8883 100644
--- a/pcbnew/dialogs/panel_setup_tracks_and_vias.cpp
+++ b/pcbnew/dialogs/panel_setup_tracks_and_vias.cpp
@@ -1,7 +1,7 @@
 /*
  * This program source code file is part of KiCad, a free EDA CAD application.
  *
- * Copyright (C) 2018-2022 KiCad Developers, see change_log.txt for contributors.
+ * Copyright (C) 2018-2023 KiCad Developers, see change_log.txt for contributors.
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
@@ -122,7 +122,7 @@ PANEL_SETUP_TRACKS_AND_VIAS::PANEL_SETUP_TRACKS_AND_VIAS( PAGED_DIALOG* aParent,
         }
     }
 
-    m_Frame->Bind( UNITS_CHANGED, &PANEL_SETUP_TRACKS_AND_VIAS::onUnitsChanged, this );
+    m_Frame->Bind( EDA_EVT_UNITS_CHANGED, &PANEL_SETUP_TRACKS_AND_VIAS::onUnitsChanged, this );
 }
 
 
@@ -133,7 +133,7 @@ PANEL_SETUP_TRACKS_AND_VIAS::~PANEL_SETUP_TRACKS_AND_VIAS()
     m_viaSizesGrid->PopEventHandler( true );
     m_diffPairsGrid->PopEventHandler( true );
 
-    m_Frame->Unbind( UNITS_CHANGED, &PANEL_SETUP_TRACKS_AND_VIAS::onUnitsChanged, this );
+    m_Frame->Unbind( EDA_EVT_UNITS_CHANGED, &PANEL_SETUP_TRACKS_AND_VIAS::onUnitsChanged, this );
 }
 
 
diff --git a/pcbnew/fp_text_grid_table.cpp b/pcbnew/fp_text_grid_table.cpp
index 43ab242935..ba9c64a448 100644
--- a/pcbnew/fp_text_grid_table.cpp
+++ b/pcbnew/fp_text_grid_table.cpp
@@ -69,7 +69,7 @@ FP_TEXT_GRID_TABLE::FP_TEXT_GRID_TABLE( PCB_BASE_FRAME* aFrame ) :
 
     m_eval = std::make_unique<NUMERIC_EVALUATOR>( m_frame->GetUserUnits() );
 
-    m_frame->Bind( UNITS_CHANGED, &FP_TEXT_GRID_TABLE::onUnitsChanged, this );
+    m_frame->Bind( EDA_EVT_UNITS_CHANGED, &FP_TEXT_GRID_TABLE::onUnitsChanged, this );
 }
 
 
@@ -80,7 +80,7 @@ FP_TEXT_GRID_TABLE::~FP_TEXT_GRID_TABLE()
     m_orientationColAttr->DecRef();
     m_layerColAttr->DecRef();
 
-    m_frame->Unbind( UNITS_CHANGED, &FP_TEXT_GRID_TABLE::onUnitsChanged, this );
+    m_frame->Unbind( EDA_EVT_UNITS_CHANGED, &FP_TEXT_GRID_TABLE::onUnitsChanged, this );
 }
 
 
diff --git a/pcbnew/pcb_base_frame.cpp b/pcbnew/pcb_base_frame.cpp
index e4ca369d73..3ba6237c56 100644
--- a/pcbnew/pcb_base_frame.cpp
+++ b/pcbnew/pcb_base_frame.cpp
@@ -4,7 +4,7 @@
  * Copyright (C) 2018 Jean-Pierre Charras, jean-pierre.charras@ujf-grenoble.fr
  * Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
  * Copyright (C) 2011 Wayne Stambaugh <stambaughw@gmail.com>
- * Copyright (C) 1992-2022 KiCad Developers, see AUTHORS.txt for contributors.
+ * Copyright (C) 1992-2023 KiCad Developers, see AUTHORS.txt for contributors.
  * Copyright (C) 2022 CERN
  *
  * This program is free software; you can redistribute it and/or
@@ -70,12 +70,13 @@
 using KIGFX::RENDER_SETTINGS;
 using KIGFX::PCB_RENDER_SETTINGS;
 
-wxDEFINE_EVENT( BOARD_CHANGED, wxCommandEvent );
+wxDEFINE_EVENT( EDA_EVT_BOARD_CHANGED, wxCommandEvent );
 
 PCB_BASE_FRAME::PCB_BASE_FRAME( KIWAY* aKiway, wxWindow* aParent, FRAME_T aFrameType,
                                 const wxString& aTitle, const wxPoint& aPos, const wxSize& aSize,
                                 long aStyle, const wxString& aFrameName ) :
-        EDA_DRAW_FRAME( aKiway, aParent, aFrameType, aTitle, aPos, aSize, aStyle, aFrameName, pcbIUScale ),
+        EDA_DRAW_FRAME( aKiway, aParent, aFrameType, aTitle, aPos, aSize, aStyle, aFrameName,
+                        pcbIUScale ),
         m_pcb( nullptr ),
         m_originTransforms( *this ),
         m_spaceMouse( nullptr )
@@ -226,12 +227,44 @@ void PCB_BASE_FRAME::SetBoard( BOARD* aBoard, PROGRESS_REPORTER* aReporter )
             }
         }
 
-        wxCommandEvent e( BOARD_CHANGED );
+        wxCommandEvent e( EDA_EVT_BOARD_CHANGED );
         ProcessEventLocally( e );
+
+        for( wxEvtHandler* listener : m_boardChangeListeners )
+        {
+            wxCHECK2( listener, continue );
+
+            wxWindow* win = dynamic_cast<wxWindow*>( listener );
+
+            if( win )
+                win->HandleWindowEvent( e );
+            else
+                listener->SafelyProcessEvent( e );
+        }
     }
 }
 
 
+void PCB_BASE_FRAME::AddBoardChangeListener( wxEvtHandler* aListener )
+{
+    auto it = std::find( m_boardChangeListeners.begin(), m_boardChangeListeners.end(), aListener );
+
+    // Don't add duplicate listeners.
+    if( it == m_boardChangeListeners.end() )
+        m_boardChangeListeners.push_back( aListener );
+}
+
+
+void PCB_BASE_FRAME::RemoveBoardChangeListener( wxEvtHandler* aListener )
+{
+    auto it = std::find( m_boardChangeListeners.begin(), m_boardChangeListeners.end(), aListener );
+
+    // Don't add duplicate listeners.
+    if( it != m_boardChangeListeners.end() )
+        m_boardChangeListeners.erase( it );
+}
+
+
 void PCB_BASE_FRAME::AddFootprintToBoard( FOOTPRINT* aFootprint )
 {
     if( aFootprint )
diff --git a/pcbnew/pcb_edit_frame.cpp b/pcbnew/pcb_edit_frame.cpp
index 942bb936d2..1125e4d5cd 100644
--- a/pcbnew/pcb_edit_frame.cpp
+++ b/pcbnew/pcb_edit_frame.cpp
@@ -38,6 +38,7 @@
 #include <dialog_find.h>
 #include <dialog_footprint_properties.h>
 #include <dialogs/dialog_exchange_footprints.h>
+#include <dialogs/dialog_net_inspector.h>
 #include <dialog_board_setup.h>
 #include <invoke_pcb_dialog.h>
 #include <board.h>
@@ -114,6 +115,12 @@
 using namespace std::placeholders;
 
 
+#define INSPECT_DRC_ERROR_DIALOG_NAME   wxT( "InspectDrcErrorDialog" )
+#define INSPECT_CLEARANCE_DIALOG_NAME   wxT( "InspectClearanceDialog" )
+#define INSPECT_CONSTRAINTS_DIALOG_NAME wxT( "InspectConstraintsDialog" )
+#define FOOTPRINT_DIFF_DIALOG_NAME      wxT( "FootprintDiffDialog" )
+
+
 BEGIN_EVENT_TABLE( PCB_EDIT_FRAME, PCB_BASE_FRAME )
     EVT_SOCKET( ID_EDA_SOCKET_EVENT_SERV, PCB_EDIT_FRAME::OnSockRequestServer )
     EVT_SOCKET( ID_EDA_SOCKET_EVENT, PCB_EDIT_FRAME::OnSockRequest )
@@ -183,7 +190,13 @@ PCB_EDIT_FRAME::PCB_EDIT_FRAME( KIWAY* aKiway, wxWindow* aParent ) :
         PCB_BASE_EDIT_FRAME( aKiway, aParent, FRAME_PCB_EDITOR, _( "PCB Editor" ),
                              wxDefaultPosition, wxDefaultSize, KICAD_DEFAULT_DRAWFRAME_STYLE,
                              PCB_EDIT_FRAME_NAME ),
-        m_exportNetlistAction( nullptr ), m_findDialog( nullptr )
+    m_exportNetlistAction( nullptr ),
+    m_findDialog( nullptr ),
+    m_inspectDrcErrorDlg( nullptr ),
+    m_inspectClearanceDlg( nullptr ),
+    m_inspectConstraintsDlg( nullptr ),
+    m_footprintDiffDlg( nullptr ),
+    m_netInspectorDlg( nullptr )
 {
     m_maximizeByDefault = true;
     m_showBorderAndTitleBlock = true;   // true to display sheet references
@@ -407,9 +420,9 @@ PCB_EDIT_FRAME::PCB_EDIT_FRAME( KIWAY* aKiway, wxWindow* aParent ) :
 
     wxFileName appK2S( strK2S, wxT( "kicad2step" ) );
 
-    #ifdef _WIN32
+#ifdef _WIN32
     appK2S.SetExt( wxT( "exe" ) );
-    #endif
+#endif
 
     // Ensure the window is on top
     Raise();
@@ -457,6 +470,11 @@ PCB_EDIT_FRAME::PCB_EDIT_FRAME( KIWAY* aKiway, wxWindow* aParent ) :
         m_eventCounterTimer->Start( 1000 );
     }
 
+    Bind( EDA_EVT_CLOSE_DIALOG_BOOK_REPORTER, &PCB_EDIT_FRAME::onCloseModelessBookReporterDialogs,
+          this );
+    Bind( EDA_EVT_CLOSE_NET_INSPECTOR_DIALOG, &PCB_EDIT_FRAME::onCloseNetInspectorDialog, this );
+    Bind( EDA_EVT_UNITS_CHANGED, &PCB_EDIT_FRAME::onUnitsChanged, this );
+
     m_acceptedExts.emplace( KiCadPcbFileExtension, &PCB_ACTIONS::ddAppendBoard );
     m_acceptedExts.emplace( LegacyPcbFileExtension, &PCB_ACTIONS::ddAppendBoard );
     DragAcceptFiles( true );
@@ -467,17 +485,12 @@ PCB_EDIT_FRAME::~PCB_EDIT_FRAME()
 {
     if( ADVANCED_CFG::GetCfg().m_ShowEventCounters )
     {
-        // Stop the timer during destruction early to avoid potential event race conditions (that do happen on windows)
+        // Stop the timer during destruction early to avoid potential event race conditions (that
+        // do happen on windows)
         m_eventCounterTimer->Stop();
         delete m_eventCounterTimer;
     }
 
-    // Close modeless dialogs
-    wxWindow* open_dlg = wxWindow::FindWindowByName( DIALOG_DRC_WINDOW_NAME );
-
-    if( open_dlg )
-        open_dlg->Close( true );
-
     // Shutdown all running tools
     if( m_toolManager )
         m_toolManager->ShutdownAllTools();
@@ -1059,13 +1072,6 @@ bool PCB_EDIT_FRAME::canCloseWindow( wxCloseEvent& aEvent )
         }
     }
 
-    // Close modeless dialogs.  They're trouble when they get destroyed after the frame and/or
-    // board.
-    wxWindow* open_dlg = wxWindow::FindWindowByName( DIALOG_DRC_WINDOW_NAME );
-
-    if( open_dlg )
-        open_dlg->Close( true );
-
     return PCB_BASE_EDIT_FRAME::canCloseWindow( aEvent );
 }
 
@@ -1082,6 +1088,54 @@ void PCB_EDIT_FRAME::doCloseWindow()
 
     GetCanvas()->StopDrawing();
 
+    // Clean up mode-less dialogs.
+    Unbind( EDA_EVT_CLOSE_DIALOG_BOOK_REPORTER,
+            &PCB_EDIT_FRAME::onCloseModelessBookReporterDialogs, this );
+    Unbind( EDA_EVT_CLOSE_NET_INSPECTOR_DIALOG, &PCB_EDIT_FRAME::onCloseNetInspectorDialog, this );
+    Unbind( EDA_EVT_UNITS_CHANGED, &PCB_EDIT_FRAME::onUnitsChanged, this );
+
+    wxWindow* open_dlg = wxWindow::FindWindowByName( DIALOG_DRC_WINDOW_NAME );
+
+    if( open_dlg )
+        open_dlg->Close( true );
+
+    if( m_findDialog )
+    {
+        m_findDialog->Destroy();
+        m_findDialog = nullptr;
+    }
+
+    if( m_inspectDrcErrorDlg )
+    {
+        m_inspectDrcErrorDlg->Destroy();
+        m_inspectDrcErrorDlg = nullptr;
+    }
+
+    if( m_inspectClearanceDlg )
+    {
+        m_inspectClearanceDlg->Destroy();
+        m_inspectClearanceDlg = nullptr;
+    }
+
+    if( m_inspectConstraintsDlg )
+    {
+        m_inspectConstraintsDlg->Destroy();
+        m_inspectConstraintsDlg = nullptr;
+    }
+
+    if( m_footprintDiffDlg )
+    {
+        m_footprintDiffDlg->Destroy();
+        m_footprintDiffDlg = nullptr;
+    }
+
+    if( m_netInspectorDlg )
+    {
+        RemoveBoardChangeListener( m_netInspectorDlg );
+        m_netInspectorDlg->Destroy();
+        m_netInspectorDlg = nullptr;
+    }
+
     // Delete the auto save file if it exists.
     wxFileName fn = GetBoard()->GetFileName();
 
@@ -2338,3 +2392,100 @@ void PCB_EDIT_FRAME::onSize( wxSizeEvent& aEvent )
     // Skip() is called in the base class.
     EDA_DRAW_FRAME::OnSize( aEvent );
 }
+
+
+DIALOG_BOOK_REPORTER* PCB_EDIT_FRAME::GetInspectDrcErrorDialog()
+{
+    if( !m_inspectDrcErrorDlg )
+        m_inspectDrcErrorDlg = new DIALOG_BOOK_REPORTER( this, INSPECT_DRC_ERROR_DIALOG_NAME,
+                                                         _( "Violation Report" ) );
+
+    return m_inspectDrcErrorDlg;
+}
+
+
+DIALOG_BOOK_REPORTER* PCB_EDIT_FRAME::GetInspectClearanceDialog()
+{
+    if( !m_inspectClearanceDlg )
+        m_inspectClearanceDlg = new DIALOG_BOOK_REPORTER( this, INSPECT_CLEARANCE_DIALOG_NAME,
+                                                          _( "Clearance Report" ) );
+
+    return m_inspectClearanceDlg;
+}
+
+
+DIALOG_BOOK_REPORTER* PCB_EDIT_FRAME::GetInspectConstraintsDialog()
+{
+    if( !m_inspectConstraintsDlg )
+        m_inspectConstraintsDlg = new DIALOG_BOOK_REPORTER( this, INSPECT_CONSTRAINTS_DIALOG_NAME,
+                                                            _( "Constraints Report" ) );
+
+    return m_inspectConstraintsDlg;
+}
+
+
+DIALOG_BOOK_REPORTER* PCB_EDIT_FRAME::GetFootprintDiffDialog()
+{
+    if( !m_footprintDiffDlg )
+        m_footprintDiffDlg = new DIALOG_BOOK_REPORTER( this, FOOTPRINT_DIFF_DIALOG_NAME,
+                                                       _( "Diff Footprint with Library" ) );
+
+    return m_footprintDiffDlg;
+}
+
+
+void PCB_EDIT_FRAME::onCloseModelessBookReporterDialogs( wxCommandEvent& aEvent )
+{
+    if( m_inspectDrcErrorDlg && aEvent.GetString() == INSPECT_DRC_ERROR_DIALOG_NAME )
+    {
+        m_inspectDrcErrorDlg->Destroy();
+        m_inspectDrcErrorDlg = nullptr;
+    }
+    else if( m_inspectClearanceDlg && aEvent.GetString() == INSPECT_CLEARANCE_DIALOG_NAME )
+    {
+        m_inspectClearanceDlg->Destroy();
+        m_inspectClearanceDlg = nullptr;
+    }
+    else if( m_inspectConstraintsDlg && aEvent.GetString() == INSPECT_CONSTRAINTS_DIALOG_NAME )
+    {
+        m_inspectConstraintsDlg->Destroy();
+        m_inspectConstraintsDlg = nullptr;
+    }
+    else if( m_footprintDiffDlg && aEvent.GetString() == INSPECT_CONSTRAINTS_DIALOG_NAME )
+    {
+        m_footprintDiffDlg->Destroy();
+        m_footprintDiffDlg = nullptr;
+    }
+}
+
+
+DIALOG_NET_INSPECTOR* PCB_EDIT_FRAME::GetNetInspectorDialog()
+{
+    if( !m_netInspectorDlg )
+    {
+        m_netInspectorDlg = new DIALOG_NET_INSPECTOR( this );
+        AddBoardChangeListener( m_netInspectorDlg );
+    }
+
+    return m_netInspectorDlg;
+}
+
+
+void PCB_EDIT_FRAME::onCloseNetInspectorDialog( wxCommandEvent& aEvent )
+{
+    if( m_netInspectorDlg )
+    {
+        RemoveBoardChangeListener( m_netInspectorDlg );
+        m_netInspectorDlg->Destroy();
+        m_netInspectorDlg = nullptr;
+    }
+}
+
+
+void PCB_EDIT_FRAME::onUnitsChanged( wxCommandEvent& aEvent )
+{
+    wxCommandEvent evt( EDA_EVT_UNITS_CHANGED );
+
+    if( m_netInspectorDlg )
+        m_netInspectorDlg->HandleWindowEvent( evt );
+}
diff --git a/pcbnew/pcb_edit_frame.h b/pcbnew/pcb_edit_frame.h
index af838614c2..863d68fc0c 100644
--- a/pcbnew/pcb_edit_frame.h
+++ b/pcbnew/pcb_edit_frame.h
@@ -2,7 +2,7 @@
  * This program source code file is part of KiCad, a free EDA CAD application.
  *
  * Copyright (C) 2010 Jean-Pierre Charras, jp.charras@wanadoo.fr
- * Copyright (C) 2010-2022 KiCad Developers, see AUTHORS.txt for contributors.
+ * Copyright (C) 2010-2023 KiCad Developers, see AUTHORS.txt for contributors.
  *
  * This program is free software: you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
@@ -30,6 +30,8 @@ class PCB_SCREEN;
 class BOARD;
 class BOARD_COMMIT;
 class BOARD_ITEM_CONTAINER;
+class DIALOG_BOOK_REPORTER;
+class DIALOG_NET_INSPECTOR;
 class FOOTPRINT;
 class PCB_TRACK;
 class PCB_VIA;
@@ -702,6 +704,16 @@ public:
 
     TOOL_ACTION* GetExportNetlistAction() { return m_exportNetlistAction; }
 
+    DIALOG_BOOK_REPORTER* GetInspectDrcErrorDialog();
+
+    DIALOG_BOOK_REPORTER* GetInspectConstraintsDialog();
+
+    DIALOG_BOOK_REPORTER* GetInspectClearanceDialog();
+
+    DIALOG_BOOK_REPORTER* GetFootprintDiffDialog();
+
+    DIALOG_NET_INSPECTOR* GetNetInspectorDialog();
+
     DECLARE_EVENT_TABLE()
 
 protected:
@@ -805,18 +817,24 @@ protected:
 
     void saveProjectSettings() override;
 
-public:
-    PCB_LAYER_BOX_SELECTOR* m_SelLayerBox;  // a combo box to display and select active layer
+    void onCloseModelessBookReporterDialogs( wxCommandEvent& aEvent );
 
-    wxComboBox* m_SelTrackWidthBox;        // a choice box to display and select current track width
-    wxComboBox* m_SelViaSizeBox;           // a choice box to display and select current via diameter
+    void onCloseNetInspectorDialog( wxCommandEvent& aEvent );
+
+    void onUnitsChanged( wxCommandEvent& aEvent );
+
+public:
+    PCB_LAYER_BOX_SELECTOR* m_SelLayerBox; // a combo box to display and select active layer
+
+    wxComboBox* m_SelTrackWidthBox;      // a choice box to display and select current track width
+    wxComboBox* m_SelViaSizeBox;         // a choice box to display and select current via diameter
 
     bool m_show_layer_manager_tools;
     bool m_show_search;
 
-    bool m_ZoneFillsDirty;               // Board has been modified since last zone fill.
+    bool m_ZoneFillsDirty;          // Board has been modified since last zone fill.
 
-    bool m_probingSchToPcb;              // Recursion guard when synchronizing selection from schematic
+    bool m_probingSchToPcb;         // Recursion guard when synchronizing selection from schematic
 
 private:
     friend struct PCB::IFACE;
@@ -832,6 +850,11 @@ private:
     TOOL_ACTION* m_exportNetlistAction;
 
     DIALOG_FIND* m_findDialog;
+    DIALOG_BOOK_REPORTER* m_inspectDrcErrorDlg;
+    DIALOG_BOOK_REPORTER* m_inspectClearanceDlg;
+    DIALOG_BOOK_REPORTER* m_inspectConstraintsDlg;
+    DIALOG_BOOK_REPORTER* m_footprintDiffDlg;
+    DIALOG_NET_INSPECTOR* m_netInspectorDlg;
 
     /**
      * Keep track of viewport so that track net labels can be adjusted when it changes.
diff --git a/pcbnew/tools/board_inspection_tool.cpp b/pcbnew/tools/board_inspection_tool.cpp
index c024fa5e06..eaf011af48 100644
--- a/pcbnew/tools/board_inspection_tool.cpp
+++ b/pcbnew/tools/board_inspection_tool.cpp
@@ -32,6 +32,7 @@
 #include <drc/drc_engine.h>
 #include <dialogs/dialog_board_statistics.h>
 #include <dialogs/dialog_book_reporter.h>
+#include <dialogs/dialog_net_inspector.h>
 #include <dialogs/panel_setup_rules_base.h>
 #include <string_utils.h>
 #include <tools/board_inspection_tool.h>
@@ -44,12 +45,6 @@
 #include <pad.h>
 
 
-#define LIST_NETS_DIALOG_NAME wxT( "ListNetsDialog" )
-#define INSPECT_CLEARANCE_DIALOG_NAME wxT( "InspectClearanceDialog" )
-#define INSPECT_CONSTRAINTS_DIALOG_NAME wxT( "InspectConstraintsDialog" )
-#define DIFF_FOOTPRINTS_DIALOG_NAME wxT( "DiffFootprintsDialog" )
-
-
 BOARD_INSPECTION_TOOL::BOARD_INSPECTION_TOOL() :
         PCB_TOOL_BASE( "pcbnew.InspectionTool" ),
         m_frame( nullptr )
@@ -198,7 +193,7 @@ void BOARD_INSPECTION_TOOL::reportHeader( const wxString& aTitle, BOARD_ITEM* a,
 {
     r->Report( wxT( "<h7>" ) + EscapeHTML( aTitle ) + wxT( "</h7>" ) );
     r->Report( wxT( "<ul><li>" ) + EscapeHTML( getItemDescription( a ) ) + wxT( "</li>" )
-                 + wxT( "<li>" ) + EscapeHTML( getItemDescription( b ) ) + wxT( "</li></ul>" ) );
+               + wxT( "<li>" ) + EscapeHTML( getItemDescription( b ) ) + wxT( "</li></ul>" ) );
 }
 
 
@@ -209,8 +204,8 @@ void BOARD_INSPECTION_TOOL::reportHeader( const wxString& aTitle, BOARD_ITEM* a,
 
     r->Report( wxT( "<h7>" ) + EscapeHTML( aTitle ) + wxT( "</h7>" ) );
     r->Report( wxT( "<ul><li>" ) + EscapeHTML( layerStr ) + wxT( "</li>" )
-                 + wxT( "<li>" ) + EscapeHTML( getItemDescription( a ) ) + wxT( "</li>" )
-                 + wxT( "<li>" ) + EscapeHTML( getItemDescription( b ) ) + wxT( "</li></ul>" ) );
+               + wxT( "<li>" ) + EscapeHTML( getItemDescription( a ) ) + wxT( "</li>" )
+               + wxT( "<li>" ) + EscapeHTML( getItemDescription( b ) ) + wxT( "</li></ul>" ) );
 }
 
 
@@ -248,14 +243,12 @@ void BOARD_INSPECTION_TOOL::InspectDRCError( const std::shared_ptr<RC_ITEM>& aDR
     BOARD_CONNECTED_ITEM* ac = dynamic_cast<BOARD_CONNECTED_ITEM*>( a );
     BOARD_CONNECTED_ITEM* bc = dynamic_cast<BOARD_CONNECTED_ITEM*>( b );
     PCB_LAYER_ID          layer = m_frame->GetActiveLayer();
-    wxWindow*             window = wxWindow::FindWindowByName( INSPECT_CLEARANCE_DIALOG_NAME );
-    DIALOG_BOOK_REPORTER* dialog = dynamic_cast<DIALOG_BOOK_REPORTER*>( window );
 
-    if( !dialog )
-    {
-        dialog = new DIALOG_BOOK_REPORTER( m_frame, INSPECT_CLEARANCE_DIALOG_NAME,
-                                           _( "Violation Report" ) );
-    }
+    wxCHECK( m_frame, /* void */ );
+
+    DIALOG_BOOK_REPORTER* dialog = m_frame->GetInspectDrcErrorDialog();
+
+    wxCHECK( dialog, /* void */ );
 
     WX_HTML_REPORT_BOX* r = nullptr;
     bool                compileError = false;
@@ -478,7 +471,8 @@ void BOARD_INSPECTION_TOOL::InspectDRCError( const std::shared_ptr<RC_ITEM>& aDR
         {
             layer = b->GetLayer();
         }
-        else if( a->Type() == PCB_PAD_T && static_cast<PAD*>( a )->GetAttribute() == PAD_ATTRIB::SMD )
+        else if( a->Type() == PCB_PAD_T
+               && static_cast<PAD*>( a )->GetAttribute() == PAD_ATTRIB::SMD )
         {
             PAD* pad = static_cast<PAD*>( a );
 
@@ -487,7 +481,8 @@ void BOARD_INSPECTION_TOOL::InspectDRCError( const std::shared_ptr<RC_ITEM>& aDR
             else
                 layer = B_Cu;
         }
-        else if( b->Type() == PCB_PAD_T && static_cast<PAD*>( a )->GetAttribute() == PAD_ATTRIB::SMD )
+        else if( b->Type() == PCB_PAD_T
+               && static_cast<PAD*>( a )->GetAttribute() == PAD_ATTRIB::SMD )
         {
             PAD* pad = static_cast<PAD*>( b );
 
@@ -553,7 +548,12 @@ void BOARD_INSPECTION_TOOL::InspectDRCError( const std::shared_ptr<RC_ITEM>& aDR
 
 int BOARD_INSPECTION_TOOL::InspectClearance( const TOOL_EVENT& aEvent )
 {
+    wxCHECK( m_frame, 0 );
+
     PCB_SELECTION_TOOL*  selTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
+
+    wxCHECK( selTool, 0 );
+
     const PCB_SELECTION& selection = selTool->GetSelection();
 
     if( selection.Size() != 2 )
@@ -596,14 +596,9 @@ int BOARD_INSPECTION_TOOL::InspectClearance( const TOOL_EVENT& aEvent )
     // a and b could be null after group tests above.
     wxCHECK( a && b, 0 );
 
-    wxWindow*             window = wxWindow::FindWindowByName( INSPECT_CLEARANCE_DIALOG_NAME );
-    DIALOG_BOOK_REPORTER* dialog = dynamic_cast<DIALOG_BOOK_REPORTER*>( window );
+    DIALOG_BOOK_REPORTER* dialog = m_frame->GetInspectClearanceDialog();
 
-    if( !dialog )
-    {
-        dialog = new DIALOG_BOOK_REPORTER( m_frame, INSPECT_CLEARANCE_DIALOG_NAME,
-                                           _( "Clearance Report" ) );
-    }
+    wxCHECK( dialog, 0 );
 
     dialog->DeleteAllPages();
 
@@ -722,8 +717,8 @@ int BOARD_INSPECTION_TOOL::InspectClearance( const TOOL_EVENT& aEvent )
                     clearance = constraint.m_Value.Min();
 
                     r->Report( "" );
-                    r->Report( wxString::Format( _( "Overridden by larger physical hole clearance from %s;"
-                                                    "clearance: %s." ),
+                    r->Report( wxString::Format( _( "Overridden by larger physical hole clearance "
+                                                    "from %s; clearance: %s." ),
                                                  EscapeHTML( constraint.GetName() ),
                                                  m_frame->StringFromValue( clearance, true ) ) );
                 }
@@ -781,7 +776,8 @@ int BOARD_INSPECTION_TOOL::InspectClearance( const TOOL_EVENT& aEvent )
 
             if( clearance < 0 )
             {
-                r->Report( wxString::Format( _( "Resolved clearance: %s; clearance will not be tested." ),
+                r->Report( wxString::Format( _( "Resolved clearance: %s; clearance will not be "
+                                                "tested." ),
                                              m_frame->StringFromValue( clearance, true ) ) );
             }
             else
@@ -817,7 +813,8 @@ int BOARD_INSPECTION_TOOL::InspectClearance( const TOOL_EVENT& aEvent )
             r->Report( "" );
             r->Report( "" );
             r->Report( "" );
-            reportHeader( _( "Diff pair max uncoupled length resolution for:" ), ac, bc, active, r );
+            reportHeader( _( "Diff pair max uncoupled length resolution for:" ), ac, bc,
+                          active, r );
 
             if( !drcEngine.HasRulesForConstraintType( DIFF_PAIR_MAX_UNCOUPLED_CONSTRAINT ) )
             {
@@ -1132,7 +1129,12 @@ int BOARD_INSPECTION_TOOL::InspectConstraints( const TOOL_EVENT& aEvent )
 {
 #define EVAL_RULES( constraint, a, b, layer, r ) drcEngine.EvalRules( constraint, a, b, layer, r )
 
+    wxCHECK( m_frame, 0 );
+
     PCB_SELECTION_TOOL*  selTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
+
+    wxCHECK( selTool, 0 );
+
     const PCB_SELECTION& selection = selTool->GetSelection();
 
     if( selection.Size() != 1 )
@@ -1141,14 +1143,9 @@ int BOARD_INSPECTION_TOOL::InspectConstraints( const TOOL_EVENT& aEvent )
         return 0;
     }
 
-    wxWindow*             window = wxWindow::FindWindowByName( INSPECT_CONSTRAINTS_DIALOG_NAME );
-    DIALOG_BOOK_REPORTER* dialog = dynamic_cast<DIALOG_BOOK_REPORTER*>( window );
+    DIALOG_BOOK_REPORTER* dialog = m_frame->GetInspectConstraintsDialog();
 
-    if( !dialog )
-    {
-        dialog = new DIALOG_BOOK_REPORTER( m_frame, INSPECT_CONSTRAINTS_DIALOG_NAME,
-                                           _( "Constraints Report" ) );
-    }
+    wxCHECK( dialog, 0 );
 
     dialog->DeleteAllPages();
 
@@ -1287,7 +1284,8 @@ int BOARD_INSPECTION_TOOL::InspectConstraints( const TOOL_EVENT& aEvent )
         r->Report( "" );
         r->Report( _( "Report may be incomplete: some footprint courtyards are malformed." )
                    + wxS( "&nbsp;&nbsp;" )
-                   + wxS( "<a href='$DRC'>" ) + _( "Run DRC for a full analysis." ) + wxS( "</a>" ) );
+                   + wxS( "<a href='$DRC'>" ) + _( "Run DRC for a full analysis." )
+                   + wxS( "</a>" ) );
     }
 
     r->Report( "" );
@@ -1310,7 +1308,8 @@ int BOARD_INSPECTION_TOOL::InspectConstraints( const TOOL_EVENT& aEvent )
         r->Report( "" );
         r->Report( _( "Report may be incomplete: some footprint courtyards are malformed." )
                    + wxS( "&nbsp;&nbsp;" )
-                   + wxS( "<a href='$DRC'>" ) + _( "Run DRC for a full analysis." ) + wxS( "</a>" ) );
+                   + wxS( "<a href='$DRC'>" ) + _( "Run DRC for a full analysis." )
+                   + wxS( "</a>" ) );
     }
 
     drcEngine.ProcessAssertions( item, []( const DRC_CONSTRAINT* c ){}, r );
@@ -1324,7 +1323,12 @@ int BOARD_INSPECTION_TOOL::InspectConstraints( const TOOL_EVENT& aEvent )
 
 int BOARD_INSPECTION_TOOL::DiffFootprint( const TOOL_EVENT& aEvent )
 {
+    wxCHECK( m_frame, 0 );
+
     PCB_SELECTION_TOOL*  selTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
+
+    wxCHECK( selTool, 0 );
+
     const PCB_SELECTION& selection = selTool->RequestSelection(
             []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
             {
@@ -1341,18 +1345,14 @@ int BOARD_INSPECTION_TOOL::DiffFootprint( const TOOL_EVENT& aEvent )
 
     if( selection.Size() != 1 )
     {
-        m_frame->ShowInfoBarError( _( "Select a footprint to diff against its library equivalent." ) );
+        m_frame->ShowInfoBarError( _( "Select a footprint to diff against its library "
+                                      "equivalent." ) );
         return 0;
     }
 
-    wxWindow*             window = wxWindow::FindWindowByName( DIFF_FOOTPRINTS_DIALOG_NAME );
-    DIALOG_BOOK_REPORTER* dialog = dynamic_cast<DIALOG_BOOK_REPORTER*>( window );
+    DIALOG_BOOK_REPORTER* dialog = m_frame->GetFootprintDiffDialog();
 
-    if( !dialog )
-    {
-        dialog = new DIALOG_BOOK_REPORTER( m_frame, DIFF_FOOTPRINTS_DIALOG_NAME,
-                                           _( "Diff Footprint with Library" ) );
-    }
+    wxCHECK( dialog, 0 );
 
     dialog->DeleteAllPages();
 
@@ -1387,14 +1387,16 @@ int BOARD_INSPECTION_TOOL::DiffFootprint( const TOOL_EVENT& aEvent )
     {
         r->Report( _( "The library is not included in the current configuration." )
                    + wxS( "&nbsp;&nbsp;&nbsp" )
-                   + wxS( "<a href='$CONFIG'>" ) + _( "Manage Footprint Libraries" ) + wxS( "</a>" ) );
+                   + wxS( "<a href='$CONFIG'>" ) + _( "Manage Footprint Libraries" )
+                   + wxS( "</a>" ) );
 
     }
     else if( !libTable->HasLibrary( libName, true ) )
     {
         r->Report( _( "The library is not enabled in the current configuration." )
                    + wxS( "&nbsp;&nbsp;&nbsp" )
-                   + wxS( "<a href='$CONFIG'>" ) + _( "Manage Footprint Libraries" ) + wxS( "</a>" ) );
+                   + wxS( "<a href='$CONFIG'>" ) + _( "Manage Footprint Libraries" )
+                   + wxS( "</a>" ) );
 
     }
     else
@@ -1870,11 +1872,11 @@ void BOARD_INSPECTION_TOOL::calculateSelectionRatsnest( const VECTOR2I& aDelta )
 
 int BOARD_INSPECTION_TOOL::ListNets( const TOOL_EVENT& aEvent )
 {
-    wxWindow*             window = wxWindow::FindWindowByName( LIST_NETS_DIALOG_NAME );
-    DIALOG_NET_INSPECTOR* dialog = dynamic_cast<DIALOG_NET_INSPECTOR*>( window );
+    wxCHECK( m_frame, 0 );
 
-    if( !dialog )
-        dialog = new DIALOG_NET_INSPECTOR( m_frame, LIST_NETS_DIALOG_NAME );
+    DIALOG_NET_INSPECTOR* dialog = m_frame->GetNetInspectorDialog();
+
+    wxCHECK( dialog, 0 );
 
     dialog->Raise();
     dialog->Show( true );
diff --git a/pcbnew/widgets/pcb_search_pane.cpp b/pcbnew/widgets/pcb_search_pane.cpp
index 02f7aa3c0c..de8f8b854d 100644
--- a/pcbnew/widgets/pcb_search_pane.cpp
+++ b/pcbnew/widgets/pcb_search_pane.cpp
@@ -1,7 +1,7 @@
 /*
  * This program source code file is part of KiCad, a free EDA CAD application.
  *
- * Copyright (C) 2022 KiCad Developers, see AUTHORS.txt for contributors.
+ * Copyright (C) 2022-2023 KiCad Developers, see AUTHORS.txt for contributors.
  *
  * This program is free software: you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
@@ -30,10 +30,12 @@ PCB_SEARCH_PANE::PCB_SEARCH_PANE( PCB_EDIT_FRAME* aFrame ) :
     if( m_brd != nullptr )
         m_brd->AddListener( this );
 
-    m_pcbFrame->Connect( UNITS_CHANGED, wxCommandEventHandler( PCB_SEARCH_PANE::onUnitsChanged ),
+    m_pcbFrame->Connect( EDA_EVT_UNITS_CHANGED,
+                         wxCommandEventHandler( PCB_SEARCH_PANE::onUnitsChanged ),
                          nullptr, this );
 
-    m_pcbFrame->Connect( BOARD_CHANGED, wxCommandEventHandler( PCB_SEARCH_PANE::onBoardChanged ),
+    m_pcbFrame->Connect( EDA_EVT_BOARD_CHANGED,
+                         wxCommandEventHandler( PCB_SEARCH_PANE::onBoardChanged ),
                          nullptr, this );
 
     wxFont infoFont = KIUI::GetDockedPaneFont( this );
@@ -49,9 +51,11 @@ PCB_SEARCH_PANE::PCB_SEARCH_PANE( PCB_EDIT_FRAME* aFrame ) :
 
 PCB_SEARCH_PANE::~PCB_SEARCH_PANE()
 {
-    m_pcbFrame->Disconnect( UNITS_CHANGED, wxCommandEventHandler( PCB_SEARCH_PANE::onUnitsChanged ),
+    m_pcbFrame->Disconnect( EDA_EVT_UNITS_CHANGED,
+                            wxCommandEventHandler( PCB_SEARCH_PANE::onUnitsChanged ),
                             nullptr, this );
-    m_pcbFrame->Disconnect( BOARD_CHANGED, wxCommandEventHandler( PCB_SEARCH_PANE::onBoardChanged ),
+    m_pcbFrame->Disconnect( EDA_EVT_BOARD_CHANGED,
+                            wxCommandEventHandler( PCB_SEARCH_PANE::onBoardChanged ),
                             nullptr, this );
 }
 
@@ -137,4 +141,4 @@ void PCB_SEARCH_PANE::OnBoardItemsChanged( BOARD& aBoard, std::vector<BOARD_ITEM
 
 void PCB_SEARCH_PANE::OnBoardHighlightNetChanged( BOARD& aBoard )
 {
-}
\ No newline at end of file
+}