[CLOSED] TreePanel local sort is scrolling to top

  1. #1

    [CLOSED] TreePanel local sort is scrolling to top

    The scrollbar is positioning at the top when sorting locally. See the following example:

    <%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage<dynamic>" %>
    
    <!DOCTYPE html>
    <html>
    <head runat="server">
        <script type="text/javascript">
            var click = function (btn) {
                var tree = btn.up('panel');
    
                tree.store.sort('ID', 'ASC');
            }
        </script>
    </head>
    <body>
        <ext:ResourceManager runat="server" />
        <ext:TreePanel RootVisible="false" Title="Ext.Net" Border="true" Height="400" Width="300" runat="server">
            <Store>
                <ext:TreeStore runat="server">
                    <Proxy>
                        <ext:AjaxProxy Url="~/Example/LoadTreeFakeChildren">
                            <Reader>
                                <ext:JsonReader RootProperty="data" />
                            </Reader>
                        </ext:AjaxProxy>
                    </Proxy>
                    <Model>
                        <ext:Model runat="server">
                            <Fields>
                                <ext:ModelField Name="ID" Type="Int" />
                                <ext:ModelField Name="Name" />
                            </Fields>
                        </ext:Model>
                    </Model>
                </ext:TreeStore>
            </Store>
            <Root>
                <ext:Node NodeID="0" Text="Root" />
            </Root>
            <ColumnModel>
                <Columns>
                    <ext:Column Text="ID" DataIndex="ID" runat="server" />
                    <ext:TreeColumn Text="Name" DataIndex="Name" Flex="2" runat="server" />
                </Columns>
            </ColumnModel>
            <View>
                <ext:TreeView DeferEmptyText="false" />
            </View>
            <Buttons>
                <ext:Button Text="Click Me" runat="server">
                    <Listeners>
                        <Click Handler="click(item);" />
                    </Listeners>
                </ext:Button>
            </Buttons>
        </ext:TreePanel>
    </body>
    </html>

    public class ExampleController : Controller
    {
         public ActionResult Index()
         {
             return View();
         }
    
         public StoreResult LoadTreeFakeChildren()
         {
             var nodes = new NodeCollection();
    
             for (int index = 1; index < 1001; index++)
             {
                 Node no = new Node();
                 no.NodeID = $"{index}{DateTime.Now.Second}";
                 no.CustomAttributes.Add(new ConfigItem { Name = "ID", Value = index.ToString(), Mode = ParameterMode.Value });
                 no.CustomAttributes.Add(new ConfigItem { Name = "Name", Value = $"Name - {index}", Mode = ParameterMode.Value });
                 nodes.Add(no);
             }
    
             return new StoreResult { Data = nodes.ToJson() };
         }
    }
    Here is another piece of code using only Ext JS 5.1.3.

    I think it's a bug. Considering a change of some value in a row or deleting it and then sort.. I will have to scroll down and find the row again if I want review. On previous versions I can preserve the scrollbar position using the "preserveScrollOnRefresh" option.

    Please, let me know what do you think about this.

    Best Regards.
    Last edited by fabricio.murta; Feb 11, 2017 at 1:45 AM.
  2. #2
    Hello @RCN!

    This is indeed a nuisance. But I couldn't reproduce this issue in version 4.x of Ext.NET, at least the to-be-released 4.2.0 version, so it seems this issue has been resolved at least for some time.

    I found I could address this issue with reasonably easy approach without fiddling with overrides, but just custom handlers to save and restore the scroller state during sorts. For that, I made the store call the beforerefresh event of the tree view before the sort takes place, then in the tree view's beforerefresh handler, saved the scroller state (if PreserveScrollonRefres is true), and then properly restored the scroller state during the last refresh cycle (it is called three times during the sort due to the changes on the store while sorting).

    It seems to work fine in chrome and IE. Here's what I come up with:

    <%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage" %>
    
    <!DOCTYPE html>
    <html>
    <head runat="server">
        <title></title>
        <script type="text/javascript">
            var click = function (btn) {
                var tree = btn.up('panel');
    
                tree.store.sort('ID', 'ASC');
            }
    
            // The store does not trigger 'beforeRefresh' from the view, so it can never save
            // the state while the scroller is still in position.
            var handleBeforeSort = function (store, sorters) {
                var treeView = App.tv1;
    
                if (treeView) {
                    console.log('before sorting');
                    treeView.fireEvent('beforerefresh', treeView);
                }
            }
    
            // Handle saving scroller position if the setting is in place.
            var handleBeforeRefresh = function (item) {
                var me = this;
    
                console.log('before refreshing');
                if (me.preserveScrollOnRefresh) {
                    console.log('saving scroll pos.');
                    me.saveScrollState();
                }
            }
    
            // Handle the tree refresh. On sort, it will refresh three time: removing the items from the 
            // view, adding them back (sorted), and then with the data properly in place. We only want
            // to restore the scroller when the data is ready, in the third time.
            var handleAfterRefresh = function (item) {
                var me = this, dataLoaded;
    
                console.log('after refreshing');
    
                // Here we use '.config' because it is temporarily set as false in the last refresh.
                if (me.config.preserveScrollOnRefresh) {
                    dataLoaded = me.dataSource && me.dataSource.data && me.dataSource.data.items.length > 0;
    
                    if (dataLoaded) {
                        console.log('restoring scroll pos.');
                        me.restoreScrollState();
                    }
                }
            }
        </script>
    </head>
    <body>
        <ext:ResourceManager runat="server" />
        <ext:TreePanel RootVisible="false" Title="Ext.Net" Border="true" Height="400" Width="300" runat="server">
            <Store>
                <ext:TreeStore runat="server">
                    <Proxy>
                        <ext:AjaxProxy Url="/rcn/c61686_LoadTreeFakeChildren/">
                            <Reader>
                                <ext:JsonReader RootProperty="data" />
                            </Reader>
                        </ext:AjaxProxy>
                    </Proxy>
                    <Model>
                        <ext:Model runat="server">
                            <Fields>
                                <ext:ModelField Name="ID" Type="Int" />
                                <ext:ModelField Name="Name" />
                            </Fields>
                        </ext:Model>
                    </Model>
                    <Listeners>
                        <BeforeSort Fn="handleBeforeSort" />
                        <Sort Handler="console.log('after sort...');" />
                    </Listeners>
                </ext:TreeStore>
            </Store>
            <Root>
                <ext:Node NodeID="0" Text="Root" />
            </Root>
            <ColumnModel>
                <Columns>
                    <ext:Column Text="ID" DataIndex="ID" runat="server" />
                    <ext:TreeColumn Text="Name" DataIndex="Name" Flex="2" runat="server" />
                </Columns>
            </ColumnModel>
            <View>
                <ext:TreeView ID="tv1" DeferEmptyText="false" PreserveScrollOnRefresh="true">
                    <Listeners>
                        <BeforeRefresh Fn="handleBeforeRefresh" />
                        <Refresh Fn="handleAfterRefresh" />
                    </Listeners>
                </ext:TreeView>
            </View>
            <Buttons>
                <ext:Button Text="Click Me" runat="server">
                    <Listeners>
                        <Click Handler="click(item);" />
                    </Listeners>
                </ext:Button>
            </Buttons>
        </ext:TreePanel>
    </body>
    </html>
    Hope this helps! Not sure we should log an issue for this as it is a problem that does not happen on the latest version of Ext.NET at all.
    Fabrício Murta
    Developer & Support Expert
  3. #3
    Hello @fabricio.murta.

    This fix you have proposed doesn't works. I'm using Ext.NET 3.3.0 and Ext JS 5.1.3.

    I see it works using the version 6.2 of Ext JS using fiddle: https://fiddle.sencha.com/#view/editor&fiddle/1ni1.

    I think I could address the issue using override instead. Something like the following:

    <%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage<dynamic>" %>
    
    <!DOCTYPE html>
    <html>
    <head runat="server">
        <script type="text/javascript">
            Ext.data.TreeStore.override({
                sort: function () {
                    if (!this.remoteSort) {
                        this.ownerTree.lastScrollPosition = this.ownerTree.bufferedRenderer && this.ownerTree.bufferedRenderer.scrollTop;
                    }
    
                    this.callParent(arguments);
    
                    if (this.ownerTree.lastScrollPosition > 0) {
                        this.ownerTree.bufferedRenderer.view.setScrollY(this.ownerTree.lastScrollPosition);
                        delete this.ownerTree.lastScrollPosition;
                    }
                }
            });
    
            var click = function (btn) {
                var tree = btn.up('panel');
    
                tree.store.sort('ID', 'ASC');
            }
        </script>
    </head>
    <body>
        <ext:ResourceManager runat="server" />
        <ext:TreePanel RootVisible="false" Title="Ext.Net" Border="true" Height="400" Width="300" runat="server">
            <Store>
                <ext:TreeStore runat="server">
                    <Proxy>
                        <ext:AjaxProxy Url="~/Example/LoadTreeFakeChildren">
                            <Reader>
                                <ext:JsonReader RootProperty="data" />
                            </Reader>
                        </ext:AjaxProxy>
                    </Proxy>
                    <Model>
                        <ext:Model runat="server">
                            <Fields>
                                <ext:ModelField Name="ID" Type="Int" />
                                <ext:ModelField Name="Name" />
                            </Fields>
                        </ext:Model>
                    </Model>
                </ext:TreeStore>
            </Store>
            <Root>
                <ext:Node NodeID="0" Text="Root" />
            </Root>
            <ColumnModel>
                <Columns>
                    <ext:Column Text="ID" DataIndex="ID" runat="server" />
                    <ext:TreeColumn Text="Name" DataIndex="Name" Flex="2" runat="server" />
                </Columns>
            </ColumnModel>
            <Buttons>
                <ext:Button Text="Click Me" runat="server">
                    <Listeners>
                        <Click Handler="click(item);" />
                    </Listeners>
                </ext:Button>
            </Buttons>
        </ext:TreePanel>
    </body>
    </html>
    I looked at many posts at sencha and I found this. Despite it makes sense, I tested this fix and it works pleasantly. I have to say it scares me a lot.

    What do you think?

    Best Regards.
  4. #4
    Hello @RCN!

    You should just adapt the proxy path for your project, the sample code I provided was run on Ext.NET 3.3.0 but on our local project which has to have different path and action entrypoint names.

    Thanks for sharing the override approach. It works for us if it works for you! :)

    About the thread you shared, enabling or disabling buffered rendering on the grid did not solve the issue on the test sample you provided. At least on my side. I'm using latest Ext.NET 3 from SVN but I don't think results should change from the NuGet version. If you are really stuck with NuGet version and still want help with this issue I can test this also on the NuGet version and let you know the results.
    Fabrício Murta
    Developer & Support Expert
  5. #5
    Hello @RCN!

    It's been some time since we last posted you a follow up to this thread and so far no feedback. Do you still need help with this issue? We may mark it as closed if you still don't provide any feedback in 7 business days, but don't worry -- you'll still be able to post follow ups here when you have some time!
    Last edited by fabricio.murta; Jan 21, 2017 at 10:32 PM.

Similar Threads

  1. Replies: 15
    Last Post: Oct 22, 2015, 2:26 PM
  2. Replies: 3
    Last Post: Mar 06, 2015, 1:04 PM
  3. Replies: 4
    Last Post: Aug 22, 2013, 9:00 AM
  4. [CLOSED] Keep TreePanel´s selection after local sort
    By RCN in forum 2.x Legacy Premium Help
    Replies: 2
    Last Post: Jun 23, 2012, 7:05 PM
  5. [CLOSED] Inserted records dissapear after local sort operation
    By pschojer in forum 1.x Legacy Premium Help
    Replies: 3
    Last Post: Apr 23, 2010, 5:00 AM

Posting Permissions