MVC Table Views

A WTableView displays tabular data. The data is provided by a separate WAbstractItemModel. This is a variation of the classical MVC strategy, where the View displays data provided by a Model. The View delegates the rendering of each individual item (i.e. table cell) to an Item Delegate (WItemDelegate to render an item as a widget, allowing this aspect to be customized without needing to deal with the rest of the complexity of the view.

A more direct approach to showing a table of data is to use a WTable which allows you to directly specify a widget that renders each cell. Compared to this, a WTableView support virtual scrolling (for both rows and columns !), column sorting and resize handles, and the ability to define row headers which allow you to scroll through many columns of additional data while keeping the first header column(s) within view. Its main restriction is that every item must have the same height.

Example
Age
Rural Male
Rural Female
Urban Male
Urban Female
70-74
660
543
711
500
65-69
410
309
546
351
60-64
1269
203
370
193
55-59
181
1490
243
136
50-54
117
87
154
84
source
#include <Wt/WApplication.h>
#include <Wt/WStandardItemModel.h>
#include <Wt/WTableView.h>

#include "../../treeview-dragdrop/CsvUtil.h"

auto tableView = std::make_unique<WTableView>();
tableView->setModel(csvToModel(WApplication::appRoot() + "table.csv"));

tableView->setColumnResizeEnabled(false);
tableView->setColumnAlignment(0, AlignmentFlag::Center);
tableView->setHeaderAlignment(0, AlignmentFlag::Center);
tableView->setAlternatingRowColors(true);
tableView->setRowHeight(28);
tableView->setHeaderHeight(28);
tableView->setSelectionMode(SelectionMode::Single);
tableView->setEditTriggers(EditTrigger::None);

/*
 * Configure column widths and matching table width
 */
const int WIDTH = 120;
for (int i = 0; i < tableView->model()->columnCount(); ++i)
    tableView->setColumnWidth(i, 120);

/*
 * 7 pixels are padding/border per column
 * 2 pixels are border of the entire table
 */
tableView->setWidth((WIDTH + 7) * tableView->model()->columnCount() + 2);

Top

Virtual scrolling

A tableview has the capability to display large datasets, by implementing virtual scrolling both horizontally and vertically.

The example below shows how one can scroll through data in a virtual model with 10000 rows and 50 columns.

Example
Column 0
Column 1
Column 2
Column 3
Column 4
Column 5
Column 6
Column 7
Column 8
Column 9
Column 10
Column 11
Column 12
Column 13
Column 14
Column 15
Column 16
Column 17
Column 18
Column 19
Column 20
Column 21
Column 22
Column 23
Column 24
Column 25
Column 26
Column 27
Column 28
Column 29
Column 30
Column 31
Column 32
Column 33
Column 34
Column 35
Column 36
Column 37
Column 38
Column 39
Column 40
Column 41
Column 42
Column 43
Column 44
Column 45
Column 46
Column 47
Column 48
Column 49
Row 0
Item row 0, col 1
Item row 0, col 2
Item row 0, col 3
Item row 0, col 4
Item row 0, col 5
Item row 0, col 6
Item row 0, col 7
Item row 0, col 8
Item row 0, col 9
Item row 0, col 10
Item row 0, col 11
Item row 0, col 12
Item row 0, col 13
Item row 0, col 14
Item row 0, col 15
Item row 0, col 16
Item row 0, col 17
Item row 0, col 18
Item row 0, col 19
Item row 0, col 20
Item row 0, col 21
Item row 0, col 22
Item row 0, col 23
Item row 0, col 24
Item row 0, col 25
Item row 0, col 26
Item row 0, col 27
Item row 0, col 28
Item row 0, col 29
Item row 0, col 30
Item row 0, col 31
Item row 0, col 32
Item row 0, col 33
Item row 0, col 34
Item row 0, col 35
Item row 0, col 36
Item row 0, col 37
Item row 0, col 38
Item row 0, col 39
Item row 0, col 40
Item row 0, col 41
Item row 0, col 42
Item row 0, col 43
Item row 0, col 44
Item row 0, col 45
Item row 0, col 46
Item row 0, col 47
Item row 0, col 48
Item row 0, col 49
Row 1
Item row 1, col 1
Item row 1, col 2
Item row 1, col 3
Item row 1, col 4
Item row 1, col 5
Item row 1, col 6
Item row 1, col 7
Item row 1, col 8
Item row 1, col 9
Item row 1, col 10
Item row 1, col 11
Item row 1, col 12
Item row 1, col 13
Item row 1, col 14
Item row 1, col 15
Item row 1, col 16
Item row 1, col 17
Item row 1, col 18
Item row 1, col 19
Item row 1, col 20
Item row 1, col 21
Item row 1, col 22
Item row 1, col 23
Item row 1, col 24
Item row 1, col 25
Item row 1, col 26
Item row 1, col 27
Item row 1, col 28
Item row 1, col 29
Item row 1, col 30
Item row 1, col 31
Item row 1, col 32
Item row 1, col 33
Item row 1, col 34
Item row 1, col 35
Item row 1, col 36
Item row 1, col 37
Item row 1, col 38
Item row 1, col 39
Item row 1, col 40
Item row 1, col 41
Item row 1, col 42
Item row 1, col 43
Item row 1, col 44
Item row 1, col 45
Item row 1, col 46
Item row 1, col 47
Item row 1, col 48
Item row 1, col 49
Row 2
Item row 2, col 1
Item row 2, col 2
Item row 2, col 3
Item row 2, col 4
Item row 2, col 5
Item row 2, col 6
Item row 2, col 7
Item row 2, col 8
Item row 2, col 9
Item row 2, col 10
Item row 2, col 11
Item row 2, col 12
Item row 2, col 13
Item row 2, col 14
Item row 2, col 15
Item row 2, col 16
Item row 2, col 17
Item row 2, col 18
Item row 2, col 19
Item row 2, col 20
Item row 2, col 21
Item row 2, col 22
Item row 2, col 23
Item row 2, col 24
Item row 2, col 25
Item row 2, col 26
Item row 2, col 27
Item row 2, col 28
Item row 2, col 29
Item row 2, col 30
Item row 2, col 31
Item row 2, col 32
Item row 2, col 33
Item row 2, col 34
Item row 2, col 35
Item row 2, col 36
Item row 2, col 37
Item row 2, col 38
Item row 2, col 39
Item row 2, col 40
Item row 2, col 41
Item row 2, col 42
Item row 2, col 43
Item row 2, col 44
Item row 2, col 45
Item row 2, col 46
Item row 2, col 47
Item row 2, col 48
Item row 2, col 49
Row 3
Item row 3, col 1
Item row 3, col 2
Item row 3, col 3
Item row 3, col 4
Item row 3, col 5
Item row 3, col 6
Item row 3, col 7
Item row 3, col 8
Item row 3, col 9
Item row 3, col 10
Item row 3, col 11
Item row 3, col 12
Item row 3, col 13
Item row 3, col 14
Item row 3, col 15
Item row 3, col 16
Item row 3, col 17
Item row 3, col 18
Item row 3, col 19
Item row 3, col 20
Item row 3, col 21
Item row 3, col 22
Item row 3, col 23
Item row 3, col 24
Item row 3, col 25
Item row 3, col 26
Item row 3, col 27
Item row 3, col 28
Item row 3, col 29
Item row 3, col 30
Item row 3, col 31
Item row 3, col 32
Item row 3, col 33
Item row 3, col 34
Item row 3, col 35
Item row 3, col 36
Item row 3, col 37
Item row 3, col 38
Item row 3, col 39
Item row 3, col 40
Item row 3, col 41
Item row 3, col 42
Item row 3, col 43
Item row 3, col 44
Item row 3, col 45
Item row 3, col 46
Item row 3, col 47
Item row 3, col 48
Item row 3, col 49
Row 4
Item row 4, col 1
Item row 4, col 2
Item row 4, col 3
Item row 4, col 4
Item row 4, col 5
Item row 4, col 6
Item row 4, col 7
Item row 4, col 8
Item row 4, col 9
Item row 4, col 10
Item row 4, col 11
Item row 4, col 12
Item row 4, col 13
Item row 4, col 14
Item row 4, col 15
Item row 4, col 16
Item row 4, col 17
Item row 4, col 18
Item row 4, col 19
Item row 4, col 20
Item row 4, col 21
Item row 4, col 22
Item row 4, col 23
Item row 4, col 24
Item row 4, col 25
Item row 4, col 26
Item row 4, col 27
Item row 4, col 28
Item row 4, col 29
Item row 4, col 30
Item row 4, col 31
Item row 4, col 32
Item row 4, col 33
Item row 4, col 34
Item row 4, col 35
Item row 4, col 36
Item row 4, col 37
Item row 4, col 38
Item row 4, col 39
Item row 4, col 40
Item row 4, col 41
Item row 4, col 42
Item row 4, col 43
Item row 4, col 44
Item row 4, col 45
Item row 4, col 46
Item row 4, col 47
Item row 4, col 48
Item row 4, col 49
Row 5
Item row 5, col 1
Item row 5, col 2
Item row 5, col 3
Item row 5, col 4
Item row 5, col 5
Item row 5, col 6
Item row 5, col 7
Item row 5, col 8
Item row 5, col 9
Item row 5, col 10
Item row 5, col 11
Item row 5, col 12
Item row 5, col 13
Item row 5, col 14
Item row 5, col 15
Item row 5, col 16
Item row 5, col 17
Item row 5, col 18
Item row 5, col 19
Item row 5, col 20
Item row 5, col 21
Item row 5, col 22
Item row 5, col 23
Item row 5, col 24
Item row 5, col 25
Item row 5, col 26
Item row 5, col 27
Item row 5, col 28
Item row 5, col 29
Item row 5, col 30
Item row 5, col 31
Item row 5, col 32
Item row 5, col 33
Item row 5, col 34
Item row 5, col 35
Item row 5, col 36
Item row 5, col 37
Item row 5, col 38
Item row 5, col 39
Item row 5, col 40
Item row 5, col 41
Item row 5, col 42
Item row 5, col 43
Item row 5, col 44
Item row 5, col 45
Item row 5, col 46
Item row 5, col 47
Item row 5, col 48
Item row 5, col 49
Row 6
Item row 6, col 1
Item row 6, col 2
Item row 6, col 3
Item row 6, col 4
Item row 6, col 5
Item row 6, col 6
Item row 6, col 7
Item row 6, col 8
Item row 6, col 9
Item row 6, col 10
Item row 6, col 11
Item row 6, col 12
Item row 6, col 13
Item row 6, col 14
Item row 6, col 15
Item row 6, col 16
Item row 6, col 17
Item row 6, col 18
Item row 6, col 19
Item row 6, col 20
Item row 6, col 21
Item row 6, col 22
Item row 6, col 23
Item row 6, col 24
Item row 6, col 25
Item row 6, col 26
Item row 6, col 27
Item row 6, col 28
Item row 6, col 29
Item row 6, col 30
Item row 6, col 31
Item row 6, col 32
Item row 6, col 33
Item row 6, col 34
Item row 6, col 35
Item row 6, col 36
Item row 6, col 37
Item row 6, col 38
Item row 6, col 39
Item row 6, col 40
Item row 6, col 41
Item row 6, col 42
Item row 6, col 43
Item row 6, col 44
Item row 6, col 45
Item row 6, col 46
Item row 6, col 47
Item row 6, col 48
Item row 6, col 49
Row 7
Item row 7, col 1
Item row 7, col 2
Item row 7, col 3
Item row 7, col 4
Item row 7, col 5
Item row 7, col 6
Item row 7, col 7
Item row 7, col 8
Item row 7, col 9
Item row 7, col 10
Item row 7, col 11
Item row 7, col 12
Item row 7, col 13
Item row 7, col 14
Item row 7, col 15
Item row 7, col 16
Item row 7, col 17
Item row 7, col 18
Item row 7, col 19
Item row 7, col 20
Item row 7, col 21
Item row 7, col 22
Item row 7, col 23
Item row 7, col 24
Item row 7, col 25
Item row 7, col 26
Item row 7, col 27
Item row 7, col 28
Item row 7, col 29
Item row 7, col 30
Item row 7, col 31
Item row 7, col 32
Item row 7, col 33
Item row 7, col 34
Item row 7, col 35
Item row 7, col 36
Item row 7, col 37
Item row 7, col 38
Item row 7, col 39
Item row 7, col 40
Item row 7, col 41
Item row 7, col 42
Item row 7, col 43
Item row 7, col 44
Item row 7, col 45
Item row 7, col 46
Item row 7, col 47
Item row 7, col 48
Item row 7, col 49
Row 8
Item row 8, col 1
Item row 8, col 2
Item row 8, col 3
Item row 8, col 4
Item row 8, col 5
Item row 8, col 6
Item row 8, col 7
Item row 8, col 8
Item row 8, col 9
Item row 8, col 10
Item row 8, col 11
Item row 8, col 12
Item row 8, col 13
Item row 8, col 14
Item row 8, col 15
Item row 8, col 16
Item row 8, col 17
Item row 8, col 18
Item row 8, col 19
Item row 8, col 20
Item row 8, col 21
Item row 8, col 22
Item row 8, col 23
Item row 8, col 24
Item row 8, col 25
Item row 8, col 26
Item row 8, col 27
Item row 8, col 28
Item row 8, col 29
Item row 8, col 30
Item row 8, col 31
Item row 8, col 32
Item row 8, col 33
Item row 8, col 34
Item row 8, col 35
Item row 8, col 36
Item row 8, col 37
Item row 8, col 38
Item row 8, col 39
Item row 8, col 40
Item row 8, col 41
Item row 8, col 42
Item row 8, col 43
Item row 8, col 44
Item row 8, col 45
Item row 8, col 46
Item row 8, col 47
Item row 8, col 48
Item row 8, col 49
Row 9
Item row 9, col 1
Item row 9, col 2
Item row 9, col 3
Item row 9, col 4
Item row 9, col 5
Item row 9, col 6
Item row 9, col 7
Item row 9, col 8
Item row 9, col 9
Item row 9, col 10
Item row 9, col 11
Item row 9, col 12
Item row 9, col 13
Item row 9, col 14
Item row 9, col 15
Item row 9, col 16
Item row 9, col 17
Item row 9, col 18
Item row 9, col 19
Item row 9, col 20
Item row 9, col 21
Item row 9, col 22
Item row 9, col 23
Item row 9, col 24
Item row 9, col 25
Item row 9, col 26
Item row 9, col 27
Item row 9, col 28
Item row 9, col 29
Item row 9, col 30
Item row 9, col 31
Item row 9, col 32
Item row 9, col 33
Item row 9, col 34
Item row 9, col 35
Item row 9, col 36
Item row 9, col 37
Item row 9, col 38
Item row 9, col 39
Item row 9, col 40
Item row 9, col 41
Item row 9, col 42
Item row 9, col 43
Item row 9, col 44
Item row 9, col 45
Item row 9, col 46
Item row 9, col 47
Item row 9, col 48
Item row 9, col 49
Row 10
Item row 10, col 1
Item row 10, col 2
Item row 10, col 3
Item row 10, col 4
Item row 10, col 5
Item row 10, col 6
Item row 10, col 7
Item row 10, col 8
Item row 10, col 9
Item row 10, col 10
Item row 10, col 11
Item row 10, col 12
Item row 10, col 13
Item row 10, col 14
Item row 10, col 15
Item row 10, col 16
Item row 10, col 17
Item row 10, col 18
Item row 10, col 19
Item row 10, col 20
Item row 10, col 21
Item row 10, col 22
Item row 10, col 23
Item row 10, col 24
Item row 10, col 25
Item row 10, col 26
Item row 10, col 27
Item row 10, col 28
Item row 10, col 29
Item row 10, col 30
Item row 10, col 31
Item row 10, col 32
Item row 10, col 33
Item row 10, col 34
Item row 10, col 35
Item row 10, col 36
Item row 10, col 37
Item row 10, col 38
Item row 10, col 39
Item row 10, col 40
Item row 10, col 41
Item row 10, col 42
Item row 10, col 43
Item row 10, col 44
Item row 10, col 45
Item row 10, col 46
Item row 10, col 47
Item row 10, col 48
Item row 10, col 49
Row 11
Item row 11, col 1
Item row 11, col 2
Item row 11, col 3
Item row 11, col 4
Item row 11, col 5
Item row 11, col 6
Item row 11, col 7
Item row 11, col 8
Item row 11, col 9
Item row 11, col 10
Item row 11, col 11
Item row 11, col 12
Item row 11, col 13
Item row 11, col 14
Item row 11, col 15
Item row 11, col 16
Item row 11, col 17
Item row 11, col 18
Item row 11, col 19
Item row 11, col 20
Item row 11, col 21
Item row 11, col 22
Item row 11, col 23
Item row 11, col 24
Item row 11, col 25
Item row 11, col 26
Item row 11, col 27
Item row 11, col 28
Item row 11, col 29
Item row 11, col 30
Item row 11, col 31
Item row 11, col 32
Item row 11, col 33
Item row 11, col 34
Item row 11, col 35
Item row 11, col 36
Item row 11, col 37
Item row 11, col 38
Item row 11, col 39
Item row 11, col 40
Item row 11, col 41
Item row 11, col 42
Item row 11, col 43
Item row 11, col 44
Item row 11, col 45
Item row 11, col 46
Item row 11, col 47
Item row 11, col 48
Item row 11, col 49
1 of 834

source
#include <Wt/WTableView.h>

#include "VirtualModel.cpp"

using namespace Wt;


auto tableView = std::make_unique<WTableView>();
tableView->setModel(std::make_shared<VirtualModel>(10000, 50));
tableView->setRowHeaderCount(1); // treat first column as 'fixed' row headers
tableView->setSortingEnabled(false);
tableView->setAlternatingRowColors(true);
tableView->setRowHeight(28);
tableView->setHeaderHeight(28);
tableView->setSelectionMode(SelectionMode::Extended);
tableView->setEditTriggers(EditTrigger::None);

tableView->resize(650, 400);

Note The implementation of VirtualModel is shown in the section on item models

Top

Customizing cell rendering

The rendering of each cell is handled by a WAbstractItemDelegate. An item delegate will typically query various properties of an item, which are provided by the model for different data roles (which one can consider pretty much like a 3rd dimension for what otherwise corresponds to two-dimensional model organized in rows and columns). The default implementation (WItemDelegate) will

  • render text, using the data roleDisplayRole,
  • render icons, using the data role DecorationRole,
  • render check boxes, using the data role CheckStateRole,
  • render links, using the data role LinkRole,
  • render tool tips, using the data role ToolTipRole,
  • render style classes, using the data role StyleClassRole.
If the default item delegate doesn't satisfy, you can reimplement it to tailor the rendering, and you can extend models with data for additional data roles.

Top

Editing + Combo box editor example

MVC Views will automatically react to changes to the underlying model, be it in the form of data updates, or the insertion of new rows or columns.

But an MVC View may also actively participate in editing the data. You can define editing triggers (such as a double click) and the model can indicate to the view which data can be edited. The default item delegate (WItemDelegate) will use a line edit for editing, but this can be customized by providing your own item delegate implementation.

The example below illustrates how to override the editing behaviour of WItemDelegate. It uses a combo box to allow the user to edit the cell values.

Example
apples
apples
apples
apples

source
#include <Wt/WStandardItem.h>
#include <Wt/WStandardItemModel.h>
#include <Wt/WStringListModel.h>
#include <Wt/WTableView.h>
#include <Wt/WItemDelegate.h>
#include <Wt/WContainerWidget.h>
#include <Wt/WComboBox.h>
#include <Wt/WAny.h>

/*
 * This delegate demonstrates how to override the editing behaviour of a
 * table cell.
 *
 * It takes a list of possible items on construction and, when edited, saves
 * the selected item from the list to the Wt::DisplayRole in the model for
 * Wt::WItemDelegate to render.
 * It also saves the items index for future editing (rather than each time
 * searching the item in the list). This is done using the general purpose
 * Wt::UserRole in the model.
 */
class ComboDelegate : public Wt::WItemDelegate {
public:
    ComboDelegate(std::shared_ptr<Wt::WAbstractItemModel> items)
        : items_(items)
    { }

    virtual void setModelData(const Wt::cpp17::any &editState, Wt::WAbstractItemModel* model,
                      const Wt::WModelIndex &index) const override
    {
      int stringIdx = (int)Wt::asNumber(editState);
        model->setData(index, stringIdx, Wt::ItemDataRole::User);
        model->setData(index, items_->data(stringIdx, 0), Wt::ItemDataRole::Display);
    }

    virtual Wt::cpp17::any editState(Wt::WWidget *editor, const Wt::WModelIndex& index) const override
    {
        Wt::WComboBox* combo = dynamic_cast<Wt::WComboBox*>
            (dynamic_cast<Wt::WContainerWidget*>(editor)->widget(0));
        return combo->currentIndex();
    }

    virtual void setEditState(Wt::WWidget *editor, const Wt::WModelIndex& index,
                  const Wt::cpp17::any& value) const override
    {
        Wt::WComboBox* combo = dynamic_cast<Wt::WComboBox*>
            (dynamic_cast<Wt::WContainerWidget*>(editor)->widget(0));
        combo->setCurrentIndex((int)Wt::asNumber(value));
    }

protected:
    virtual std::unique_ptr<Wt::WWidget> createEditor(const Wt::WModelIndex &index,
                                      Wt::WFlags<Wt::ViewItemRenderFlag> flags) const override
    {
        auto container = std::make_unique<Wt::WContainerWidget>();
        auto combo = container->addNew<Wt::WComboBox>();
        combo->setModel(items_);
        combo->setCurrentIndex((int)Wt::asNumber(index.data(Wt::ItemDataRole::User)));

        combo->changed().connect(std::bind(&ComboDelegate::doCloseEditor, this,
                                           container.get(), true));
        combo->enterPressed().connect(std::bind(&ComboDelegate::doCloseEditor,
                                                this, container.get(), true));
        combo->escapePressed().connect(std::bind(&ComboDelegate::doCloseEditor,
                                                 this, container.get(), false));

        return std::move(container);
    }

private:
    std::shared_ptr<Wt::WAbstractItemModel> items_;

    virtual void doCloseEditor(Wt::WWidget *editor, bool save) const
    {
        closeEditor().emit(editor, save);
    }
};


auto table = std::make_unique<Wt::WTableView>();

// create model
std::vector<WString> options { "apples", "pears", "bananas", "cherries" };

auto model = std::make_shared<Wt::WStandardItemModel>();
for (unsigned i=0; i < 2; i++) {
  for (unsigned j=0; j < 2; j++) {
    auto item = std::make_unique<Wt::WStandardItem>();
    item->setData(0, Wt::ItemDataRole::User);
    item->setData(options[0], Wt::ItemDataRole::Display);
    item->setFlags(Wt::ItemFlag::Editable);
    model->setItem(i, j, std::move(item));
  }
}

// create table
table->setModel(model);
table->setEditTriggers(Wt::EditTrigger::SingleClicked);
auto slModel = std::make_shared<Wt::WStringListModel>();
slModel->setStringList(options);
std::shared_ptr<ComboDelegate> customdelegate =
    std::make_shared<ComboDelegate>(slModel);
table->setItemDelegate(customdelegate);

table->setSortingEnabled(false);
table->setColumnResizeEnabled(false);
table->setRowHeight(40);
table->setHeaderHeight(0);

const int WIDTH = 120;
for (int i = 0; i < table->model()->columnCount(); ++i)
    table->setColumnWidth(i, WIDTH);
table->setWidth((WIDTH + 7) * table->model()->columnCount() + 2);

Top

Row headers

Especially when displaying datasets with many columns, it may be convenient to define a number of columns as row headers (using WAbstractItemView::setRowHeaderCount()), which are kept in view while scrolling to the columns.

Top