The official release of Ext.NET Mobile is now available. Ext.NET Mobile is an ASP.NET component framework for building Phone and Tablet specific mobile web applications. Read More

Buffered Grid with FilterHeader and GroupingSummary causes scrolling problem

  1. #1

    Buffered Grid with FilterHeader and GroupingSummary causes scrolling problem

    Hi, I'm trying to use a buffered grid with the FilterHeader plugin. The Buffered Scrolling example http://examples.ext.net/#/GridPanel/...red_Scrolling/ works when there is no FilterHeader plugin, but if one is added, then moving the scrollbar causes the grid rows to display incorrectly. This is made worse when the GroupingSummary feature is used.

    <%@ Page Language="C#" %>
    
    <script runat="server">
        protected void Page_Load(object sender, EventArgs e)
        {
            if (!X.IsAjaxRequest)
            {
                this.Store1.DataSource = this.TestData(5000);
                this.Store1.DataBind();
            }
        }
    
        private object[] TestData(int count)
        {
            string[] firstNames = new string[] { "Russel", "Clark", "Steve", "Sam", "Lance", "Robert", "Sean", "David", "Justin", "Nicolas", "Brent" };
            string[] lastNames = new string[] { "Wood", "Lewis", "Scott", "Parker", "Ross", "Garcia", "Bell", "Kelly", "Powell", "Moore", "Cook" };
    
            int[] ratings = new int[] { 1, 2, 3, 4, 5 };
            int[] salaries = new int[] { 100, 400, 900, 1500, 1000000 };
    
            object[] data = new object[count];
            Random rnd = new Random();
    
            for (int i = 0; i < count; i++)
            {
                int ratingId = rnd.Next(ratings.Length);
                int salaryId = rnd.Next(salaries.Length);
                int firstNameId = rnd.Next(firstNames.Length);
                int lastNameId = rnd.Next(lastNames.Length);
    
                int rating = ratings[ratingId];
                int salary = salaries[salaryId];
                string name = String.Format("{0} {1}", firstNames[firstNameId], lastNames[lastNameId]);
                string id = "rec-" + i;
    
                data[i] = new object[] { id, name, rating, salary };
            }
    
            return data;
        }
    </script>
    
    <!DOCTYPE html>
    
    <html>
    <head runat="server">
        <title>Buffered Scrolling - Ext.NET Examples</title>
        <link href="/resources/css/examples.css" rel="stylesheet" />
    
        <script>
            var jumpToRow = function () {
                var me = this,
                    field = me.up('toolbar').down('#gotoLine');
    
                if (field.isValid()) {
                    me.up('grid').view.bufferedRenderer.scrollTo(field.getValue() - 1, true);
                }
            };
        </script>
    </head>
    <body>
        <form runat="server">
            <ext:ResourceManager runat="server" />
    
            <h1>Buffered Scrolling</h1>
    
            <p>Introduced with Ext.Net 2, the Infinite Scrolling support for GridPanels enables you to load any number of records into a grid without paging.</p>
            <p>This grid uses a virtualized scrolling system to handle potentially infinite data sets without any impact on client side performance.</p>
            <p>This example illustrates loading of all the records up front thus acting as a data buffer used as rows are rendered.</p>
    
            <ext:GridPanel
                runat="server"
                Title="Buffered Grid of 5,000 random records"
                Width="700"
                Height="500">
                <Store>                                                                         
    <%------------------------------ Add a GroupField --%>
    
                    <ext:Store
                        ID="Store1"
                        GroupField="rating"
                        runat="server">
                        <Model>
                            <ext:Model runat="server">
                                <Fields>
                                    <ext:ModelField Name="id" />
                                    <ext:ModelField Name="name" />
                                    <ext:ModelField Name="rating" Type="Int" />
                                    <ext:ModelField Name="salary" Type="Float" />
                                </Fields>
                            </ext:Model>
                        </Model>
                    </ext:Store>
                </Store>
                <Plugins>
                    <ext:BufferedRenderer runat="server" />
    <%--------------------------------------------- Add a FilterHeader --%>
                    <ext:FilterHeader runat="server" />                                               
                </Plugins>
                <Features>                                                                            
    <%--------------------------------------------- Add a GroupingSummary --%>
                    <ext:GroupingSummary
                        runat="server"
                        HideGroupedHeader="true"
                        ShowSummaryRow="false"
                        StartCollapsed="false"
                        GroupHeaderTplString='{columnName}: {name} ({rows.length} {[values.rows.length > 1 ? "records" : "record"]})'
                        EnableNoGroups="true">
                    </ext:GroupingSummary>
                </Features>
    
                <View>
                    <ext:GridView runat="server" TrackOver="false" />
                </View>
                <SelectionModel>
                    <ext:RowSelectionModel runat="server" PruneRemoved="false" />
                </SelectionModel>
                <ColumnModel runat="server">
                    <Columns>
                        <ext:RowNumbererColumn
                            runat="server"
                            Width="40"
                            Sortable="false" />
                        <ext:Column
                            runat="server"
                            Text="Name"
                            Flex="1"
                            DataIndex="name" />
                        <ext:Column
                            runat="server"
                            Text="Rating"
                            Width="125"
                            DataIndex="rating" />
                        <ext:Column
                            runat="server"
                            Text="Salary"
                            Width="125"
                            DataIndex="salary"
                            Align="Right">
                            <Renderer Format="UsMoney" />
                        </ext:Column>
                    </Columns>
                </ColumnModel>
                <BottomBar>
                    <ext:Toolbar runat="server">
                        <Items>
                            <ext:NumberField
                                runat="server"
                                LabelWidth="70"
                                FieldLabel="Jump to row"
                                MinValue="1"
                                MaxValue="5000"
                                AllowDecimals="false"
                                EnableKeyEvents="true"
                                ItemId="gotoLine">
                                <Listeners>
                                    <SpecialKey Handler="if (e.getKey() === e.ENTER) { jumpToRow.call(this); }" />
                                </Listeners>
                            </ext:NumberField>
    
                            <ext:Button runat="server" Text="Go">
                                <Listeners>
                                    <Click Fn="jumpToRow" />
                                </Listeners>
                            </ext:Button>
                        </Items>
                    </ext:Toolbar>
                </BottomBar>
            </ext:GridPanel>
        </form>
    </body>
    </html>
    Running the above code will initially display ok, but if you drag the scrollbar down the grid will no longer display the rows correctly. Without the FilterHeader it works correctly. The issue is present in versions 4.1 and 4.2.
    Last edited by chrisuae; Apr 18, 2017 at 2:29 AM.
  2. #2

    Workaround

    The issue appears to be related to a recursive call to onRangeFetched in the BufferedRenderer and only causes problems when using WebKit and the Ext.net FilterHeaders plugin is in use. The call is related to the focus being set back to the column header (which has been modified by the FilterHeader). The following workaround seems to solve the problem (version 4.2):

    Ext.grid.plugin.BufferedRenderer.override({    onRangeFetched: function (range, start, end, options, fromLockingPartner) {
            var me = this,
                view = me.view,
                scroller = me.scroller,
                viewEl = view.el,
                rows = view.all,
                increment = 0,
                calculatedTop,
                lockingPartner = (view.lockingPartner && !fromLockingPartner && !me.doNotMirror) && view.lockingPartner.bufferedRenderer,
                variableRowHeight = me.variableRowHeight,
                oldBodyHeight = me.bodyHeight,
                layoutCount = view.componentLayoutCounter,
                activeEl, containsFocus, i, newRows, newTop, newFocus, noOverlap, oldStart, partnerNewRows, pos, removeCount, topAdditionSize, topBufferZone;
    
    
            if (view.destroyed) {
                return;
            }
    
    
            if (range) {
                if (!fromLockingPartner) {
    
    
                    me.scrollTop = me.scroller.getPosition().y;
                }
            } else {
                range = me.store.getRange(start, end);
    
    
                if (!range) {
                    return;
                }
            }
    
    
            activeEl = Ext.fly(Ext.Element.getActiveElement());
            containsFocus = viewEl.contains(activeEl);
    
    
            if (containsFocus) {
                activeEl.suspendFocusEvents();
            }
    
    
            calculatedTop = start * me.rowHeight;
    
    
            if (start < rows.startIndex && end > rows.endIndex) {
    
    
                topAdditionSize = rows.startIndex - start;
    
    
                view.clearViewEl(true);
                newRows = view.doAdd(range, start);
                view.fireItemMutationEvent('itemadd', range, start, newRows, view);
                for (i = 0; i < topAdditionSize; i++) {
                    increment -= newRows[i].offsetHeight;
                }
    
    
                newTop = me.bodyTop + increment;
            } else {
    
    
                noOverlap = me.teleported || start > rows.endIndex || end < rows.startIndex;
                if (noOverlap) {
                    view.clearViewEl(true);
                    me.teleported = false;
                }
                if (!rows.getCount()) {
                    newRows = view.doAdd(range, start);
                    view.fireItemMutationEvent('itemadd', range, start, newRows, view);
                    newTop = calculatedTop;
    
    
                    if (noOverlap && variableRowHeight) {
                        topBufferZone = me.scrollTop < me.position ? me.leadingBufferZone : me.trailingBufferZone;
                        newTop = Math.max(me.scrollTop - rows.item(rows.startIndex + topBufferZone - 1, true).offsetTop, 0);
                    }
                }
    
    
                else if (end > rows.endIndex) {
                    removeCount = Math.max(start - rows.startIndex, 0);
    
    
                    if (variableRowHeight) {
                        increment = rows.item(rows.startIndex + removeCount, true).offsetTop;
                    }
                    newRows = rows.scroll(Ext.Array.slice(range, rows.endIndex + 1 - start), 1, removeCount);
    
    
                    if (variableRowHeight) {
    
    
                        newTop = me.bodyTop + increment;
                    } else {
                        newTop = calculatedTop;
                    }
                } else {
                    removeCount = Math.max(rows.endIndex - end, 0);
                    oldStart = rows.startIndex;
                    newRows = rows.scroll(Ext.Array.slice(range, 0, rows.startIndex - start), -1, removeCount);
    
    
                    if (variableRowHeight) {
    
    
                        newTop = me.bodyTop - rows.item(oldStart, true).offsetTop;
    
    
                        if (!rows.startIndex) {
    
    
                            if (newTop) {
                                scroller.scrollTo(null, me.position = (me.scrollTop -= newTop));
                                newTop = 0;
                            }
                        }
    
    
                        else if (newTop < 0) {
                            increment = rows.startIndex * me.rowHeight;
                            scroller.scrollTo(null, me.position = (me.scrollTop += increment));
                            newTop = me.bodyTop + increment;
                        }
                    } else {
                        newTop = calculatedTop;
                    }
                }
    
    
                me.position = me.scrollTop;
            }
    
    
            if (containsFocus) {
    
    
                activeEl.resumeFocusEvents();
                if (!viewEl.contains(activeEl)) {
                    pos = view.actionableMode ? view.actionPosition : view.lastFocused;
                    if (pos && pos.column) {
    
    
                        view.renderingRows = true;
                        view.onFocusLeave({});
                        view.renderingRows = false;
                        // Try to focus the contextual column header.
                        // Failing that, look inside it for a tabbable element.
                        // Failing that, focus the view.
                        // Focus MUST NOT just silently die due to DOM removal
                        if (pos.column.focusable) {
                            newFocus = pos.column;
                        } else {
                            newFocus = pos.column.el.findTabbableElements()[0];
                        }
                        if (!newFocus) {
                            newFocus = view.el;
                        }
                        if (pos.column.focusable) {
                            newFocus = pos.column;
                        } else {
                            newFocus = pos.column.el.findTabbableElements()[0];
                        }
                        if (!newFocus) {
                            newFocus = view.el;
                        }
                        if (!Ext.isWebKit || !view.ownerGrid.filterHeader) {   // only set the focus if there is no FilterHeader plugin and browser uses WebKit
                            newFocus.focus();
                        }
                    }
                }
            }
    
    
            newTop = Math.max(Math.floor(newTop), 0);
    
    
            if (newRows && lockingPartner && !lockingPartner.disabled) {
    
    
                lockingPartner.scrollTop = lockingPartner.position = me.scrollTop;
                if (lockingPartner.view.ownerCt.isVisible()) {
                    partnerNewRows = lockingPartner.onRangeFetched(range, start, end, options, true);
    
    
                    if (view.ownerGrid.syncRowHeight || view.ownerGrid.syncRowHeightOnNextLayout || (lockingPartner.variableRowHeight !== variableRowHeight)) {
                        me.syncRowHeights(newRows, partnerNewRows);
                        view.ownerGrid.syncRowHeightOnNextLayout = false;
                    }
                }
                if (lockingPartner.bodyTop !== newTop) {
                    lockingPartner.setBodyTop(newTop, true);
                }
            }
            if (view.positionBody) {
                me.setBodyTop(newTop, true);
            }
    
    
            if (me.variableRowHeight) {
                delete me.rowHeight;
                me.refreshSize();
            }
    
    
            if (view.getVisibleColumnManager().getColumns().length && rows.getCount() !== Math.min(me.store.getCount(), me.viewSize)) {
                Ext.raise('rendered block refreshed at ' + rows.getCount() + ' rows while BufferedRenderer view size is ' + me.viewSize);
            }
            return newRows;
        }
    
    
    })
    The only change is to put the focus call in line 172 inside an IF statement to prevent it from being called when using WebKit and FilterHeaders. It seems to work, but it may cause other problems.

Similar Threads

  1. Replies: 1
    Last Post: Oct 03, 2016, 8:39 PM
  2. Replies: 6
    Last Post: Feb 06, 2014, 7:28 AM
  3. [CLOSED] Buffered Grid Scrolling repeats rows
    By jwhitmire36 in forum 2.x Premium Help
    Replies: 10
    Last Post: Feb 09, 2013, 5:49 AM
  4. Replies: 6
    Last Post: Feb 04, 2013, 3:55 AM
  5. [CLOSED] Buffered Grid View Scrolling
    By bethc in forum 1.x Premium Help
    Replies: 3
    Last Post: Dec 02, 2009, 7:29 AM

Tags for this Thread

Posting Permissions