How to implement freeze column in GXT 3.x?
Asked Answered
S

2

8

How can frozen columns be implemented in GXT 3.x (from Sencha)? Ext-JS, another product from Sencha seems to implement this, but I can't see where the Java based GXT implement the same thing:

http://dev.sencha.com/deploy/ext-4.0.0/examples/grid/locking-grid.html

Sipes answered 13/2, 2013 at 8:13 Comment(0)
S
13

The basic idea is that you need two different scrolling containers, one with the fixed column(s), one with the scrolling columns. Each of these need to be in a distinct viewport, so the standard Grid/GridView doesn't work well with this - they make assumptions about how scrolling should behave, so simply subclassing one or both is likely to be fairly involved.

Instead, you can build two grids, one for the locked columns, one for the scrolling ones. Each can deal with their own ColumnConfig classes, to draw headers and rows, and will be linked to the same ListStore to ensure their data is in sync - changes in the store will be passed along to both listening grids.

To get the full effect, some additional wiring will be needed:

  • Linking scrolling. Listen to the BodyScrollEvent from each Grid, and scroll the other to the same place (changing only top, not left, since you don't want one to control the other).
  • Sizing is the second big piece - both grids need their scrollable height to be the same, but the horizontal scrolling one needs a buffer on the bottom when it is actually showing that scrollbar. Usually the Grid is told to size based on its parent's instructions, though sometimes you directly size the Grid - in this case, this step is not needed, just size the two grids slightly differently. Otherwise, you'll need to structure the layout to properly configure this.
  • Finally, the locked column needs its vertical scrollbar to be hidden away - the user has no need to see two vertical scrollbars.

This covers the basic use case, but doesn't deal with things like alternate GridView implementations - GroupingView and subclasses will need to link expanding (and to hide the group headings so they don't appear twice, plus deal with the fact that the group row shouldn't get split when the second half scrolls sideways), TreeGridView and TreeGrid will need to link expanding nodes and hide the tree +/- icons from the second grid.

Here's this basic set of modifications applied to the basic grid example at http://www.sencha.com/examples/#ExamplePlace:basicgrid. To help avoid confusing the issue, I've deleted a number of other features in that grid such as tooltips and changing the selection model:

public class GridExample implements IsWidget, EntryPoint {
  private static final StockProperties props = GWT.create(StockProperties.class);

  private ContentPanel root;

  @Override
  public Widget asWidget() {
    if (root == null) {
      final NumberFormat number = NumberFormat.getFormat("0.00");

      ColumnConfig<Stock, String> nameCol = new ColumnConfig<Stock, String>(props.name(), 50, SafeHtmlUtils.fromTrustedString("<b>Company</b>"));

      ColumnConfig<Stock, String> symbolCol = new ColumnConfig<Stock, String>(props.symbol(), 100, "Symbol");
      ColumnConfig<Stock, Double> lastCol = new ColumnConfig<Stock, Double>(props.last(), 75, "Last");

      ColumnConfig<Stock, Double> changeCol = new ColumnConfig<Stock, Double>(props.change(), 100, "Change");
      changeCol.setCell(new AbstractCell<Double>() {

        @Override
        public void render(Context context, Double value, SafeHtmlBuilder sb) {
          String style = "style='color: " + (value < 0 ? "red" : "green") + "'";
          String v = number.format(value);
          sb.appendHtmlConstant("<span " + style + " qtitle='Change' qtip='" + v + "'>" + v + "</span>");
        }
      });

      ColumnConfig<Stock, Date> lastTransCol = new ColumnConfig<Stock, Date>(props.lastTrans(), 100, "Last Updated");
      lastTransCol.setCell(new DateCell(DateTimeFormat.getFormat("MM/dd/yyyy")));

      List<ColumnConfig<Stock, ?>> l = new ArrayList<ColumnConfig<Stock, ?>>();
      //Remove name from main set of columns
      //      l.add(nameCol);
      l.add(symbolCol);
      l.add(lastCol);
      l.add(changeCol);
      l.add(lastTransCol);

      //create two column models, one for the locked section
      ColumnModel<Stock> lockedCm = new ColumnModel<Stock>(Collections.<ColumnConfig<Stock, ?>>singletonList(nameCol));
      ColumnModel<Stock> cm = new ColumnModel<Stock>(l);

      ListStore<Stock> store = new ListStore<Stock>(props.key());
      store.addAll(TestData.getStocks());

      root = new ContentPanel();
      root.setHeadingText("Locked Grid Sample");
      root.setPixelSize(600, 300);

      final Resizable resizable = new Resizable(root, Dir.E, Dir.SE, Dir.S);
      root.addExpandHandler(new ExpandHandler() {
        @Override
        public void onExpand(ExpandEvent event) {
          resizable.setEnabled(true);
        }
      });
      root.addCollapseHandler(new CollapseHandler() {
        @Override
        public void onCollapse(CollapseEvent event) {
          resizable.setEnabled(false);
        }
      });

      //locked grid
      final Grid<Stock> lockedGrid = new Grid<Stock>(store, lockedCm) {
        @Override
        protected Size adjustSize(Size size) {
          //this is a tricky part - convince the grid to draw just slightly too wide
          //and so push the scrollbar out of sight
          return new Size(size.getWidth() + XDOM.getScrollBarWidth() - 1, size.getHeight());
        }
      };
      lockedGrid.setView(new GridView<Stock>(){{
        this.scrollOffset=0;
      }});
      //require columns to always fit, preventing scrollbar
      lockedGrid.getView().setForceFit(true);

      //main grid, with horiz scrollbar
      final Grid<Stock> grid = new Grid<Stock>(store, cm);
      //don't want this feature, want to encourage horizontal scrollbars
      //      grid.getView().setAutoExpandColumn(nameCol);
      grid.getView().setStripeRows(true);
      grid.getView().setColumnLines(true);
      grid.setBorders(false);

      grid.setColumnReordering(true);
      grid.setStateful(true);
      grid.setStateId("gridExample");

      //link scrolling
      lockedGrid.addBodyScrollHandler(new BodyScrollHandler() {
        @Override
        public void onBodyScroll(BodyScrollEvent event) {
          grid.getView().getScroller().scrollTo(ScrollDirection.TOP, event.getScrollTop());
        }
      });
      grid.addBodyScrollHandler(new BodyScrollHandler() {
        @Override
        public void onBodyScroll(BodyScrollEvent event) {
          lockedGrid.getView().getScroller().scrollTo(ScrollDirection.TOP, event.getScrollTop());
        }
      });

      HorizontalLayoutContainer gridWrapper = new HorizontalLayoutContainer();
      root.setWidget(gridWrapper);

      //add locked column, only 300px wide (in this example, use layouts to change how this works
      HorizontalLayoutData lockedColumnLayoutData = new HorizontalLayoutData(300, 1.0);

      //this is optional - without this, you get a little offset issue at the very bottom of the non-locked grid
      lockedColumnLayoutData.setMargins(new Margins(0, 0, XDOM.getScrollBarWidth(), 0));

      gridWrapper.add(lockedGrid, lockedColumnLayoutData);

      //add non-locked section, taking up all remaining width
      gridWrapper.add(grid, new HorizontalLayoutData(1.0, 1.0));
    }

    return root;
  }

  @Override
  public void onModuleLoad() {
    RootPanel.get().add(asWidget());
  }
}

There are a handful of issues (no line between locked and unlocked column, locked column header menu context icon is slightly out of place), but it covers most of the details without much hassle, and leaves almost all of it open to configuration - want the lock at the end? Just move the modifications around - want more than one locked column? just add more to the lockedCm.

Sklar answered 24/2, 2013 at 4:4 Comment(11)
Amazingly detailed answer, thanks. I'm just trying it out now. I also awarded the bounty before I know it works as reading through the answer made me think, as I want to make sure I understand whatever solution I implement. Thanks! :)Sipes
Great, let me know if there are other issues with the basic idea. I've given this concept to at least half a dozen people before, at conferences, in IRC, on forums, etc, but none of them ever came back and complained that it didn't work or told me that it was great, so I finally decided to take 30 mins and build it myself here. Let me know how it goes for you!Sklar
Well, I'm in a big company so still explaining it to other people, but I intend to come back to you and give you feedback on this (it wasn't as simple as just knocking out the code in the end as I have to make sure it works with all their edge cases, but you have given me a great head start :)Sipes
No worries, I'll be here - I'm also in ##gwt and #extgwt on irc.freenode.net as niloc132 if you want to chat about it.Sklar
The scrollbarwidth thing didnt work, so I just used overflow to remove the scrollbars. Now the problem is that the grid goes a little bit too high as defaultSipes
Sorry, I've been in meetings for days, but I'll be on IRC today. The scroll handler I have is a little screwy, so I've been working on a change for that. Can you tell me what 'didn't work' about the scrollbar width and on what browsers?Sklar
Hi, I'm back and have alot of questions now, and then I can explain about the scrollbar. Will you be available on IRC today?Sipes
Sorry, I kept getting disconnected. I just figured out why the scrollbar still shows, I'm using the LiveGrid, not the Grid. But I still haven't solved itSipes
As I think I mentioned, this won't work for other GridViews like live, tree, grouping, etc - those have a custom way of drawing, and you'll need to hijack those details according to how those views work internally.Sklar
@ColinAlworth nice solution but how to handle situation where you've aggregation using AggregationRowConfig?Calque
@akram it should probably behave like any footer - both sides will need a footer, though one may need to be empty to keep that same space reserved. Otherwise you can do resizing work such as is done to account for the horizontal scrollbar.Sklar
P
2

That functionality is not implemented in GXT, but there is an user called The_Jackal who made a workaround for this problem in the Sencha Forum - Thread. I haven't tried yet, but I hope it can help you.

GXT - Freeze grid workaround Download

Pankhurst answered 20/2, 2013 at 19:20 Comment(1)
Unfortunately this does not work as it relies on the GXT 2.x BufferView, which has since been removed from GXT 3.x - sencha.com/forum/…Sipes

© 2022 - 2024 — McMap. All rights reserved.