diff --git a/Makefile.in b/Makefile.in
index 1ac008bef6..2359e53008 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -3827,6 +3827,7 @@ COND_USE_GUI_1_ALL_GUI_HEADERS = \
wx/creddlg.h \
wx/bmpbndl.h \
wx/filedlgcustomize.h \
+ wx/compositebookctrl.h \
$(LOWLEVEL_HDR) \
$(GUI_CORE_HEADERS) \
wx/mediactrl.h \
diff --git a/build/bakefiles/files.bkl b/build/bakefiles/files.bkl
index 470247f1ad..18a596c268 100644
--- a/build/bakefiles/files.bkl
+++ b/build/bakefiles/files.bkl
@@ -1317,6 +1317,7 @@ IMPORTANT: please read docs/tech/tn0016.txt before modifying this file!
wx/creddlg.h
wx/bmpbndl.h
wx/filedlgcustomize.h
+ wx/compositebookctrl.h
diff --git a/build/cmake/files.cmake b/build/cmake/files.cmake
index 4c81a1a2b1..514016d530 100644
--- a/build/cmake/files.cmake
+++ b/build/cmake/files.cmake
@@ -1218,6 +1218,7 @@ set(GUI_CMN_HDR
wx/generic/animate.h
wx/bmpbndl.h
wx/filedlgcustomize.h
+ wx/compositebookctrl.h
)
set(UNIX_SRC
diff --git a/build/files b/build/files
index 7246a8e599..6ad7a8fc82 100644
--- a/build/files
+++ b/build/files
@@ -983,6 +983,7 @@ GUI_CMN_HDR =
wx/combo.h
wx/combobox.h
wx/commandlinkbutton.h
+ wx/compositebookctrl.h
wx/compositewin.h
wx/control.h
wx/creddlg.h
diff --git a/build/msw/wx_core.vcxproj b/build/msw/wx_core.vcxproj
index 5956773327..98ded50540 100644
--- a/build/msw/wx_core.vcxproj
+++ b/build/msw/wx_core.vcxproj
@@ -1516,6 +1516,7 @@
+
diff --git a/build/msw/wx_core.vcxproj.filters b/build/msw/wx_core.vcxproj.filters
index b7f990fcc8..dfe24b3836 100644
--- a/build/msw/wx_core.vcxproj.filters
+++ b/build/msw/wx_core.vcxproj.filters
@@ -753,6 +753,9 @@
MSW Sources
+
+ MSW Sources
+
MSW Sources
@@ -1074,9 +1077,6 @@
Common Sources
-
- MSW Sources
-
@@ -1207,6 +1207,9 @@
Common Headers
+
+ Common Headers
+
Common Headers
diff --git a/include/wx/aui/auibook.h b/include/wx/aui/auibook.h
index f52e5e82c5..29e0cf3a74 100644
--- a/include/wx/aui/auibook.h
+++ b/include/wx/aui/auibook.h
@@ -23,8 +23,7 @@
#include "wx/aui/tabart.h"
#include "wx/aui/framemanager.h"
-#include "wx/bookctrl.h"
-#include "wx/containr.h"
+#include "wx/compositebookctrl.h"
class wxAuiNotebook;
@@ -244,7 +243,7 @@ protected:
-class WXDLLIMPEXP_AUI wxAuiNotebook : public wxNavigationEnabled
+class WXDLLIMPEXP_AUI wxAuiNotebook : public wxCompositeBookCtrlBase
{
public:
diff --git a/include/wx/choicebk.h b/include/wx/choicebk.h
index 65c04b0266..7425433433 100644
--- a/include/wx/choicebk.h
+++ b/include/wx/choicebk.h
@@ -15,9 +15,8 @@
#if wxUSE_CHOICEBOOK
-#include "wx/bookctrl.h"
+#include "wx/compositebookctrl.h"
#include "wx/choice.h"
-#include "wx/containr.h"
class WXDLLIMPEXP_FWD_CORE wxChoice;
@@ -36,7 +35,7 @@ wxDECLARE_EXPORTED_EVENT( WXDLLIMPEXP_CORE, wxEVT_CHOICEBOOK_PAGE_CHANGING, wxBo
// wxChoicebook
// ----------------------------------------------------------------------------
-class WXDLLIMPEXP_CORE wxChoicebook : public wxNavigationEnabled
+class WXDLLIMPEXP_CORE wxChoicebook : public wxCompositeBookCtrlBase
{
public:
wxChoicebook() { }
diff --git a/include/wx/compositebookctrl.h b/include/wx/compositebookctrl.h
new file mode 100644
index 0000000000..78e63e821f
--- /dev/null
+++ b/include/wx/compositebookctrl.h
@@ -0,0 +1,33 @@
+///////////////////////////////////////////////////////////////////////////////
+// Name: wx/custombookctrl.h
+// Purpose: Helper for wxBookCtrlBase subclasses composed of several windows
+// Author: Vadim Zeitlin
+// Created: 2023-01-29
+// Copyright: (c) 2023 Vadim Zeitlin
+// Licence: wxWindows licence
+///////////////////////////////////////////////////////////////////////////////
+
+#ifndef _WX_COMPOSITEBOOKCTRL_H_
+#define _WX_COMPOSITEBOOKCTRL_H_
+
+#include "wx/bookctrl.h"
+
+#if wxUSE_BOOKCTRL
+
+#include "wx/containr.h"
+
+// ----------------------------------------------------------------------------
+// wxCompositeBookCtrlBase
+// ----------------------------------------------------------------------------
+
+// This class is specifically DLL-exported, even though it's trivial, in order
+// to ensure that there is only a single copy of it in the wx DLL.
+class WXDLLIMPEXP_CORE wxCompositeBookCtrlBase : public wxNavigationEnabled
+{
+public:
+ wxCompositeBookCtrlBase();
+};
+
+#endif // wxUSE_BOOKCTRL
+
+#endif // _WX_COMPOSITEBOOKCTRL_H_
diff --git a/include/wx/listbook.h b/include/wx/listbook.h
index cfd20a4479..0ad9e6cd0e 100644
--- a/include/wx/listbook.h
+++ b/include/wx/listbook.h
@@ -15,8 +15,7 @@
#if wxUSE_LISTBOOK
-#include "wx/bookctrl.h"
-#include "wx/containr.h"
+#include "wx/compositebookctrl.h"
class WXDLLIMPEXP_FWD_CORE wxListView;
class WXDLLIMPEXP_FWD_CORE wxListEvent;
@@ -36,7 +35,7 @@ wxDECLARE_EXPORTED_EVENT( WXDLLIMPEXP_CORE, wxEVT_LISTBOOK_PAGE_CHANGING, wxBook
// wxListbook
// ----------------------------------------------------------------------------
-class WXDLLIMPEXP_CORE wxListbook : public wxNavigationEnabled
+class WXDLLIMPEXP_CORE wxListbook : public wxCompositeBookCtrlBase
{
public:
wxListbook() { }
diff --git a/include/wx/simplebook.h b/include/wx/simplebook.h
index c9b6b2ac1d..e4c754f8f6 100644
--- a/include/wx/simplebook.h
+++ b/include/wx/simplebook.h
@@ -10,11 +10,10 @@
#ifndef _WX_SIMPLEBOOK_H_
#define _WX_SIMPLEBOOK_H_
-#include "wx/bookctrl.h"
+#include "wx/compositebookctrl.h"
#if wxUSE_BOOKCTRL
-#include "wx/containr.h"
#include "wx/vector.h"
// ----------------------------------------------------------------------------
@@ -23,7 +22,7 @@
// NB: This class doesn't use DLL export declaration as it's fully inline.
-class wxSimplebook : public wxNavigationEnabled
+class wxSimplebook : public wxCompositeBookCtrlBase
{
public:
wxSimplebook()
diff --git a/include/wx/toolbook.h b/include/wx/toolbook.h
index 757033da0b..6cd35b5fab 100644
--- a/include/wx/toolbook.h
+++ b/include/wx/toolbook.h
@@ -15,8 +15,7 @@
#if wxUSE_TOOLBOOK
-#include "wx/bookctrl.h"
-#include "wx/containr.h"
+#include "wx/compositebookctrl.h"
class WXDLLIMPEXP_FWD_CORE wxToolBarBase;
class WXDLLIMPEXP_FWD_CORE wxCommandEvent;
@@ -35,7 +34,7 @@ wxDECLARE_EXPORTED_EVENT( WXDLLIMPEXP_CORE, wxEVT_TOOLBOOK_PAGE_CHANGING, wxBook
// wxToolbook
// ----------------------------------------------------------------------------
-class WXDLLIMPEXP_CORE wxToolbook : public wxNavigationEnabled
+class WXDLLIMPEXP_CORE wxToolbook : public wxCompositeBookCtrlBase
{
public:
wxToolbook()
diff --git a/include/wx/treebook.h b/include/wx/treebook.h
index 7901306ee2..e25f3f0b59 100644
--- a/include/wx/treebook.h
+++ b/include/wx/treebook.h
@@ -15,8 +15,7 @@
#if wxUSE_TREEBOOK
-#include "wx/bookctrl.h"
-#include "wx/containr.h"
+#include "wx/compositebookctrl.h"
#include "wx/treebase.h" // for wxTreeItemId
#include "wx/vector.h"
@@ -29,7 +28,7 @@ class WXDLLIMPEXP_FWD_CORE wxTreeEvent;
// wxTreebook
// ----------------------------------------------------------------------------
-class WXDLLIMPEXP_CORE wxTreebook : public wxNavigationEnabled
+class WXDLLIMPEXP_CORE wxTreebook : public wxCompositeBookCtrlBase
{
public:
// Constructors and such
diff --git a/src/common/bookctrl.cpp b/src/common/bookctrl.cpp
index 6cf4bff9da..dcb433c3b0 100644
--- a/src/common/bookctrl.cpp
+++ b/src/common/bookctrl.cpp
@@ -22,9 +22,9 @@
#if wxUSE_BOOKCTRL
-#include "wx/imaglist.h"
+#include "wx/compositebookctrl.h"
-#include "wx/bookctrl.h"
+#include "wx/imaglist.h"
// ============================================================================
// implementation
@@ -557,4 +557,10 @@ int wxBookCtrlBase::DoSetSelection(size_t n, int flags)
wxIMPLEMENT_DYNAMIC_CLASS(wxBookCtrlEvent, wxNotifyEvent);
+// Implement the trivial ctor here to ensure it's emitted here and exported
+// from the DLL instead of having an inline version of it which may result in
+// link errors if it happens to be instantiated both inside and outside of the
+// DLL, see #22805.
+wxCompositeBookCtrlBase::wxCompositeBookCtrlBase() = default;
+
#endif // wxUSE_BOOKCTRL