You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by ah...@apache.org on 2021/12/22 14:06:42 UTC

[isis] branch master updated: ISIS-2929: re-wire the multi-select toggle stuff correctly

This is an automated email from the ASF dual-hosted git repository.

ahuber pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/isis.git


The following commit(s) were added to refs/heads/master by this push:
     new bf04748  ISIS-2929: re-wire the multi-select toggle stuff correctly
bf04748 is described below

commit bf04748652885267e78548ca9ef64c6e5c916e8c
Author: Andi Huber <ah...@apache.org>
AuthorDate: Wed Dec 22 15:06:33 2021 +0100

    ISIS-2929: re-wire the multi-select toggle stuff correctly
    
    - multiple refactoring and simplifications along the way
---
 .../org/apache/isis/commons/collections/Can.java   |   8 +
 .../apache/isis/commons/collections/Can_Empty.java |   5 +
 .../isis/commons/collections/Can_Multiple.java     |   8 +
 .../isis/commons/collections/Can_Singleton.java    |   8 +
 .../apache/isis/commons/internal/base/_Casts.java  |   2 -
 .../commons/internal/functions/_Functions.java     |  16 ++
 .../interactions/managed/nonscalar/DataRow.java    |  14 +-
 .../managed/nonscalar/DataTableModel.java          |  21 +-
 .../nonscalar/{_Debug.java => _ToggleDebug.java}   |   4 +-
 .../wicket/model/models/EntityCollectionModel.java |  14 +-
 .../models/EntityCollectionModelAbstract.java      |  34 +--
 .../model/models/EntityCollectionModelHidden.java  |   2 +-
 .../interaction/act/ActionInteractionWkt.java      |   7 +-
 .../{DataRowWkt.java => DataRowToggleWkt.java}     |  49 ++---
 .../model/models/interaction/coll/DataRowWkt.java  |  33 +--
 .../models/interaction/coll/DataTableModelWkt.java |   8 +-
 .../ui/components/collection/CollectionPanel.java  |  18 +-
 ...rovider.java => MultiselectToggleProvider.java} |   2 +-
 .../selector/CollectionSelectorProvider.java       |   3 +-
 .../CollectionContentsAsAjaxTablePanel.java        |  48 ++---
 .../CollectionContentsSortableDataProvider.java    |  73 +++----
 ...llbackDataTable.java => IsisAjaxDataTable.java} | 187 ++++++++--------
 .../ajaxtable/IsisAjaxFallbackHeadersToolbar.java  |  93 --------
 .../ajaxtable/IsisAjaxFallbackOrderByBorder.java   |  16 +-
 .../ajaxtable/IsisAjaxHeadersToolbar.java          | 234 +++++----------------
 ...ar.java => IsisAjaxHeadersToolbarAbstract.java} |   4 +-
 .../ajaxtable/IsisAjaxNavigationToolbar.java       |   7 +-
 .../IsisAjaxPagingNavigationIncrementLink.java     |  10 +-
 .../ajaxtable/IsisAjaxPagingNavigationLink.java    |  12 +-
 .../ajaxtable/columns/BulkToggleWkt.java}          |  33 +--
 .../ajaxtable/columns/GenericToggleboxColumn.java  |  29 ++-
 .../summary/CollectionContentsAsSummary.java       |   2 +-
 .../StandaloneCollectionPanel.java                 |   2 +-
 .../widgets/checkbox/ContainedToggleboxPanel.java  |  33 ++-
 .../org/apache/isis/viewer/wicket/ui/util/Wkt.java |  28 ++-
 35 files changed, 440 insertions(+), 627 deletions(-)

diff --git a/commons/src/main/java/org/apache/isis/commons/collections/Can.java b/commons/src/main/java/org/apache/isis/commons/collections/Can.java
index 64a64cb..6c6cc6b 100644
--- a/commons/src/main/java/org/apache/isis/commons/collections/Can.java
+++ b/commons/src/main/java/org/apache/isis/commons/collections/Can.java
@@ -460,6 +460,14 @@ extends Iterable<T>, Comparable<Can<T>>, Serializable {
 
     // -- TRAVERSAL
 
+    /**
+     * Returns an iterator that skips the first {@code skip} elements, then returns a
+     * maximum of {@code limit} elements.
+     * @param skip - similar to {@link Stream#skip(long)}
+     * @param limit - similar to {@link Stream#limit(long)}
+     */
+    Iterator<T> iterator(int skip, int limit);
+
     Iterator<T> reverseIterator();
 
     @Override
diff --git a/commons/src/main/java/org/apache/isis/commons/collections/Can_Empty.java b/commons/src/main/java/org/apache/isis/commons/collections/Can_Empty.java
index dcecece..86729d4 100644
--- a/commons/src/main/java/org/apache/isis/commons/collections/Can_Empty.java
+++ b/commons/src/main/java/org/apache/isis/commons/collections/Can_Empty.java
@@ -98,6 +98,11 @@ final class Can_Empty<T> implements Can<T> {
     }
 
     @Override
+    public Iterator<T> iterator(final int skip, final int limit) {
+        return iterator(); // empty iterator no matter what the arguments
+    }
+
+    @Override
     public Iterator<T> iterator() {
         return Collections.<T>emptyList().iterator();
     }
diff --git a/commons/src/main/java/org/apache/isis/commons/collections/Can_Multiple.java b/commons/src/main/java/org/apache/isis/commons/collections/Can_Multiple.java
index fcf7683..bbca697 100644
--- a/commons/src/main/java/org/apache/isis/commons/collections/Can_Multiple.java
+++ b/commons/src/main/java/org/apache/isis/commons/collections/Can_Multiple.java
@@ -143,6 +143,14 @@ final class Can_Multiple<T> implements Can<T> {
     }
 
     @Override
+    public Iterator<T> iterator(final int skip, final int limit) {
+        return Collections.unmodifiableList(elements).stream()
+            .skip(skip)
+            .limit(limit)
+            .iterator();
+    }
+
+    @Override
     public Iterator<T> iterator() {
         return Collections.unmodifiableList(elements).iterator();
     }
diff --git a/commons/src/main/java/org/apache/isis/commons/collections/Can_Singleton.java b/commons/src/main/java/org/apache/isis/commons/collections/Can_Singleton.java
index bc6b82b..5649bd9 100644
--- a/commons/src/main/java/org/apache/isis/commons/collections/Can_Singleton.java
+++ b/commons/src/main/java/org/apache/isis/commons/collections/Can_Singleton.java
@@ -99,6 +99,14 @@ final class Can_Singleton<T> implements Can<T> {
     }
 
     @Override
+    public Iterator<T> iterator(final int skip, final int limit) {
+        return skip<1
+                && limit>0
+                ? iterator()
+                : Collections.<T>emptyList().iterator();
+    }
+
+    @Override
     public Iterator<T> iterator() {
         return Collections.singletonList(element).iterator();
     }
diff --git a/commons/src/main/java/org/apache/isis/commons/internal/base/_Casts.java b/commons/src/main/java/org/apache/isis/commons/internal/base/_Casts.java
index cb1e62f..1bc1397 100644
--- a/commons/src/main/java/org/apache/isis/commons/internal/base/_Casts.java
+++ b/commons/src/main/java/org/apache/isis/commons/internal/base/_Casts.java
@@ -139,6 +139,4 @@ public final class _Casts {
     }
 
 
-
-
 }
diff --git a/commons/src/main/java/org/apache/isis/commons/internal/functions/_Functions.java b/commons/src/main/java/org/apache/isis/commons/internal/functions/_Functions.java
index d45c23c..409dff6 100644
--- a/commons/src/main/java/org/apache/isis/commons/internal/functions/_Functions.java
+++ b/commons/src/main/java/org/apache/isis/commons/internal/functions/_Functions.java
@@ -18,6 +18,7 @@
  */
 package org.apache.isis.commons.internal.functions;
 
+import java.io.Serializable;
 import java.util.function.BiConsumer;
 import java.util.function.Consumer;
 import java.util.function.Function;
@@ -43,6 +44,21 @@ public final class _Functions {
         return t->{};
     }
 
+    // --
+
+    @FunctionalInterface
+    public static interface SerializableConsumer<T> extends Consumer<T>, Serializable {
+    }
+
+    @FunctionalInterface
+    public static interface SerializableSupplier<T> extends Supplier<T>, Serializable {
+    }
+
+    @FunctionalInterface
+    public static interface SerializableFunction<T, R> extends Function<T, R>, Serializable {
+
+    }
+
     // -- CHECKED EXCEPTION ADAPTERS (FUNCTION)
 
     /**
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/interactions/managed/nonscalar/DataRow.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/interactions/managed/nonscalar/DataRow.java
index a4d82d2..b1faedb 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/interactions/managed/nonscalar/DataRow.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/interactions/managed/nonscalar/DataRow.java
@@ -22,14 +22,16 @@ import java.util.UUID;
 
 import org.apache.isis.commons.internal.binding._Bindables;
 import org.apache.isis.commons.internal.binding._Bindables.BooleanBindable;
+import org.apache.isis.commons.internal.debug._Debug;
 import org.apache.isis.core.metamodel.spec.ManagedObject;
+import org.apache.isis.core.metamodel.spec.ManagedObjects.EntityUtil;
 
 import lombok.Getter;
 
 public class DataRow {
 
     @Getter private final UUID uuid = UUID.randomUUID(); // in support of client side sorting
-    @Getter private final ManagedObject rowElement;
+    private final ManagedObject rowElement;
     @Getter private final BooleanBindable selectToggle;
 
     public DataRow(final DataTableModel parentTable, final ManagedObject rowElement) {
@@ -38,7 +40,9 @@ public class DataRow {
         selectToggle = _Bindables.forBoolean(false);
         selectToggle.addListener((e,o,n)->{
 
-            //_Debug.onSelectRowToggle(rowElement, o, n, parentTable.isToggleAllEvent.get());
+            _Debug.log("toggle %b [table=%d]", n, parentTable.hashCode());
+
+            //_ToggleDebug.onSelectRowToggle(rowElement, o, n, parentTable.isToggleAllEvent.get());
 
             if(parentTable.isToggleAllEvent.get()) {
                 return;
@@ -50,8 +54,12 @@ public class DataRow {
 
     }
 
+    public ManagedObject getRowElement() {
+        return EntityUtil.refetch(rowElement);
+    }
+
     public ManagedObject getCellElement(final DataColumn column) {
-        return column.getPropertyMetaModel().get(rowElement);
+        return column.getPropertyMetaModel().get(getRowElement());
     }
 
 }
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/interactions/managed/nonscalar/DataTableModel.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/interactions/managed/nonscalar/DataTableModel.java
index 70d19fe..37ac9f2 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/interactions/managed/nonscalar/DataTableModel.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/interactions/managed/nonscalar/DataTableModel.java
@@ -19,6 +19,8 @@
 package org.apache.isis.core.metamodel.interactions.managed.nonscalar;
 
 import java.io.Serializable;
+import java.util.Optional;
+import java.util.UUID;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.function.Supplier;
 
@@ -46,6 +48,7 @@ import org.apache.isis.core.metamodel.interactions.managed.ManagedCollection;
 import org.apache.isis.core.metamodel.interactions.managed.ManagedMember;
 import org.apache.isis.core.metamodel.interactions.managed.MultiselectChoices;
 import org.apache.isis.core.metamodel.spec.ManagedObject;
+import org.apache.isis.core.metamodel.spec.ObjectSpecification;
 import org.apache.isis.core.metamodel.spec.PackedManagedObject;
 import org.apache.isis.core.metamodel.spec.feature.ObjectMember;
 
@@ -159,10 +162,25 @@ implements MultiselectChoices {
             .getFriendlyName());
     }
 
+    public int getElementCount() {
+        return dataRowsFiltered.getValue().size();
+    }
+
     public ObjectMember getMetaModel() {
         return managedMember.getMetaModel();
     }
 
+    public ObjectSpecification getElementType() {
+        return getMetaModel().getElementType();
+    }
+
+    public Optional<DataRow> lookupDataRow(final @NonNull UUID uuid) {
+        //TODO can be safely cached
+        return getDataRowsFiltered().getValue().stream()
+                .filter(dr->dr.getUuid().equals(uuid))
+                .findFirst();
+    }
+
     // -- TOGGLE ALL
 
     final AtomicBoolean isToggleAllEvent = new AtomicBoolean();
@@ -198,7 +216,7 @@ implements MultiselectChoices {
 
     @Override
     public Can<ManagedObject> getSelected() {
-        return getDataRowsSelected()
+      return getDataRowsSelected()
                 .getValue()
                 .map(DataRow::getRowElement);
     }
@@ -275,5 +293,4 @@ implements MultiselectChoices {
         }
     }
 
-
 }
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/interactions/managed/nonscalar/_Debug.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/interactions/managed/nonscalar/_ToggleDebug.java
similarity index 93%
rename from core/metamodel/src/main/java/org/apache/isis/core/metamodel/interactions/managed/nonscalar/_Debug.java
rename to core/metamodel/src/main/java/org/apache/isis/core/metamodel/interactions/managed/nonscalar/_ToggleDebug.java
index 70c85dc..2dd9e3a 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/interactions/managed/nonscalar/_Debug.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/interactions/managed/nonscalar/_ToggleDebug.java
@@ -20,7 +20,7 @@ package org.apache.isis.core.metamodel.interactions.managed.nonscalar;
 
 import org.apache.isis.core.metamodel.spec.ManagedObject;
 
-class _Debug {
+class _ToggleDebug {
 
     public static void onSelectRowToggle(
             final ManagedObject rowElement,
@@ -28,7 +28,7 @@ class _Debug {
             final Boolean _new,
             final boolean isToggleAllEvent) {
 
-        System.err.printf("%s %b->%b (toggle-all: %b)%n",
+        System.err.printf("onSelectRowToggle: %s %b->%b (toggle-all: %b)%n",
                 ""+rowElement.getPojo(), old, _new, isToggleAllEvent);
     }
 
diff --git a/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/EntityCollectionModel.java b/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/EntityCollectionModel.java
index 6fc053b..86040fe 100644
--- a/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/EntityCollectionModel.java
+++ b/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/EntityCollectionModel.java
@@ -101,10 +101,16 @@ extends
 
     // -- BASIC PROPERTIES
 
-    @Deprecated // move to DataTableModel
-    int getCount();
-    @Deprecated // move to DataTableModel
-    String getName();
+    default int getElementCount() {
+        return getDataTableModel().getElementCount();
+    }
+
+    default String getName() {
+        return getDataTableModel().getTitle().getValue();
+    }
+
+    // -- DEPRECATIONS(?)
+
     @Deprecated // move to DataTableModel
     int getPageSize();
 
diff --git a/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/EntityCollectionModelAbstract.java b/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/EntityCollectionModelAbstract.java
index 8b96e50..e3b6baf 100644
--- a/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/EntityCollectionModelAbstract.java
+++ b/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/EntityCollectionModelAbstract.java
@@ -32,9 +32,7 @@ import org.apache.isis.core.runtime.context.IsisAppCommonContext;
 import org.apache.isis.viewer.wicket.model.links.LinkAndLabel;
 import org.apache.isis.viewer.wicket.model.models.interaction.coll.DataTableModelWkt;
 
-import lombok.Getter;
 import lombok.NonNull;
-import lombok.val;
 
 /**
  * Represents a collection (a member) of an entity.
@@ -50,9 +48,6 @@ implements EntityCollectionModel {
 
     private static final long serialVersionUID = 1L;
 
-    @Getter(onMethod_ = {@Override}) private final @NonNull Identifier identifier; //TODO don't memoize
-    @Getter private final int pageSize; //TODO don't memoize
-
     private final @NonNull Variant variant;
 
     protected EntityCollectionModelAbstract(
@@ -60,12 +55,6 @@ implements EntityCollectionModel {
             final @NonNull Variant variant) {
         super(dataTableModelWkt);
         this.variant = variant;
-
-        val memberMeta = getMetaModel();
-
-        this.identifier = memberMeta.getFeatureIdentifier();
-        this.pageSize = memberMeta.getPageSize()
-            .orElse(getVariant().getPageSizeDefault());
     }
 
     public final DataTableModelWkt delegate() {
@@ -94,6 +83,17 @@ implements EntityCollectionModel {
     }
 
     @Override
+    public int getPageSize() {
+        return getMetaModel().getPageSize()
+                .orElse(getVariant().getPageSizeDefault());
+    }
+
+    @Override
+    public Identifier getIdentifier() {
+        return getMetaModel().getFeatureIdentifier();
+    }
+
+    @Override
     public final ManagedObject getParentObject() {
         return delegate().getBookmarkedOwner();
     }
@@ -127,16 +127,4 @@ implements EntityCollectionModel {
         return Can.ofCollection(linkAndLabels);
     }
 
-    // -- DEPRECATIONS(?)
-
-    @Override
-    public final String getName() {
-        return getDataTableModel().getTitle().getValue();
-    }
-
-    @Override
-    public int getCount() {
-        return getDataTableModel().getDataElements().getValue().size();
-    }
-
 }
diff --git a/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/EntityCollectionModelHidden.java b/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/EntityCollectionModelHidden.java
index aafe152..887b937 100644
--- a/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/EntityCollectionModelHidden.java
+++ b/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/EntityCollectionModelHidden.java
@@ -36,7 +36,7 @@ extends EntityCollectionModelAbstract {
     }
 
     @Override
-    public int getCount() {
+    public int getElementCount() {
         return 0;
     }
 
diff --git a/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/interaction/act/ActionInteractionWkt.java b/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/interaction/act/ActionInteractionWkt.java
index 0f65d2a..8cc709d 100644
--- a/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/interaction/act/ActionInteractionWkt.java
+++ b/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/interaction/act/ActionInteractionWkt.java
@@ -90,7 +90,7 @@ extends HasBookmarkedOwnerAbstract<ActionInteraction> {
                 associatedWithCollectionIfAny);
     }
 
-    public ActionInteractionWkt(
+    private ActionInteractionWkt(
             final BookmarkedObjectWkt bookmarkedObject,
             final String memberId,
             final Where where,
@@ -119,10 +119,9 @@ extends HasBookmarkedOwnerAbstract<ActionInteraction> {
         }
 
         return associatedWithCollectionIfAny!=null
-                ? associatedWithCollectionIfAny.getDataTableModel()
-                        .startAssociatedActionInteraction(memberId, where)
+                ? ActionInteraction.startWithMultiselect(getBookmarkedOwner(), memberId, where,
+                        associatedWithCollectionIfAny.getDataTableModel())
                 : ActionInteraction.start(getBookmarkedOwner(), memberId, where);
-
     }
 
     public final ActionInteraction actionInteraction() {
diff --git a/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/interaction/coll/DataRowWkt.java b/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/interaction/coll/DataRowToggleWkt.java
similarity index 50%
copy from viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/interaction/coll/DataRowWkt.java
copy to viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/interaction/coll/DataRowToggleWkt.java
index 875518b..8818876 100644
--- a/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/interaction/coll/DataRowWkt.java
+++ b/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/interaction/coll/DataRowToggleWkt.java
@@ -18,48 +18,39 @@
  */
 package org.apache.isis.viewer.wicket.model.models.interaction.coll;
 
-import java.util.UUID;
-
 import org.apache.wicket.model.ChainingModel;
+import org.apache.wicket.model.IModel;
 
 import org.apache.isis.core.metamodel.interactions.managed.nonscalar.DataRow;
-import org.apache.isis.viewer.wicket.model.models.EntityCollectionModel;
+import org.apache.isis.core.metamodel.interactions.managed.nonscalar.DataTableModel;
 
-public class DataRowWkt
-extends ChainingModel<DataRow> {
+/**
+ * Boolean {@link IModel} to bind to the associated {@link DataTableModel}'s
+ * {@link DataRow} model to handle check-box selection.
+ */
+public class DataRowToggleWkt
+extends ChainingModel<Boolean> {
 
     private static final long serialVersionUID = 1L;
 
-    public static DataRowWkt chain(
-            final EntityCollectionModel entityCollectionModel,
-            final DataRow dataRow) {
-        return new DataRowWkt(entityCollectionModel, dataRow);
+    public DataRowToggleWkt(final DataRowWkt dataRowWkt) {
+        super(dataRowWkt);
     }
 
-    private final UUID uuid; // in support of client side sorting
-    private transient DataRow dataRow;
-
-    private DataRowWkt(
-            final EntityCollectionModel entityCollectionModel,
-            final DataRow dataRow) {
-        super(entityCollectionModel);
-        this.dataRow = dataRow;
-        this.uuid = dataRow.getUuid();
+    @Override
+    public Boolean getObject() {
+        return dataRow().getObject().getSelectToggle().getValue();
     }
 
-    EntityCollectionModel parent() {
-        return (EntityCollectionModel) super.getTarget();
+    @Override
+    public void setObject(final Boolean value) {
+        dataRow().getObject().getSelectToggle().setValue(value);
     }
 
-    @Override
-    public DataRow getObject() {
-        if(dataRow==null) {
-            dataRow = parent().getDataTableModel().getDataRowsFiltered().getValue().stream()
-                    .filter(dr->dr.getUuid().equals(uuid))
-                    .findFirst()
-                    .orElse(null);
-        }
-        return dataRow;
+    // -- HELPER
+
+    private DataRowWkt dataRow() {
+        return (DataRowWkt) super.getTarget();
     }
 
 }
diff --git a/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/interaction/coll/DataRowWkt.java b/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/interaction/coll/DataRowWkt.java
index 875518b..f2ed184 100644
--- a/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/interaction/coll/DataRowWkt.java
+++ b/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/interaction/coll/DataRowWkt.java
@@ -21,9 +21,13 @@ package org.apache.isis.viewer.wicket.model.models.interaction.coll;
 import java.util.UUID;
 
 import org.apache.wicket.model.ChainingModel;
+import org.apache.wicket.model.IModel;
 
 import org.apache.isis.core.metamodel.interactions.managed.nonscalar.DataRow;
-import org.apache.isis.viewer.wicket.model.models.EntityCollectionModel;
+import org.apache.isis.core.metamodel.interactions.managed.nonscalar.DataTableModel;
+
+import lombok.Getter;
+import lombok.NonNull;
 
 public class DataRowWkt
 extends ChainingModel<DataRow> {
@@ -31,35 +35,38 @@ extends ChainingModel<DataRow> {
     private static final long serialVersionUID = 1L;
 
     public static DataRowWkt chain(
-            final EntityCollectionModel entityCollectionModel,
+            final IModel<DataTableModel> dataTableModelHolder,
             final DataRow dataRow) {
-        return new DataRowWkt(entityCollectionModel, dataRow);
+        return new DataRowWkt(dataTableModelHolder, dataRow);
     }
 
-    private final UUID uuid; // in support of client side sorting
+    @Getter private final @NonNull UUID uuid; // in support of table sorting
+    @Getter private final @NonNull DataRowToggleWkt dataRowToggle;
+
     private transient DataRow dataRow;
 
     private DataRowWkt(
-            final EntityCollectionModel entityCollectionModel,
+            final IModel<DataTableModel> dataTableModelHolder,
             final DataRow dataRow) {
-        super(entityCollectionModel);
+        super(dataTableModelHolder);
         this.dataRow = dataRow;
         this.uuid = dataRow.getUuid();
-    }
-
-    EntityCollectionModel parent() {
-        return (EntityCollectionModel) super.getTarget();
+        this.dataRowToggle = new DataRowToggleWkt(this);
     }
 
     @Override
     public DataRow getObject() {
         if(dataRow==null) {
-            dataRow = parent().getDataTableModel().getDataRowsFiltered().getValue().stream()
-                    .filter(dr->dr.getUuid().equals(uuid))
-                    .findFirst()
+            dataRow = getDataTableModel().lookupDataRow(uuid)
                     .orElse(null);
         }
         return dataRow;
     }
 
+    // -- HELPER
+
+    private DataTableModel getDataTableModel() {
+        return ((DataTableModelWkt) super.getTarget()).getObject();
+    }
+
 }
diff --git a/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/interaction/coll/DataTableModelWkt.java b/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/interaction/coll/DataTableModelWkt.java
index 0c20aec..eda6a4d 100644
--- a/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/interaction/coll/DataTableModelWkt.java
+++ b/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/interaction/coll/DataTableModelWkt.java
@@ -120,7 +120,13 @@ implements
 
     @Override
     protected DataTableModel load() {
-        return tableMemento.getDataTableModel(getBookmarkedOwner());
+        val dataTableModel = tableMemento.getDataTableModel(getBookmarkedOwner());
+        return dataTableModel;
+    }
+
+    @Override
+    public void detach() {
+        // in support of table sorting, don't detach; instead reuse model
     }
 
 }
diff --git a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collection/CollectionPanel.java b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collection/CollectionPanel.java
index 308456f..4fc154a 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collection/CollectionPanel.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collection/CollectionPanel.java
@@ -29,7 +29,7 @@ import org.apache.isis.core.metamodel.interactions.managed.nonscalar.DataTableMo
 import org.apache.isis.viewer.common.model.components.ComponentType;
 import org.apache.isis.viewer.wicket.model.models.EntityCollectionModelParented;
 import org.apache.isis.viewer.wicket.ui.components.actionmenu.entityactions.LinkAndLabelFactory;
-import org.apache.isis.viewer.wicket.ui.components.collection.bulk.BulkActionsProvider;
+import org.apache.isis.viewer.wicket.ui.components.collection.bulk.MultiselectToggleProvider;
 import org.apache.isis.viewer.wicket.ui.components.collection.selector.CollectionSelectorPanel;
 import org.apache.isis.viewer.wicket.ui.components.collection.selector.CollectionSelectorProvider;
 import org.apache.isis.viewer.wicket.ui.components.collectioncontents.ajaxtable.columns.GenericToggleboxColumn;
@@ -51,7 +51,7 @@ public class CollectionPanel
 extends PanelAbstract<DataTableModel, EntityCollectionModelParented>
 implements
     CollectionSelectorProvider,
-    BulkActionsProvider {
+    MultiselectToggleProvider {
 
     private static final long serialVersionUID = 1L;
 
@@ -73,7 +73,6 @@ implements
         .collect(Can.toCan());
 
         collectionModel.setLinkAndLabels(associatedActions);
-
     }
 
     @Override
@@ -83,9 +82,11 @@ implements
     }
 
     private void buildGui() {
-        collectionContents = getComponentFactoryRegistry().addOrReplaceComponent(this, ComponentType.COLLECTION_CONTENTS, getModel());
+        collectionContents = getComponentFactoryRegistry()
+                .addOrReplaceComponent(this, ComponentType.COLLECTION_CONTENTS, getModel());
 
-        addOrReplace(new NotificationPanel(ID_FEEDBACK, collectionContents, new ComponentFeedbackMessageFilter(collectionContents)));
+        addOrReplace(new NotificationPanel(ID_FEEDBACK, collectionContents,
+                new ComponentFeedbackMessageFilter(collectionContents)));
 
         setOutputMarkupId(true);
     }
@@ -96,16 +97,17 @@ implements
         return this.label;
     }
 
-    // -- BULK SELECTION SUPPORT
+    // -- MULTI SELECTION SUPPORT
 
     private transient Optional<GenericToggleboxColumn> toggleboxColumn;
 
     @Override
     public GenericToggleboxColumn getToggleboxColumn() {
         if(toggleboxColumn == null) {
-            val collMetaModel = getModel().getMetaModel();
+            val collModel = getModel();
+            val collMetaModel = collModel.getMetaModel();
             toggleboxColumn =  collMetaModel.hasAssociatedActionsWithChoicesFromThisCollection()
-                    ? Optional.of(new GenericToggleboxColumn(super.getCommonContext()))
+                    ? Optional.of(new GenericToggleboxColumn(super.getCommonContext(), collModel.delegate()))
                     : Optional.empty();
         }
         return toggleboxColumn.orElse(null);
diff --git a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collection/bulk/BulkActionsProvider.java b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collection/bulk/MultiselectToggleProvider.java
similarity index 94%
rename from viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collection/bulk/BulkActionsProvider.java
rename to viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collection/bulk/MultiselectToggleProvider.java
index 97a19b3..981fa20 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collection/bulk/BulkActionsProvider.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collection/bulk/MultiselectToggleProvider.java
@@ -22,7 +22,7 @@ import java.io.Serializable;
 
 import org.apache.isis.viewer.wicket.ui.components.collectioncontents.ajaxtable.columns.GenericToggleboxColumn;
 
-public interface BulkActionsProvider extends Serializable {
+public interface MultiselectToggleProvider extends Serializable {
 
     GenericToggleboxColumn getToggleboxColumn();
 }
diff --git a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collection/selector/CollectionSelectorProvider.java b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collection/selector/CollectionSelectorProvider.java
index 1930e11..badfd74 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collection/selector/CollectionSelectorProvider.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collection/selector/CollectionSelectorProvider.java
@@ -32,8 +32,7 @@ public interface CollectionSelectorProvider {
     public static CollectionSelectorPanel getCollectionSelectorProvider(Component component) {
         while(component != null) {
             if(component instanceof CollectionSelectorProvider) {
-                final CollectionSelectorPanel selectorDropdownPanelIfAny = ((CollectionSelectorProvider) component).getSelectorDropdownPanel();
-                return selectorDropdownPanelIfAny;
+                return ((CollectionSelectorProvider) component).getSelectorDropdownPanel();
             }
             component = component.getParent();
         }
diff --git a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/ajaxtable/CollectionContentsAsAjaxTablePanel.java b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/ajaxtable/CollectionContentsAsAjaxTablePanel.java
index 98d1950..13b96b8 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/ajaxtable/CollectionContentsAsAjaxTablePanel.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/ajaxtable/CollectionContentsAsAjaxTablePanel.java
@@ -30,7 +30,7 @@ import org.apache.isis.core.metamodel.spec.ManagedObject;
 import org.apache.isis.core.metamodel.spec.feature.OneToOneAssociation;
 import org.apache.isis.viewer.wicket.model.models.EntityCollectionModel;
 import org.apache.isis.viewer.wicket.model.models.EntityCollectionModel.Variant;
-import org.apache.isis.viewer.wicket.ui.components.collection.bulk.BulkActionsProvider;
+import org.apache.isis.viewer.wicket.ui.components.collection.bulk.MultiselectToggleProvider;
 import org.apache.isis.viewer.wicket.ui.components.collection.count.CollectionCountProvider;
 import org.apache.isis.viewer.wicket.ui.components.collectioncontents.ajaxtable.columns.GenericColumn;
 import org.apache.isis.viewer.wicket.ui.components.collectioncontents.ajaxtable.columns.GenericPropertyColumn;
@@ -51,18 +51,28 @@ implements CollectionCountProvider {
     private static final long serialVersionUID = 1L;
     private static final String ID_TABLE = "table";
 
-    private IsisAjaxFallbackDataTable dataTable;
-
     public CollectionContentsAsAjaxTablePanel(final String id, final EntityCollectionModel model) {
         super(id, model);
     }
 
     @Override
+    public Integer getCount() {
+        return getModel().getDataTableModel().getElementCount();
+    }
+
+    @Override
     protected void onInitialize() {
         super.onInitialize();
         buildGui();
     }
 
+    @Override
+    protected void onModelChanged() {
+        //buildGui();
+    }
+
+    // -- HELPER
+
     private EntityCollectionModel entityCollectionModel() {
         return getModel();
     }
@@ -71,13 +81,13 @@ implements CollectionCountProvider {
 
         final List<GenericColumn> columns = _Lists.newArrayList();
 
-        // bulk actions
-        final BulkActionsProvider bulkActionsProvider = getBulkActionsProvider();
+        // multi select check boxes
+        final MultiselectToggleProvider multiselectToggleProvider = getMultiselectToggleProvider();
 
         GenericToggleboxColumn toggleboxColumn = null;
-        if(bulkActionsProvider != null) {
+        if(multiselectToggleProvider != null) {
 
-            toggleboxColumn = bulkActionsProvider.getToggleboxColumn();
+            toggleboxColumn = multiselectToggleProvider.getToggleboxColumn();
             if(toggleboxColumn != null) {
                 columns.add(toggleboxColumn);
             }
@@ -94,17 +104,16 @@ implements CollectionCountProvider {
         addPropertyColumnsIfRequired(columns);
 
         val dataProvider = new CollectionContentsSortableDataProvider(collectionModel);
-        dataTable = new IsisAjaxFallbackDataTable<>(
+        val dataTable = new IsisAjaxDataTable(
                 ID_TABLE, columns, dataProvider, collectionModel.getPageSize(), toggleboxColumn);
         addOrReplace(dataTable);
-
     }
 
-    private BulkActionsProvider getBulkActionsProvider() {
+    private MultiselectToggleProvider getMultiselectToggleProvider() {
         Component component = this;
         while(component != null) {
-            if(component instanceof BulkActionsProvider) {
-                return (BulkActionsProvider) component;
+            if(component instanceof MultiselectToggleProvider) {
+                return (MultiselectToggleProvider) component;
             }
             component = component.getParent();
         }
@@ -165,19 +174,4 @@ implements CollectionCountProvider {
                 property.getCanonicalDescription());
     }
 
-
-
-    @Override
-    protected void onModelChanged() {
-        buildGui();
-    }
-
-    @Override
-    public Integer getCount() {
-        final EntityCollectionModel model = getModel();
-        return model.getCount();
-    }
-
-
-
 }
diff --git a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/ajaxtable/CollectionContentsSortableDataProvider.java b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/ajaxtable/CollectionContentsSortableDataProvider.java
index 7228883..c870231 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/ajaxtable/CollectionContentsSortableDataProvider.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/ajaxtable/CollectionContentsSortableDataProvider.java
@@ -18,9 +18,7 @@
  */
 package org.apache.isis.viewer.wicket.ui.components.collectioncontents.ajaxtable;
 
-import java.util.Collections;
 import java.util.Iterator;
-import java.util.List;
 import java.util.Optional;
 
 import org.apache.wicket.extensions.ajax.markup.html.repeater.data.table.AjaxFallbackDefaultDataTable;
@@ -28,11 +26,12 @@ import org.apache.wicket.extensions.markup.html.repeater.util.SortParam;
 import org.apache.wicket.extensions.markup.html.repeater.util.SortableDataProvider;
 import org.apache.wicket.model.IModel;
 
-import org.apache.isis.commons.internal.collections._Lists;
+import org.apache.isis.commons.collections.Can;
 import org.apache.isis.core.metamodel.interactions.managed.nonscalar.DataRow;
+import org.apache.isis.core.metamodel.interactions.managed.nonscalar.DataTableModel;
 import org.apache.isis.core.metamodel.spec.ManagedObjects;
 import org.apache.isis.core.metamodel.spec.feature.OneToOneAssociation;
-import org.apache.isis.viewer.wicket.model.models.EntityCollectionModel;
+import org.apache.isis.viewer.wicket.model.models.EntityCollectionModelAbstract;
 import org.apache.isis.viewer.wicket.model.models.interaction.coll.DataRowWkt;
 
 import lombok.val;
@@ -45,75 +44,51 @@ extends SortableDataProvider<DataRow, String> {
 
     private static final long serialVersionUID = 1L;
 
-    private final EntityCollectionModel model;
+    private final IModel<DataTableModel> dataTableModelHolder;
 
-    public CollectionContentsSortableDataProvider(final EntityCollectionModel model) {
-        this.model = model;
+    public CollectionContentsSortableDataProvider(final IModel<DataTableModel> dataTableModelHolder) {
+        this.dataTableModelHolder = dataTableModelHolder instanceof EntityCollectionModelAbstract
+                ? ((EntityCollectionModelAbstract)dataTableModelHolder).delegate()
+                : dataTableModelHolder;
     }
 
-    @Override
-    public IModel<DataRow> model(final DataRow dataRow) {
-        return DataRowWkt.chain(model, dataRow);
+    public DataTableModel getDataTableModel() {
+        return dataTableModelHolder.getObject();
     }
 
     @Override
-    public long size() {
-        return model.getCount();
+    public IModel<DataRow> model(final DataRow dataRow) {
+        return DataRowWkt.chain(dataTableModelHolder, dataRow);
     }
 
     @Override
-    public void detach() {
-        super.detach();
-        model.detach();
-    }
-
-    public EntityCollectionModel getEntityCollectionModel() {
-        return model;
+    public long size() {
+        return getDataTableModel().getElementCount();
     }
 
     @Override
-    public Iterator<DataRow> iterator(final long first, final long count) {
-
-        val visibleRows = model.getDataTableModel().getDataRowsFiltered()
-                .getValue()
-                .toList();
-
-        final List<DataRow> sortedVisibleRows = sortedCopy(visibleRows, getSort());
-        final List<DataRow> pagedRows = subList(first, count, sortedVisibleRows);
-        return pagedRows.iterator();
+    public Iterator<DataRow> iterator(final long skip, final long limit) {
+        val visibleRows = getDataTableModel().getDataRowsFiltered()
+                .getValue();
+        return sorted(visibleRows).iterator(Math.toIntExact(skip), Math.toIntExact(limit));
     }
 
-    private List<DataRow> sortedCopy(
-            final List<DataRow> dataRows,
-            final SortParam<String> sort) {
+    // -- HELPER
 
-        var sortProperty = lookupPropertyFor(sort).orElse(null);
+    private Can<DataRow> sorted(final Can<DataRow> dataRows) {
+        val sort = getSort();
+        val sortProperty = lookupPropertyFor(sort).orElse(null);
         if(sortProperty != null) {
-            val copy = _Lists.newArrayList(dataRows);
             val objComparator = ManagedObjects.orderingBy(sortProperty, sort.isAscending());
-            Collections.sort(copy, (a, b)->objComparator.compare(a.getRowElement(), b.getRowElement()) );
-            return copy;
+            return dataRows.sorted((a, b)->objComparator.compare(a.getRowElement(), b.getRowElement()));
         }
-
         return dataRows;
     }
 
-    private static List<DataRow> subList(
-            final long first,
-            final long count,
-            final List<DataRow> dataRows) {
-
-        final int fromIndex = (int) first;
-        // if adapters where filter out (as invisible), then make sure don't run off the end
-        final int toIndex = Math.min((int) (first + count), dataRows.size());
-
-        return dataRows.subList(fromIndex, toIndex);
-    }
-
     private Optional<OneToOneAssociation> lookupPropertyFor(final SortParam<String> sort) {
         return Optional.ofNullable(sort)
         .map(SortParam::getProperty)
-        .flatMap(model.getElementType()::getProperty);
+        .flatMap(getDataTableModel().getElementType()::getProperty);
     }
 
 }
\ No newline at end of file
diff --git a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/ajaxtable/IsisAjaxFallbackDataTable.java b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/ajaxtable/IsisAjaxDataTable.java
similarity index 59%
rename from viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/ajaxtable/IsisAjaxFallbackDataTable.java
rename to viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/ajaxtable/IsisAjaxDataTable.java
index 35838ff..14031fe 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/ajaxtable/IsisAjaxFallbackDataTable.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/ajaxtable/IsisAjaxDataTable.java
@@ -18,33 +18,34 @@
  */
 package org.apache.isis.viewer.wicket.ui.components.collectioncontents.ajaxtable;
 
+import java.io.Serializable;
 import java.util.Iterator;
 import java.util.List;
-import java.util.Map;
+import java.util.UUID;
 
 import org.apache.wicket.ajax.AjaxRequestTarget;
 import org.apache.wicket.extensions.markup.html.repeater.data.sort.SortOrder;
 import org.apache.wicket.extensions.markup.html.repeater.data.table.DataTable;
 import org.apache.wicket.extensions.markup.html.repeater.data.table.IColumn;
-import org.apache.wicket.extensions.markup.html.repeater.data.table.ISortableDataProvider;
 import org.apache.wicket.extensions.markup.html.repeater.data.table.NoRecordsToolbar;
-import org.apache.wicket.markup.ComponentTag;
 import org.apache.wicket.markup.repeater.IItemFactory;
 import org.apache.wicket.markup.repeater.IItemReuseStrategy;
 import org.apache.wicket.markup.repeater.Item;
-import org.apache.wicket.markup.repeater.OddEvenItem;
 import org.apache.wicket.model.IModel;
-import org.apache.wicket.util.lang.Generics;
 
-import org.apache.isis.core.metamodel.spec.ObjectSpecification;
+import org.apache.isis.commons.internal.base._Casts;
+import org.apache.isis.commons.internal.collections._Maps;
+import org.apache.isis.core.metamodel.interactions.managed.nonscalar.DataRow;
+import org.apache.isis.core.metamodel.spec.ManagedObjects;
 import org.apache.isis.viewer.wicket.model.hints.UiHintContainer;
 import org.apache.isis.viewer.wicket.model.models.EntityModel;
+import org.apache.isis.viewer.wicket.model.models.interaction.coll.DataRowWkt;
 import org.apache.isis.viewer.wicket.ui.components.collectioncontents.ajaxtable.columns.GenericToggleboxColumn;
 import org.apache.isis.viewer.wicket.ui.util.Wkt;
 
 import lombok.val;
 
-public class IsisAjaxFallbackDataTable<T, S> extends DataTable<T, S> {
+public class IsisAjaxDataTable extends DataTable<DataRow, String> {
 
     private static final long serialVersionUID = 1L;
 
@@ -53,22 +54,44 @@ public class IsisAjaxFallbackDataTable<T, S> extends DataTable<T, S> {
     private final CollectionContentsSortableDataProvider dataProvider;
     private final GenericToggleboxColumn toggleboxColumn;
 
-    private IsisAjaxFallbackHeadersToolbar<S> headersToolbar;
+    private IsisAjaxHeadersToolbar headersToolbar;
     private IsisAjaxNavigationToolbar navigationToolbar;
 
-    public IsisAjaxFallbackDataTable(
+    public IsisAjaxDataTable(
             final String id,
-            final List<? extends IColumn<T, S>> columns,
+            final List<? extends IColumn<DataRow, String>> columns,
             final CollectionContentsSortableDataProvider dataProvider,
             final int rowsPerPage,
             final GenericToggleboxColumn toggleboxColumn) {
 
-        super(id, columns, (ISortableDataProvider<T, S>)dataProvider, rowsPerPage);
+        super(id, columns, dataProvider, rowsPerPage);
         this.dataProvider = dataProvider;
         this.toggleboxColumn = toggleboxColumn;
         setOutputMarkupId(true);
         setVersioned(false);
-        setItemReuseStrategy(new PreserveModelReuseStrategy());
+        setItemReuseStrategy((IItemReuseStrategy & Serializable) IsisAjaxDataTable::itemReuseStrategyWithCast);
+    }
+
+    public void setPageNumberHintAndBroadcast(final AjaxRequestTarget target) {
+        final UiHintContainer uiHintContainer = getUiHintContainer();
+        if(uiHintContainer == null) {
+            return;
+        }
+        uiHintContainer.setHint(this, IsisAjaxDataTable.UIHINT_PAGE_NUMBER, ""+getCurrentPage());
+    }
+
+    public void setSortOrderHintAndBroadcast(final SortOrder order, final String property, final AjaxRequestTarget target) {
+        final UiHintContainer uiHintContainer = getUiHintContainer();
+        if(uiHintContainer == null) {
+            return;
+        }
+
+        // first clear all SortOrder hints...
+        for (SortOrder eachSortOrder : SortOrder.values()) {
+            uiHintContainer.clearHint(this, eachSortOrder.name());
+        }
+        // .. then set this one
+        uiHintContainer.setHint(this, order.name(), property);
     }
 
     @Override
@@ -79,7 +102,7 @@ public class IsisAjaxFallbackDataTable<T, S> extends DataTable<T, S> {
     }
 
     private void buildGui() {
-        headersToolbar = new IsisAjaxFallbackHeadersToolbar<>(this, this.dataProvider);
+        headersToolbar = new IsisAjaxHeadersToolbar(this, this.dataProvider);
         addTopToolbar(headersToolbar);
 
         navigationToolbar = new IsisAjaxNavigationToolbar(this, this.toggleboxColumn);
@@ -96,83 +119,71 @@ public class IsisAjaxFallbackDataTable<T, S> extends DataTable<T, S> {
     }
 
     @Override
-    protected Item<T> newRowItem(final String id, final int index, final IModel<T> model)
-    {
-        return new OddEvenItem<T>(id, index, model) {
-            private static final long serialVersionUID = 1L;
-
-            @Override
-            protected void onComponentTag(final ComponentTag tag) {
-                super.onComponentTag(tag);
-
-                if (model instanceof EntityModel) {
-                    EntityModel entityModel = (EntityModel) model;
-                    val objectAdapter = entityModel.getObject();
-                    final ObjectSpecification typeOfSpecification = entityModel.getTypeOfSpecification();
-                    String cssClass = typeOfSpecification.getCssClass(objectAdapter);
-                    Wkt.cssAppend(tag, cssClass);
-                }
-            }
-        };
+    protected Item<DataRow> newRowItem(final String id, final int index, final IModel<DataRow> model) {
+        return Wkt.oddEvenItem(id, index, model, IsisAjaxDataTable::cssClassForRow);
     }
 
-    static class PreserveModelReuseStrategy implements IItemReuseStrategy {
-        private static final long serialVersionUID = 1L;
+    // -- HELPER
 
-        private static IItemReuseStrategy instance = new PreserveModelReuseStrategy();
-
-        /**
-         * @return static instance
-         */
-        public static IItemReuseStrategy getInstance()
-        {
-            return instance;
+    private static String cssClassForRow(final DataRow model) {
+        if(model==null
+                || ManagedObjects.isNullOrUnspecifiedOrEmpty(model.getRowElement())) {
+            return null;
         }
+        val rowElement = model.getRowElement();
+        return rowElement.getSpecification().getCssClass(rowElement);
+    }
 
-        /**
-         * @see org.apache.wicket.markup.repeater.IItemReuseStrategy#getItems(org.apache.wicket.markup.repeater.IItemFactory,
-         *      java.util.Iterator, java.util.Iterator)
-         */
-        @Override
-        public <T> Iterator<Item<T>> getItems(final IItemFactory<T> factory,
-                final Iterator<IModel<T>> newModels, final Iterator<Item<T>> existingItems)
-        {
-            final Map<IModel<T>, Item<T>> modelToItem = Generics.newHashMap();
-            while (existingItems.hasNext())
-            {
-                final Item<T> item = existingItems.next();
-                modelToItem.put(item.getModel(), item);
+    private static Iterator<Item<DataRow>> itemReuseStrategy(
+            final IItemFactory<DataRow> factory,
+            final Iterator<IModel<DataRow>> newModels,
+            final Iterator<Item<DataRow>> existingItems) {
+
+        val itemByUuid = _Maps.<UUID, Item<DataRow>>newHashMap();
+        existingItems.forEachRemaining(item->{
+            val model = item.getModel();
+            if(model instanceof DataRowWkt) {
+                val dataRowWkt = (DataRowWkt)item.getModel();
+                itemByUuid.put(dataRowWkt.getUuid(), item);
             }
+        });
 
-            return new Iterator<Item<T>>()
-            {
-                private int index = 0;
+        return new Iterator<Item<DataRow>>() {
+            private int index = 0;
 
-                @Override
-                public boolean hasNext()
-                {
-                    return newModels.hasNext();
-                }
+            @Override
+            public boolean hasNext() {
+                return newModels.hasNext();
+            }
 
-                @Override
-                public Item<T> next()
-                {
-                    final IModel<T> model = newModels.next();
-                    final Item<T> oldItem = modelToItem.get(model);
+            @Override
+            public Item<DataRow> next() {
+                final DataRowWkt newModel = (DataRowWkt)newModels.next();
+                final Item<DataRow> oldItem = itemByUuid.get(newModel.getUuid());
+
+                final IModel<DataRow> model2 = oldItem != null
+                        ? oldItem.getModel()
+                        : newModel;
+                return factory.newItem(index++, model2);
+            }
 
-                    final IModel<T> model2 = oldItem != null ? oldItem.getModel() : model;
-                    return factory.newItem(index++, model2);
-                }
+            @Override
+            public void remove() {
+                throw new UnsupportedOperationException();
+            }
 
-                @Override
-                public void remove()
-                {
-                    throw new UnsupportedOperationException();
-                }
+        };
 
-            };
-        }
+    }
 
+    private static <T> Iterator<Item<T>> itemReuseStrategyWithCast(
+            final IItemFactory<T> factory,
+            final Iterator<IModel<T>> newModels,
+            final Iterator<Item<T>> existingItems) {
+        return _Casts.uncheckedCast(itemReuseStrategy(
+                _Casts.uncheckedCast(factory),
+                _Casts.uncheckedCast(newModels),
+                _Casts.uncheckedCast(existingItems)));
     }
 
     private void honorHints() {
@@ -202,28 +213,6 @@ public class IsisAjaxFallbackDataTable<T, S> extends DataTable<T, S> {
         // don't broadcast (no AjaxRequestTarget, still configuring initial setup)
     }
 
-    public void setPageNumberHintAndBroadcast(final AjaxRequestTarget target) {
-        final UiHintContainer uiHintContainer = getUiHintContainer();
-        if(uiHintContainer == null) {
-            return;
-        }
-        uiHintContainer.setHint(this, IsisAjaxFallbackDataTable.UIHINT_PAGE_NUMBER, ""+getCurrentPage());
-    }
-
-    public void setSortOrderHintAndBroadcast(final SortOrder order, final String property, final AjaxRequestTarget target) {
-        final UiHintContainer uiHintContainer = getUiHintContainer();
-        if(uiHintContainer == null) {
-            return;
-        }
-
-        // first clear all SortOrder hints...
-        for (SortOrder eachSortOrder : SortOrder.values()) {
-            uiHintContainer.clearHint(this, eachSortOrder.name());
-        }
-        // .. then set this one
-        uiHintContainer.setHint(this, order.name(), property);
-    }
-
     private UiHintContainer getUiHintContainer() {
         return UiHintContainer.Util.hintContainerOf(this, EntityModel.class);
     }
diff --git a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/ajaxtable/IsisAjaxFallbackHeadersToolbar.java b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/ajaxtable/IsisAjaxFallbackHeadersToolbar.java
deleted file mode 100644
index e0cce3e..0000000
--- a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/ajaxtable/IsisAjaxFallbackHeadersToolbar.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- *  Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you under the Apache License, Version 2.0 (the
- *  "License"); you may not use this file except in compliance
- *  with the License.  You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing,
- *  software distributed under the License is distributed on an
- *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- *  KIND, either express or implied.  See the License for the
- *  specific language governing permissions and limitations
- *  under the License.
- */
-package org.apache.isis.viewer.wicket.ui.components.collectioncontents.ajaxtable;
-
-import org.apache.wicket.extensions.ajax.markup.html.repeater.data.table.AjaxFallbackHeadersToolbar;
-import org.apache.wicket.extensions.markup.html.repeater.data.sort.ISortStateLocator;
-import org.apache.wicket.extensions.markup.html.repeater.data.sort.SortOrder;
-import org.apache.wicket.markup.html.WebMarkupContainer;
-
-import org.apache.isis.commons.internal.base._Casts;
-import org.apache.isis.viewer.wicket.model.hints.UiHintContainer;
-import org.apache.isis.viewer.wicket.model.models.EntityModel;
-
-/**
- * Adapted from Wicket's own {@link AjaxFallbackHeadersToolbar}.
- */
-public class IsisAjaxFallbackHeadersToolbar<S> extends IsisAjaxHeadersToolbar<S>
-{
-    private static final long serialVersionUID = 1L;
-    private final CollectionContentsSortableDataProvider stateLocator;
-    private IsisAjaxFallbackDataTable<?, S> table;
-
-    public IsisAjaxFallbackHeadersToolbar(
-            final IsisAjaxFallbackDataTable<?, S> table,
-            final CollectionContentsSortableDataProvider stateLocator)
-    {
-        super(table, _Casts.uncheckedCast(stateLocator));
-        this.table = table;
-        table.setOutputMarkupId(true);
-        this.stateLocator = stateLocator;
-    }
-
-    @Override
-    protected void onInitialize() {
-        super.onInitialize();
-    }
-
-    // //////////////////////////////////////
-
-    @Override
-    protected WebMarkupContainer newSortableHeader(final String borderId, final S property,
-            final ISortStateLocator<S> locator)
-    {
-        return new IsisAjaxFallbackOrderByBorder<S>(borderId, table, property, locator/*, getAjaxCallListener()*/);
-    }
-
-    //    /**
-    //     * Returns a decorator that will be used to decorate ajax links used in sortable headers
-    //     *
-    //     * @return decorator or null for none
-    //     */
-    //    protected IAjaxCallListener getAjaxCallListener()
-    //    {
-    //        return null;
-    //    }
-
-    // //////////////////////////////////////
-
-    void honourSortOrderHints() {
-        UiHintContainer uiHintContainer = getUiHintContainer();
-        if(uiHintContainer == null) {
-            return;
-        }
-
-        for (SortOrder sortOrder : SortOrder.values()) {
-            String property = uiHintContainer.getHint(table, sortOrder.name());
-            if(property != null) {
-                stateLocator.getSortState().setPropertySortOrder(property, sortOrder);
-            }
-        }
-    }
-
-    private UiHintContainer getUiHintContainer() {
-        return UiHintContainer.Util.hintContainerOf(this, EntityModel.class);
-    }
-
-}
diff --git a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/ajaxtable/IsisAjaxFallbackOrderByBorder.java b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/ajaxtable/IsisAjaxFallbackOrderByBorder.java
index 2e59cae..7e17739 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/ajaxtable/IsisAjaxFallbackOrderByBorder.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/ajaxtable/IsisAjaxFallbackOrderByBorder.java
@@ -31,15 +31,15 @@ public class IsisAjaxFallbackOrderByBorder<T> extends AjaxFallbackOrderByBorder<
     private static final long serialVersionUID = 1L;
 
     private final T sortProperty;
-    private final IsisAjaxFallbackDataTable<?, ?> dataTable;
+    private final IsisAjaxDataTable dataTable;
 
     private final ISortStateLocator<T> stateLocator;
 
     public IsisAjaxFallbackOrderByBorder(
-            String id,
-            IsisAjaxFallbackDataTable<?, ?> dataTable,
-            T sortProperty,
-            ISortStateLocator<T> stateLocator
+            final String id,
+            final IsisAjaxDataTable dataTable,
+            final T sortProperty,
+            final ISortStateLocator<T> stateLocator
             /* removed in wicket 8, IAjaxCallListener ajaxCallListener*/) {
         super(id, sortProperty, stateLocator/* removed in wicket 8, ajaxCallListener*/);
         this.dataTable = dataTable;
@@ -48,8 +48,7 @@ public class IsisAjaxFallbackOrderByBorder<T> extends AjaxFallbackOrderByBorder<
     }
 
     @Override
-    protected void onAjaxClick(final AjaxRequestTarget target)
-    {
+    protected void onAjaxClick(final AjaxRequestTarget target) {
         target.add(dataTable);
 
         final UiHintContainer uiHintContainer = getUiHintContainer();
@@ -65,8 +64,7 @@ public class IsisAjaxFallbackOrderByBorder<T> extends AjaxFallbackOrderByBorder<
     }
 
     @Override
-    protected void onSortChanged()
-    {
+    protected void onSortChanged() {
         super.onSortChanged();
         // UI hint & event broadcast in onAjaxClick
         dataTable.setCurrentPage(0);
diff --git a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/ajaxtable/IsisAjaxHeadersToolbar.java b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/ajaxtable/IsisAjaxHeadersToolbar.java
index 12dcf4d..b23e937 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/ajaxtable/IsisAjaxHeadersToolbar.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/ajaxtable/IsisAjaxHeadersToolbar.java
@@ -18,205 +18,75 @@
  */
 package org.apache.isis.viewer.wicket.ui.components.collectioncontents.ajaxtable;
 
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
-
-import org.apache.wicket.Component;
-import org.apache.wicket.behavior.Behavior;
-import org.apache.wicket.extensions.markup.html.repeater.data.sort.ISortState;
+import org.apache.wicket.extensions.ajax.markup.html.repeater.data.table.AjaxFallbackHeadersToolbar;
 import org.apache.wicket.extensions.markup.html.repeater.data.sort.ISortStateLocator;
-import org.apache.wicket.extensions.markup.html.repeater.data.sort.OrderByBorder;
 import org.apache.wicket.extensions.markup.html.repeater.data.sort.SortOrder;
-import org.apache.wicket.extensions.markup.html.repeater.data.table.AbstractToolbar;
-import org.apache.wicket.extensions.markup.html.repeater.data.table.DataTable;
-import org.apache.wicket.extensions.markup.html.repeater.data.table.HeadersToolbar;
-import org.apache.wicket.extensions.markup.html.repeater.data.table.IColumn;
-import org.apache.wicket.extensions.markup.html.repeater.data.table.IStyledColumn;
-import org.apache.wicket.markup.ComponentTag;
-import org.apache.wicket.markup.html.WebComponent;
 import org.apache.wicket.markup.html.WebMarkupContainer;
-import org.apache.wicket.markup.repeater.Item;
-import org.apache.wicket.markup.repeater.RefreshingView;
-import org.apache.wicket.model.IModel;
-import org.apache.wicket.model.Model;
-import org.apache.wicket.util.string.Strings;
-
-import org.apache.isis.viewer.wicket.ui.components.collectioncontents.ajaxtable.columns.GenericTitleColumn;
-import org.apache.isis.viewer.wicket.ui.util.Wkt;
-
-import de.agilecoders.wicket.core.util.Attributes;
 
+import org.apache.isis.commons.internal.base._Casts;
+import org.apache.isis.viewer.wicket.model.hints.UiHintContainer;
+import org.apache.isis.viewer.wicket.model.models.EntityModel;
 
 /**
- * Adapted from Wicket's own {@link HeadersToolbar}.
+ * Adapted from Wicket's own {@link AjaxFallbackHeadersToolbar}.
  */
-public class IsisAjaxHeadersToolbar<S> extends AbstractToolbar
-{
-    private static final long serialVersionUID = 1L;
+public class IsisAjaxHeadersToolbar
+extends IsisAjaxHeadersToolbarAbstract<String> {
 
-    private static final String CLASS_SORT_NONE = "fa fa-fw fa-sort";
-    private static final String CLASS_SORT_UP = "fa fa-fw fa-sort-up";
-    private static final String CLASS_SORT_DOWN = "fa fa-fw fa-sort-down";
-
-    static abstract class CssAttributeBehavior extends Behavior
-    {
-        private static final long serialVersionUID = 1L;
-
-        protected abstract String getCssClass();
-
-        /**
-         * @see Behavior#onComponentTag(Component, ComponentTag)
-         */
-        @Override
-        public void onComponentTag(final Component component, final ComponentTag tag)
-        {
-            String className = getCssClass();
-            if (!Strings.isEmpty(className))
-            {
-                tag.append("class", className, " ");
-            }
-        }
+    private static final long serialVersionUID = 1L;
+    private final CollectionContentsSortableDataProvider stateLocator;
+    private IsisAjaxDataTable table;
+
+    public IsisAjaxHeadersToolbar(
+            final IsisAjaxDataTable table,
+            final CollectionContentsSortableDataProvider stateLocator) {
+        super(table, _Casts.uncheckedCast(stateLocator));
+        this.table = table;
+        table.setOutputMarkupId(true);
+        this.stateLocator = stateLocator;
     }
 
-    /**
-     * Constructor
-     *
-     * @param <T>
-     *            the column data type
-     * @param table
-     *            data table this toolbar will be attached to
-     * @param stateLocator
-     *            locator for the ISortState implementation used by sortable headers
-     */
-    public <T> IsisAjaxHeadersToolbar(final DataTable<T, S> table, final ISortStateLocator<S> stateLocator)
-    {
-        super(table);
-
-        RefreshingView<IColumn<T, S>> headers = new RefreshingView<IColumn<T, S>>("headers")
-        {
-            private static final long serialVersionUID = 1L;
-
-            @Override
-            protected Iterator<IModel<IColumn<T, S>>> getItemModels()
-            {
-                List<IModel<IColumn<T, S>>> columnsModels = new LinkedList<IModel<IColumn<T, S>>>();
-
-                for (IColumn<T, S> column : table.getColumns())
-                {
-                    columnsModels.add(Model.of(column));
-                }
-
-                return columnsModels.iterator();
-            }
-
-            @Override
-            protected void populateItem(final Item<IColumn<T, S>> item)
-            {
-                final IColumn<T, S> column = item.getModelObject();
-
-                WebMarkupContainer header;
-
-                if (column.isSortable())
-                {
-                    header = newSortableHeader("header", column.getSortProperty(), stateLocator);
-
-                    if (column instanceof IStyledColumn)
-                    {
-                        CssAttributeBehavior cssAttributeBehavior = new CssAttributeBehavior()
-                        {
-                            private static final long serialVersionUID = 1L;
-
-                            @Override
-                            protected String getCssClass()
-                            {
-                                return ((IStyledColumn<?, S>)column).getCssClass();
-                            }
-                        };
-
-                        header.add(cssAttributeBehavior);
-                    }
-
-                }
-                else
-                {
-                    header = new WebMarkupContainer("header");
-                }
-
+    @Override
+    protected void onInitialize() {
+        super.onInitialize();
+    }
 
-                item.add(header);
-                item.setRenderBodyOnly(true);
-                Component label = column.getHeader("label");
-                Component sortIcon = newSortIcon("sortIcon", column, stateLocator);
-                header.add(label, sortIcon);
+    // //////////////////////////////////////
 
-                if(column instanceof GenericTitleColumn) {
-                    Wkt.cssAppend(header, "title-column");
-                }
-            }
-        };
-        add(headers);
+    @Override
+    protected WebMarkupContainer newSortableHeader(final String borderId, final String property,
+            final ISortStateLocator<String> locator) {
+        return new IsisAjaxFallbackOrderByBorder<String>(borderId, table, property, locator/*, getAjaxCallListener()*/);
     }
 
-    /**
-     * Factory method for the sort icon
-     *
-     * @param id
-     *          the component id
-     * @param column
-     *          the column for which a sort icon is needed
-     * @param stateLocator
-     *          locator for the ISortState implementation used by sortable headers
-     * @param <T>
-     *          The model object type of the data table
-     * @return A component that should be used as a sort icon
-     */
-    protected <T> Component newSortIcon(final String id, final IColumn<T, S> column, final ISortStateLocator<S> stateLocator) {
-        return new WebComponent(id) {
-            @Override
-            protected void onComponentTag(final ComponentTag tag) {
-                super.onComponentTag(tag);
+    //    /**
+    //     * Returns a decorator that will be used to decorate ajax links used in sortable headers
+    //     *
+    //     * @return decorator or null for none
+    //     */
+    //    protected IAjaxCallListener getAjaxCallListener()
+    //    {
+    //        return null;
+    //    }
+
+    // //////////////////////////////////////
+
+    void honourSortOrderHints() {
+        UiHintContainer uiHintContainer = getUiHintContainer();
+        if(uiHintContainer == null) {
+            return;
+        }
 
-                if(column.isSortable()) {
-                    ISortState<S> sortState = stateLocator.getSortState();
-                    S sortProperty = column.getSortProperty();
-                    SortOrder sortOrder = sortProperty == null ? SortOrder.NONE : sortState.getPropertySortOrder(sortProperty);
-                    if (sortOrder == SortOrder.ASCENDING) {
-                        Attributes.addClass(tag, CLASS_SORT_UP);
-                    } else if (sortOrder == SortOrder.DESCENDING) {
-                        Attributes.addClass(tag, CLASS_SORT_DOWN);
-                    } else {
-                        Attributes.addClass(tag, CLASS_SORT_NONE);
-                    }
-                }
+        for (SortOrder sortOrder : SortOrder.values()) {
+            String property = uiHintContainer.getHint(table, sortOrder.name());
+            if(property != null) {
+                stateLocator.getSortState().setPropertySortOrder(property, sortOrder);
             }
-        };
+        }
     }
 
-    /**
-     * Factory method for sortable header components. A sortable header component must have id of
-     * <code>headerId</code> and conform to markup specified in <code>HeadersToolbar.html</code>
-     *
-     * @param headerId
-     *            header component id
-     * @param property
-     *            property this header represents
-     * @param locator
-     *            sort state locator
-     * @return created header component
-     */
-    protected WebMarkupContainer newSortableHeader(final String headerId, final S property,
-            final ISortStateLocator<S> locator)
-    {
-        return new OrderByBorder<S>(headerId, property, locator)
-        {
-            private static final long serialVersionUID = 1L;
-
-            @Override
-            protected void onSortChanged()
-            {
-                getTable().setCurrentPage(0);
-            }
-        };
+    private UiHintContainer getUiHintContainer() {
+        return UiHintContainer.Util.hintContainerOf(this, EntityModel.class);
     }
+
 }
diff --git a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/ajaxtable/IsisAjaxHeadersToolbar.java b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/ajaxtable/IsisAjaxHeadersToolbarAbstract.java
similarity index 97%
copy from viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/ajaxtable/IsisAjaxHeadersToolbar.java
copy to viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/ajaxtable/IsisAjaxHeadersToolbarAbstract.java
index 12dcf4d..ec56f5d 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/ajaxtable/IsisAjaxHeadersToolbar.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/ajaxtable/IsisAjaxHeadersToolbarAbstract.java
@@ -51,7 +51,7 @@ import de.agilecoders.wicket.core.util.Attributes;
 /**
  * Adapted from Wicket's own {@link HeadersToolbar}.
  */
-public class IsisAjaxHeadersToolbar<S> extends AbstractToolbar
+public abstract class IsisAjaxHeadersToolbarAbstract<S> extends AbstractToolbar
 {
     private static final long serialVersionUID = 1L;
 
@@ -89,7 +89,7 @@ public class IsisAjaxHeadersToolbar<S> extends AbstractToolbar
      * @param stateLocator
      *            locator for the ISortState implementation used by sortable headers
      */
-    public <T> IsisAjaxHeadersToolbar(final DataTable<T, S> table, final ISortStateLocator<S> stateLocator)
+    public <T> IsisAjaxHeadersToolbarAbstract(final DataTable<T, S> table, final ISortStateLocator<S> stateLocator)
     {
         super(table);
 
diff --git a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/ajaxtable/IsisAjaxNavigationToolbar.java b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/ajaxtable/IsisAjaxNavigationToolbar.java
index 4558a15..93b2ba1 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/ajaxtable/IsisAjaxNavigationToolbar.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/ajaxtable/IsisAjaxNavigationToolbar.java
@@ -24,7 +24,6 @@ import org.apache.wicket.extensions.markup.html.repeater.data.table.DataTable;
 import org.apache.wicket.markup.html.navigation.paging.PagingNavigator;
 
 import org.apache.isis.viewer.wicket.model.hints.UiHintContainer;
-import org.apache.isis.viewer.wicket.model.models.EntityCollectionModel;
 import org.apache.isis.viewer.wicket.model.models.EntityModel;
 import org.apache.isis.viewer.wicket.ui.components.collectioncontents.ajaxtable.columns.GenericToggleboxColumn;
 import org.apache.isis.viewer.wicket.ui.util.Wkt;
@@ -63,12 +62,12 @@ public class IsisAjaxNavigationToolbar extends AjaxNavigationToolbar {
             showAllItemsOn(table);
 
             final DataTable<?, ?> dataTable = getTable();
-            final CollectionContentsSortableDataProvider dataProvider = (CollectionContentsSortableDataProvider) dataTable.getDataProvider();
-            final EntityCollectionModel collectionModel = dataProvider.getEntityCollectionModel();
+            final CollectionContentsSortableDataProvider dataProvider =
+                    (CollectionContentsSortableDataProvider) dataTable.getDataProvider();
 
             if(toggleboxColumn != null) {
                 // clear the underlying backend selection model
-                collectionModel.getDataTableModel().getSelectAllToggle().setValue(false);
+                dataProvider.getDataTableModel().getSelectAllToggle().setValue(false);
                 // remove toggle UI components
                 toggleboxColumn.removeToggles();
             }
diff --git a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/ajaxtable/IsisAjaxPagingNavigationIncrementLink.java b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/ajaxtable/IsisAjaxPagingNavigationIncrementLink.java
index d48cdf8..5ec13a0 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/ajaxtable/IsisAjaxPagingNavigationIncrementLink.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/ajaxtable/IsisAjaxPagingNavigationIncrementLink.java
@@ -32,23 +32,23 @@ public class IsisAjaxPagingNavigationIncrementLink extends AjaxPagingNavigationI
 
     private static final long serialVersionUID = 1L;
 
-    private final IsisAjaxFallbackDataTable<?, ?> dataTable;
+    private final IsisAjaxDataTable dataTable;
     private final Component component;
 
-    public IsisAjaxPagingNavigationIncrementLink(String id, IPageable pageable, int increment) {
+    public IsisAjaxPagingNavigationIncrementLink(final String id, final IPageable pageable, final int increment) {
         super(id, pageable, increment);
-        dataTable = (IsisAjaxFallbackDataTable<?, ?>) pageable;
+        dataTable = (IsisAjaxDataTable) pageable;
         component = pageable instanceof Component ? (Component) pageable : null;
     }
 
     @Override
-    public void onClick(AjaxRequestTarget target) {
+    public void onClick(final AjaxRequestTarget target) {
         super.onClick(target);
         dataTable.setPageNumberHintAndBroadcast(target);
     }
 
     @Override
-    protected AjaxPagingNavigationBehavior newAjaxPagingNavigationBehavior(IPageable pageable, String event) {
+    protected AjaxPagingNavigationBehavior newAjaxPagingNavigationBehavior(final IPageable pageable, final String event) {
         return new BootstrapAjaxPagingNavigationBehavior(this, pageable, event);
     }
 
diff --git a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/ajaxtable/IsisAjaxPagingNavigationLink.java b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/ajaxtable/IsisAjaxPagingNavigationLink.java
index 9deb52f..0ae66b0 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/ajaxtable/IsisAjaxPagingNavigationLink.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/ajaxtable/IsisAjaxPagingNavigationLink.java
@@ -33,23 +33,23 @@ import de.agilecoders.wicket.core.util.Attributes;
 public class IsisAjaxPagingNavigationLink extends AjaxPagingNavigationLink {
 
     private static final long serialVersionUID = 1L;
-    private final IsisAjaxFallbackDataTable<?, ?> dataTable;
+    private final IsisAjaxDataTable dataTable;
     private final Component component;
 
-    public IsisAjaxPagingNavigationLink(String id, IPageable pageable, long pageNumber) {
+    public IsisAjaxPagingNavigationLink(final String id, final IPageable pageable, final long pageNumber) {
         super(id, pageable, pageNumber);
-        dataTable = (IsisAjaxFallbackDataTable<?, ?>) pageable;
+        dataTable = (IsisAjaxDataTable) pageable;
         component = pageable instanceof Component ? (Component) pageable : null;
     }
 
     @Override
-    public void onClick(AjaxRequestTarget target) {
+    public void onClick(final AjaxRequestTarget target) {
         super.onClick(target);
         dataTable.setPageNumberHintAndBroadcast(target);
     }
 
     @Override
-    protected AjaxPagingNavigationBehavior newAjaxPagingNavigationBehavior(IPageable pageable, String event) {
+    protected AjaxPagingNavigationBehavior newAjaxPagingNavigationBehavior(final IPageable pageable, final String event) {
         return new BootstrapAjaxPagingNavigationBehavior(this, pageable, event);
     }
 
@@ -59,7 +59,7 @@ public class IsisAjaxPagingNavigationLink extends AjaxPagingNavigationLink {
     }
 
 	@Override
-	protected void onComponentTag(ComponentTag tag) {
+	protected void onComponentTag(final ComponentTag tag) {
 		super.onComponentTag(tag);
 
 		Attributes.addClass(tag, "page-link");
diff --git a/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/EntityCollectionModelHidden.java b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/ajaxtable/columns/BulkToggleWkt.java
similarity index 52%
copy from viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/EntityCollectionModelHidden.java
copy to viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/ajaxtable/columns/BulkToggleWkt.java
index aafe152..4f4881a 100644
--- a/viewers/wicket/model/src/main/java/org/apache/isis/viewer/wicket/model/models/EntityCollectionModelHidden.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/ajaxtable/columns/BulkToggleWkt.java
@@ -16,28 +16,37 @@
  *  specific language governing permissions and limitations
  *  under the License.
  */
-package org.apache.isis.viewer.wicket.model.models;
+package org.apache.isis.viewer.wicket.ui.components.collectioncontents.ajaxtable.columns;
 
-import lombok.NonNull;
+import org.apache.wicket.model.ChainingModel;
+import org.apache.wicket.model.IModel;
 
-public class EntityCollectionModelHidden
-extends EntityCollectionModelAbstract {
+import org.apache.isis.core.metamodel.interactions.managed.nonscalar.DataTableModel;
+
+public class BulkToggleWkt
+extends ChainingModel<Boolean> {
 
     private static final long serialVersionUID = 1L;
 
-    public static EntityCollectionModelHidden forCollectionModel(
-            final @NonNull EntityCollectionModelAbstract collectionModel) {
-        return new EntityCollectionModelHidden(collectionModel);
+    public BulkToggleWkt(final IModel<DataTableModel> dataTableModelHolder) {
+        super(dataTableModelHolder);
     }
 
-    protected EntityCollectionModelHidden(
-            final @NonNull EntityCollectionModelAbstract collectionModel) {
-        super(collectionModel.delegate(), collectionModel.getVariant());
+    @Override
+    public Boolean getObject() {
+        return dataTableModel().getSelectAllToggle().getValue();
     }
 
     @Override
-    public int getCount() {
-        return 0;
+    public void setObject(final Boolean value) {
+        dataTableModel().getSelectAllToggle().setValue(value);
+    }
+
+    // -- HELPER
+
+    @SuppressWarnings("unchecked")
+    private DataTableModel dataTableModel() {
+        return ((IModel<DataTableModel>) super.getTarget()).getObject();
     }
 
 }
diff --git a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/ajaxtable/columns/GenericToggleboxColumn.java b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/ajaxtable/columns/GenericToggleboxColumn.java
index add1994..6981d4b 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/ajaxtable/columns/GenericToggleboxColumn.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/ajaxtable/columns/GenericToggleboxColumn.java
@@ -29,7 +29,9 @@ import org.apache.wicket.model.IModel;
 
 import org.apache.isis.commons.internal.collections._Lists;
 import org.apache.isis.core.metamodel.interactions.managed.nonscalar.DataRow;
+import org.apache.isis.core.metamodel.interactions.managed.nonscalar.DataTableModel;
 import org.apache.isis.core.runtime.context.IsisAppCommonContext;
+import org.apache.isis.viewer.wicket.model.models.interaction.coll.DataRowWkt;
 import org.apache.isis.viewer.wicket.ui.components.widgets.checkbox.ContainedToggleboxPanel;
 import org.apache.isis.viewer.wicket.ui.util.Wkt;
 
@@ -48,23 +50,25 @@ extends GenericColumnAbstract {
         public boolean isSetAll() { return this == SET_ALL; }
     }
 
+    private IModel<DataTableModel> dataTableModelHolder;
 
     public GenericToggleboxColumn(
-            final IsisAppCommonContext commonContext) {
+            final IsisAppCommonContext commonContext,
+            final IModel<DataTableModel> dataTableModelHolder
+            ) {
         super(commonContext, "");
+        this.dataTableModelHolder = dataTableModelHolder;
     }
 
     @Override
     public Component getHeader(final String componentId) {
 
-        val bulkToggle = new ContainedToggleboxPanel(componentId) {
+        val bulkToggle = new ContainedToggleboxPanel(componentId, new BulkToggleWkt(dataTableModelHolder)) {
             private static final long serialVersionUID = 1L;
-            @Override
-            public void onSubmit(final AjaxRequestTarget target) {
-                val bulkToggle = BulkToggle.valueOf(!this.isChecked());
-                //System.err.printf("bulkToggle: %s%n", bulkToggle);
+            @Override public void onUpdate(final AjaxRequestTarget target) {
+                val bulkToggle = BulkToggle.valueOf(this.isChecked());
                 for (ContainedToggleboxPanel rowToggle : rowToggles) {
-                    rowToggle.smartSet(bulkToggle, target);
+                    rowToggle.set(bulkToggle, target);
                 }
             }
         };
@@ -84,16 +88,7 @@ extends GenericColumnAbstract {
 
         final MarkupContainer row = cellItem.getParent().getParent();
         row.setOutputMarkupId(true);
-
-        val rowToggle = new ContainedToggleboxPanel(componentId) {
-            private static final long serialVersionUID = 1L;
-            @Override
-            public void onSubmit(final AjaxRequestTarget target) {
-                val isChecked = rowModel.getObject().getSelectToggle().toggleThenGet();
-                // no matter what, the underlying backend model must be reflected by the UI
-                setModel(isChecked);
-            }
-        };
+        val rowToggle = new ContainedToggleboxPanel(componentId, ((DataRowWkt)rowModel).getDataRowToggle());
         rowToggles.add(rowToggle);
         rowToggle.setOutputMarkupId(true);
         cellItem.add(rowToggle);
diff --git a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/summary/CollectionContentsAsSummary.java b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/summary/CollectionContentsAsSummary.java
index 3de1461..97f7c28 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/summary/CollectionContentsAsSummary.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/summary/CollectionContentsAsSummary.java
@@ -208,7 +208,7 @@ implements CollectionCountProvider {
     @Override
     public Integer getCount() {
         final EntityCollectionModel model = getModel();
-        return model.getCount();
+        return model.getElementCount();
     }
 
 }
diff --git a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/standalonecollection/StandaloneCollectionPanel.java b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/standalonecollection/StandaloneCollectionPanel.java
index d46c6a3..96f4d85 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/standalonecollection/StandaloneCollectionPanel.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/standalonecollection/StandaloneCollectionPanel.java
@@ -114,7 +114,7 @@ implements CollectionCountProvider, CollectionSelectorProvider {
     @Override
     public Integer getCount() {
         final EntityCollectionModel model = getModel();
-        return model.getCount();
+        return model.getElementCount();
     }
 
 }
diff --git a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/widgets/checkbox/ContainedToggleboxPanel.java b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/widgets/checkbox/ContainedToggleboxPanel.java
index 16f5c1c..901cdc3 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/widgets/checkbox/ContainedToggleboxPanel.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/widgets/checkbox/ContainedToggleboxPanel.java
@@ -22,6 +22,7 @@ import org.apache.wicket.ajax.AjaxRequestTarget;
 import org.apache.wicket.ajax.markup.html.form.AjaxCheckBox;
 import org.apache.wicket.markup.html.WebMarkupContainer;
 import org.apache.wicket.markup.html.form.Form;
+import org.apache.wicket.model.IModel;
 import org.apache.wicket.model.Model;
 
 import org.apache.isis.viewer.wicket.ui.components.collectioncontents.ajaxtable.columns.GenericToggleboxColumn.BulkToggle;
@@ -42,6 +43,10 @@ extends PanelAbstract<Boolean, Model<Boolean>> {
     private final AjaxCheckBox checkbox;
 
     public ContainedToggleboxPanel(final String id) {
+        this(id, Model.of(false));
+    }
+
+    public ContainedToggleboxPanel(final String id, final IModel<Boolean> model) {
         super(id);
 
         final WebMarkupContainer markupContainer = new WebMarkupContainer(ID_CONTAINER);
@@ -50,13 +55,12 @@ extends PanelAbstract<Boolean, Model<Boolean>> {
         final Form<Object> form = new Form<Object>(ID_FORM);
         markupContainer.add(form);
 
-        checkbox = new AjaxCheckBox(ID_TOGGLEBOX, Model.of(false)) {
+        checkbox = new AjaxCheckBox(ID_TOGGLEBOX, model) {
             private static final long serialVersionUID = 1L;
 
             @Override
             protected void onUpdate(final AjaxRequestTarget target) {
-
-                ContainedToggleboxPanel.this.toggle(target);
+                ContainedToggleboxPanel.this.onUpdate(target);
             }
 
         };
@@ -67,34 +71,21 @@ extends PanelAbstract<Boolean, Model<Boolean>> {
     /**
      * Hook method for (typically anonymous) subclasses to override.
      */
-    public void onSubmit(final AjaxRequestTarget target) {
-    }
-
-    /**
-     * Programmatic toggling.
-     * @param target
-     */
-    public void toggle(final AjaxRequestTarget target) {
-        setModel(!isChecked());
-        onSubmit(target);
+    protected void onUpdate(final AjaxRequestTarget target) {
     }
 
-    public boolean isChecked() {
+    protected boolean isChecked() {
         return checkbox.getModelObject();
     }
 
-    public void setModel(final boolean isChecked) {
+    protected void setModel(final boolean isChecked) {
         checkbox.setModelObject(isChecked);
     }
 
-    public void smartSet(
+    public void set(
             final BulkToggle bulkToggle,
             final AjaxRequestTarget target) {
-        if(isChecked()==bulkToggle.isSetAll()) {
-            //smart update idiom: skip if no change of state
-            return;
-        }
-        toggle(target);
+        setModel(bulkToggle.isSetAll());
         target.add(this);
     }
 
diff --git a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/util/Wkt.java b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/util/Wkt.java
index eb23f7e..ad0db79 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/util/Wkt.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/util/Wkt.java
@@ -43,6 +43,8 @@ import org.apache.wicket.markup.html.image.Image;
 import org.apache.wicket.markup.html.list.ListItem;
 import org.apache.wicket.markup.html.list.ListView;
 import org.apache.wicket.markup.html.panel.Fragment;
+import org.apache.wicket.markup.repeater.Item;
+import org.apache.wicket.markup.repeater.OddEvenItem;
 import org.apache.wicket.model.IModel;
 import org.apache.wicket.request.resource.ResourceReference;
 import org.apache.wicket.util.convert.IConverter;
@@ -57,16 +59,18 @@ import org.apache.isis.applib.Identifier;
 import org.apache.isis.commons.internal.base._Strings;
 import org.apache.isis.commons.internal.debug._Probe;
 import org.apache.isis.commons.internal.debug._Probe.EntryPoint;
+import org.apache.isis.commons.internal.functions._Functions.SerializableFunction;
 import org.apache.isis.viewer.wicket.model.isis.WicketViewerSettings;
 import org.apache.isis.viewer.wicket.ui.panels.PanelUtil;
 
-import de.agilecoders.wicket.core.markup.html.bootstrap.behavior.CssClassNameAppender;
-import de.agilecoders.wicket.core.markup.html.bootstrap.button.Buttons;
-import de.agilecoders.wicket.core.util.Attributes;
 import lombok.NonNull;
 import lombok.val;
 import lombok.experimental.UtilityClass;
 
+import de.agilecoders.wicket.core.markup.html.bootstrap.behavior.CssClassNameAppender;
+import de.agilecoders.wicket.core.markup.html.bootstrap.button.Buttons;
+import de.agilecoders.wicket.core.util.Attributes;
+
 /**
  * Wicket common idioms, in alphabetical order.
  */
@@ -474,6 +478,23 @@ public class Wkt {
         return add(container, listView(id, listModel, itemPopulator));
     }
 
+    // -- TABLES
+
+    public <T> Item<T> oddEvenItem(
+            final String id, final int index, final IModel<T> model,
+            final SerializableFunction<T, String> cssClassProvider) {
+
+        return new OddEvenItem<T>(id, index, model) {
+            private static final long serialVersionUID = 1L;
+
+            @Override
+            protected void onComponentTag(final ComponentTag tag) {
+                super.onComponentTag(tag);
+                Wkt.cssAppend(tag, cssClassProvider.apply(model.getObject()));
+            }
+        };
+    }
+
     // -- TEXT AREA
 
     public TextArea<String> textAreaNoTab(final String id, final IModel<String> textModel) {
@@ -592,5 +613,4 @@ public class Wkt {
     }
 
 
-
 }