diff --git a/GUI/Model/Sample/SamplesSet.cpp b/GUI/Model/Sample/SamplesSet.cpp
index 9a56e7d47e774f66bcf4f0481c9c58b4e2765ebf..73404246d2ddb73c93b39fb4029ceb6391fee02f 100644
--- a/GUI/Model/Sample/SamplesSet.cpp
+++ b/GUI/Model/Sample/SamplesSet.cpp
@@ -13,7 +13,6 @@
 //  ************************************************************************************************
 
 #include "GUI/Model/Sample/SamplesSet.h"
-#include "GUI/Model/Sample/SampleItem.h"
 #include "GUI/Support/XML/Backup.h"
 #include "GUI/Support/XML/UtilXML.h"
 
@@ -68,7 +67,7 @@ void SamplesSet::readFrom(QXmlStreamReader* r)
         if (tag == Tag::Sample) {
             auto* t = new SampleItem;
             t->readFrom(r);
-            push_back(t);
+            add_item(t);
             XML::gotoEndElementOfTag(r, tag);
 
             // selected index
diff --git a/GUI/Model/Sample/SamplesSet.h b/GUI/Model/Sample/SamplesSet.h
index 563117767909e02823ebe7334202685c084d1116..8fc7bc09ff6d8165674bfd7a2a87746cc634add4 100644
--- a/GUI/Model/Sample/SamplesSet.h
+++ b/GUI/Model/Sample/SamplesSet.h
@@ -15,28 +15,23 @@
 #ifndef BORNAGAIN_GUI_MODEL_SAMPLE_SAMPLESSET_H
 #define BORNAGAIN_GUI_MODEL_SAMPLE_SAMPLESSET_H
 
-#include "Base/Types/VectorWC.h"
-#include <QObject>
+#include "GUI/Model/Sample/SampleItem.h"
+#include "GUI/Model/Type/SetWithModel.h"
 #include <QXmlStreamReader>
 
 class SampleItem;
 
 //! Main model to hold sample items.
 
-class SamplesSet : public QObject, public VectorWC<SampleItem> {
-    Q_OBJECT
+class SamplesSet : public SetWithModel<SampleItem> {
 public:
     SamplesSet();
-    ~SamplesSet();
+    virtual ~SamplesSet();
 
     void writeTo(QXmlStreamWriter* w) const;
     void readFrom(QXmlStreamReader* r);
 
     QStringList itemNames() const;
-
-signals:
-    void setChanged() const;
-    void currentModified() const;
 };
 
 #endif // BORNAGAIN_GUI_MODEL_SAMPLE_SAMPLESSET_H
diff --git a/GUI/View/List/SamplesQListView.cpp b/GUI/View/List/SamplesQListView.cpp
index 9241954ba823e11392e3056aaeea1a9d0edfa1fe..871f34ab04290389133991d666d814fab87e2a85 100644
--- a/GUI/View/List/SamplesQListView.cpp
+++ b/GUI/View/List/SamplesQListView.cpp
@@ -15,25 +15,22 @@
 #include "GUI/View/List/SamplesQListView.h"
 #include "GUI/Model/Project/ProjectDocument.h"
 #include "GUI/Model/Sample/SamplesSet.h"
-#include "GUI/View/List/SamplesQModel.h"
 #include "GUI/View/Widget/ListItemDelegate.h"
 
-SamplesQListView::SamplesQListView(SamplesQModel* model)
+SamplesQListView::SamplesQListView(SamplesSet* set)
 {
     setMinimumWidth(200);
     setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding);
     setSelectionMode(QAbstractItemView::NoSelection);
     setItemDelegate(new ListItemDelegate(this));
-    setModel(model);
+    setModel(set->model());
 
-    connect(this, &QListView::clicked, [this](const QModelIndex& qi) {
-        gDoc->samplesModifier()->setCurrentIndex(qi.row());
-        emit gDoc->samples()->setChanged();
-    });
+    connect(this, &QListView::clicked,
+            [this, set](const QModelIndex& qi) { set->setCurrentIndex(qi.row()); });
 
     disconnect(gDoc->samples(), &SamplesSet::setChanged, nullptr, nullptr);
-    connect(gDoc->samples(), &SamplesSet::setChanged, [this, model] {
-        setCurrentIndex(model->index(gDoc->samples()->currentIndex(), 0));
+    connect(gDoc->samples(), &SamplesSet::setChanged, [this, set] {
+        setCurrentIndex(set->model()->index(set->currentIndex(), 0));
         repaint();
     });
 
diff --git a/GUI/View/List/SamplesQListView.h b/GUI/View/List/SamplesQListView.h
index c168c6aa45606b37c9a45053b4f06c1717b893e9..0f296d9eb9146f22808b2557969df5809a3cf48a 100644
--- a/GUI/View/List/SamplesQListView.h
+++ b/GUI/View/List/SamplesQListView.h
@@ -17,12 +17,12 @@
 
 #include <QListView>
 
-class SamplesQModel;
+class SamplesSet;
 
 //! List view to select one sample (left side of layer-oriented sample editor)
 class SamplesQListView : public QListView {
 public:
-    SamplesQListView(SamplesQModel*);
+    SamplesQListView(SamplesSet*);
 };
 
 #endif // BORNAGAIN_GUI_VIEW_LIST_SAMPLESQLISTVIEW_H
diff --git a/GUI/View/List/SamplesQModel.cpp b/GUI/View/List/SamplesQModel.cpp
deleted file mode 100644
index da7e8804fa2299c471316ba90c25c211cb7104c0..0000000000000000000000000000000000000000
--- a/GUI/View/List/SamplesQModel.cpp
+++ /dev/null
@@ -1,94 +0,0 @@
-//  ************************************************************************************************
-//
-//  BornAgain: simulate and fit reflection and scattering
-//
-//! @file      GUI/View/List/SamplesQModel.cpp
-//! @brief     Implements class SamplesQModel.
-//!
-//! @homepage  http://www.bornagainproject.org
-//! @license   GNU General Public License v3 or higher (see COPYING)
-//! @copyright Forschungszentrum Jülich GmbH 2021
-//! @authors   Scientific Computing Group at MLZ (see CITATION, AUTHORS)
-//
-//  ************************************************************************************************
-
-#include "GUI/View/List/SamplesQModel.h"
-#include "Base/Util/Assert.h"
-#include "GUI/Model/Project/ProjectDocument.h"
-#include "GUI/Model/Sample/SampleItem.h"
-#include "GUI/Model/Sample/SamplesSet.h"
-#include "GUI/Support/Util/String.h"
-#include "Sample/Multilayer/MultiLayer.h"
-#include <QColor>
-
-SamplesQModel::SamplesQModel(QObject* parent)
-    : QAbstractListModel(parent) // parent needed to ensure end of life
-{
-}
-
-SamplesSet* SamplesQModel::set()
-{
-    return gDoc->samplesModifier();
-}
-
-const SamplesSet* SamplesQModel::set() const
-{
-    return gDoc->samples();
-}
-
-int SamplesQModel::rowCount(const QModelIndex&) const
-{
-    return set()->size();
-}
-
-QVariant SamplesQModel::data(const QModelIndex& index, int role) const
-{
-    if (!index.isValid())
-        return {};
-    size_t row = index.row();
-    if (row >= set()->size())
-        return {};
-    const SampleItem* t = set()->at(row);
-
-    switch (role) {
-    case Qt::DisplayRole:
-        return t->name();
-    case Qt::ToolTipRole:
-        return t->description();
-    case Qt::BackgroundRole:
-        return row == set()->currentIndex() ? QColor(Qt::cyan) : QColor(Qt::white);
-    default:
-        return {};
-    }
-}
-
-void SamplesQModel::deleteItem()
-{
-    const size_t row = set()->currentIndex();
-    ASSERT(row != size_t(-1));
-    beginRemoveRows({}, row, row);
-    set()->delete_current();
-    endRemoveRows();
-
-    emit set()->setChanged();
-}
-
-void SamplesQModel::cloneItem()
-{
-    const SampleItem* t = set()->currentItem();
-    ASSERT(t);
-    SampleItem* t2 = t->clone();
-    pushItem(t2);
-}
-
-void SamplesQModel::pushItem(SampleItem* t)
-{
-    if (!t)
-        return;
-    const size_t row = set()->size();
-    beginInsertRows({}, row, row);
-    set()->push_back(t);
-    endInsertRows();
-
-    emit set()->setChanged();
-}
diff --git a/GUI/View/List/SamplesQModel.h b/GUI/View/List/SamplesQModel.h
deleted file mode 100644
index 941615ba94a09d400535722e83817ca5d35a3b83..0000000000000000000000000000000000000000
--- a/GUI/View/List/SamplesQModel.h
+++ /dev/null
@@ -1,41 +0,0 @@
-//  ************************************************************************************************
-//
-//  BornAgain: simulate and fit reflection and scattering
-//
-//! @file      GUI/View/List/SamplesQModel.h
-//! @brief     Defines class SamplesQModel.
-//!
-//! @homepage  http://www.bornagainproject.org
-//! @license   GNU General Public License v3 or higher (see COPYING)
-//! @copyright Forschungszentrum Jülich GmbH 2021
-//! @authors   Scientific Computing Group at MLZ (see CITATION, AUTHORS)
-//
-//  ************************************************************************************************
-
-#ifndef BORNAGAIN_GUI_VIEW_LIST_SAMPLESQMODEL_H
-#define BORNAGAIN_GUI_VIEW_LIST_SAMPLESQMODEL_H
-
-#include <QAbstractListModel>
-
-class SampleItem;
-class SamplesSet;
-
-//! Data model for SamplesQListView.
-
-class SamplesQModel : public QAbstractListModel {
-public:
-    SamplesQModel(QObject* parent);
-
-    int rowCount(const QModelIndex&) const override;
-    QVariant data(const QModelIndex& index, int role) const override;
-
-    void deleteItem();
-    void cloneItem();
-    void pushItem(SampleItem*);
-
-private:
-    SamplesSet* set();
-    const SamplesSet* set() const;
-};
-
-#endif // BORNAGAIN_GUI_VIEW_LIST_SAMPLESQMODEL_H
diff --git a/GUI/View/Views/InstrumentView.cpp b/GUI/View/Views/InstrumentView.cpp
index 1764289926ae9fd1ff6c5a52378c384e508ae0ab..8402f7eb913e073027eb8d959f9b02b05f0cc535 100644
--- a/GUI/View/Views/InstrumentView.cpp
+++ b/GUI/View/Views/InstrumentView.cpp
@@ -154,7 +154,7 @@ void InstrumentView::createWidgetsForCurrentInstrument()
     layout->addWidget(g);
 
     auto* nameEdit = new QLineEdit(t->name(), g);
-    connect(nameEdit, &QLineEdit::textEdited, [t](const QString &text) { t->setName(text); });
+    connect(nameEdit, &QLineEdit::textEdited, [t](const QString& text) { t->setName(text); });
     formLayout->addRow("Name:", nameEdit);
 
     auto* descriptionEdit = new QTextEdit(g);
@@ -163,8 +163,8 @@ void InstrumentView::createWidgetsForCurrentInstrument()
     descriptionEdit->setAcceptRichText(false);
     descriptionEdit->setTabChangesFocus(true);
     descriptionEdit->setPlainText(t->description());
-    connect(descriptionEdit, &QTextEdit::textChanged, [t, descriptionEdit]
-	{ t->setDescription(descriptionEdit->toPlainText()); });
+    connect(descriptionEdit, &QTextEdit::textChanged,
+            [t, descriptionEdit] { t->setDescription(descriptionEdit->toPlainText()); });
     formLayout->addRow("Description:", descriptionEdit);
 
     //... All remaining content depends on instrument type
diff --git a/GUI/View/Views/SampleView.cpp b/GUI/View/Views/SampleView.cpp
index eb84ab9dcbc9df17885ff26a2f2a828279ba54ac..a4679224290799909384a7fd47bad92d56607904 100644
--- a/GUI/View/Views/SampleView.cpp
+++ b/GUI/View/Views/SampleView.cpp
@@ -19,11 +19,9 @@
 #include "GUI/Model/Sample/ItemWithParticles.h"
 #include "GUI/Model/Sample/LayerItem.h"
 #include "GUI/Model/Sample/ParticleLayoutItem.h"
-#include "GUI/Model/Sample/SampleItem.h"
 #include "GUI/Model/Sample/SamplesSet.h"
 #include "GUI/View/Layout/mainwindow_constants.h"
 #include "GUI/View/List/SamplesQListView.h"
-#include "GUI/View/List/SamplesQModel.h"
 #include "GUI/View/Manager/PyImportAssistant.h"
 #include "GUI/View/Realspace/RealspacePanel.h"
 #include "GUI/View/Realspace/RealspaceWidget.h"
@@ -38,8 +36,8 @@
 #include <QToolButton>
 
 SampleView::SampleView()
-    : m_qlistmodel(new SamplesQModel(this))
-    , m_qlistview(new SamplesQListView(m_qlistmodel))
+    : m_set(gDoc->samplesModifier())
+    , m_qlistview(new SamplesQListView(m_set))
     , m_realspace_panel(new RealspacePanel(this))
 {
     auto* layout = new QVBoxLayout(this);
@@ -129,7 +127,7 @@ void SampleView::setToolbarActions(QToolBar* toolbar)
     toolbar->addAction(new_sample_action);
 
     connect(new_sample_action, &QAction::triggered, [this] {
-        m_qlistmodel->pushItem(new SampleItem);
+        m_set->add_item(new SampleItem);
         // TODO call new_item->addStandardMaterials() ??
     });
 
@@ -142,7 +140,7 @@ void SampleView::setToolbarActions(QToolBar* toolbar)
         "Import sample from a Python script that contains a function that returns a multi-layer");
     toolbar->addAction(import_sample_action);
     connect(import_sample_action, &QAction::triggered,
-            [this] { m_qlistmodel->pushItem(PyImportAssistant::importMultiLayer()); });
+            [this] { m_set->add_item(PyImportAssistant::importMultiLayer()); });
 #endif
 
     auto* choose_from_library_action = new QAction("Examples", this);
@@ -166,7 +164,7 @@ void SampleView::setToolbarActions(QToolBar* toolbar)
                 return;
             t->setName(title);
             t->setDescription(description);
-            m_qlistmodel->pushItem(t);
+            m_set->add_item(t);
         });
     }
 
@@ -174,11 +172,12 @@ void SampleView::setToolbarActions(QToolBar* toolbar)
 
     m_cp_action = ActionFactory::createCopyAction("sample", this);
     toolbar->addAction(m_cp_action);
-    connect(m_cp_action, &QAction::triggered, m_qlistmodel, &SamplesQModel::cloneItem);
+    connect(m_cp_action, &QAction::triggered,
+            [this] { m_set->add_item(m_set->currentItem()->clone()); });
 
     m_rm_action = ActionFactory::createRemoveAction("sample", this);
     toolbar->addAction(m_rm_action);
-    connect(m_rm_action, &QAction::triggered, m_qlistmodel, &SamplesQModel::deleteItem);
+    connect(m_rm_action, &QAction::triggered, m_set, &SamplesSet::delete_current);
 }
 
 void SampleView::applySplitterPos()
diff --git a/GUI/View/Views/SampleView.h b/GUI/View/Views/SampleView.h
index 910a0dedabb14988fee1567a0f8d08707ef69b7a..fbb4676f840914a3fa2f71aa6404aff249eb31f5 100644
--- a/GUI/View/Views/SampleView.h
+++ b/GUI/View/Views/SampleView.h
@@ -22,7 +22,7 @@ class Item3D;
 class QToolBar;
 class RealspacePanel;
 class SamplesQListView;
-class SamplesQModel;
+class SamplesSet;
 
 class SampleView : public QWidget {
     Q_OBJECT
@@ -40,7 +40,7 @@ private:
     void applySplitterPos();
     void saveSplitterPos();
 
-    SamplesQModel* m_qlistmodel;
+    SamplesSet* m_set;
     SamplesQListView* m_qlistview;
     RealspacePanel* m_realspace_panel;