[CLOSED] GridPanel - "select all" feature?

Hybrid View

Previous Post Previous Post   Next Post Next Post
  1. #1

    [CLOSED] GridPanel - "select all" feature?

    Hi, Ext.NET

    Our users need to be able to manually select all the data from a GridPanel. And then paste the results somewhere else.

    Below is an example of a GridPanel ... the problem is that we can't ever get the "header row" to be selected.
    This is basically the column object's "text" property, which is the user friendly verbiage for the column's "dataindex".

    Is there a plugin or some configuration or JS logic that can achieve this? Thanks

    <%@ Page Language="C#" %>
    <%@ Import Namespace="System.Data" %>
    <script runat="server">    
    
        protected void Page_Load(object sender, EventArgs e){
            if (!Page.IsPostBack && !X.IsAjaxRequest)
            {
                var gridCM = FPHGridPanel.ColumnModel;
                    gridCM.Add(new Column() { DataIndex = "LOCATION", Text = "LOCATION", Sortable = true, CellWrap = true, Align = ColumnAlign.Center });
                    var gridSummary = new Renderer() { Fn = "custSumRender" };
                    gridCM.Add(new Column() { DataIndex = "Healthy", Text = "Healthy", Sortable = true, SummaryType = SummaryType.Sum, SummaryRenderer = gridSummary, CellWrap = true, Align = ColumnAlign.Center });
                    gridCM.Add(new Column() { DataIndex = "Expiring (>3 but <= 6 months remaining)", Text = "Expiring (>3 but <= 6 months remaining)", Sortable = true, SummaryType = SummaryType.Sum, SummaryRenderer = gridSummary, CellWrap = true, Align = ColumnAlign.Center });
                    gridCM.Add(new Column() { DataIndex = "Expiring within 3 months", Text = "Expiring within 3 months", Sortable = true, SummaryType = SummaryType.Sum, SummaryRenderer = gridSummary, CellWrap = true, Align = ColumnAlign.Center });
                    gridCM.Add(new Column() { DataIndex = "Inactive Rep Qty", Text = "Inactive Rep Qty", Sortable = true, SummaryType = SummaryType.Sum, SummaryRenderer = gridSummary, CellWrap = true, Align = ColumnAlign.Center });
                    gridCM.Add(new Column() { DataIndex = "Completed", Text = "Completed", Sortable = true, SummaryType = SummaryType.Sum, SummaryRenderer = gridSummary, CellWrap = true, Align = ColumnAlign.Center });
                    gridCM.Add(new Column() { DataIndex = "Not Completed", Text = "Not Completed", Sortable = true, SummaryType = SummaryType.Sum, SummaryRenderer = gridSummary, CellWrap = true, Align = ColumnAlign.Center });
                    gridCM.Add(new Column() { DataIndex = "Ahead of Projected Burn Rate", Text = "Ahead of Projected Burn Rate", Sortable = true, SummaryType = SummaryType.Sum, SummaryRenderer = gridSummary, CellWrap = true, Align = ColumnAlign.Center });
                    gridCM.Add(new Column() { DataIndex = "StatusTotal", Text = "", Sortable = true, SummaryType = SummaryType.Sum, SummaryRenderer = new Renderer() { Fn = "custGrandTotal" }, CellWrap = true, Align = ColumnAlign.Center, Renderer = new Renderer() { Fn= "sumColumnRender" } });
    
                var FPHStore2 = new Store();
                var FPHDataModel2 = new Model();
                FPHDataModel2.Fields.Add(new ModelField() { Name = "LOCATION", Type = ModelFieldType.String, });
                FPHDataModel2.Fields.Add(new ModelField() { Name = "Healthy", Type = ModelFieldType.Int, });
                FPHDataModel2.Fields.Add(new ModelField() { Name = "Expiring (>3 but <= 6 months remaining)", Type = ModelFieldType.Int, });
                FPHDataModel2.Fields.Add(new ModelField() { Name = "Expiring within 3 months", Type = ModelFieldType.Int, });
                FPHDataModel2.Fields.Add(new ModelField() { Name = "Inactive Rep Qty", Type = ModelFieldType.Int, });
                FPHDataModel2.Fields.Add(new ModelField() { Name = "Completed", Type = ModelFieldType.Int, });
                FPHDataModel2.Fields.Add(new ModelField() { Name = "Not Completed", Type = ModelFieldType.Int, });
                FPHDataModel2.Fields.Add(new ModelField() { Name = "Ahead of Projected Burn Rate", Type = ModelFieldType.Int, });
                FPHDataModel2.Fields.Add(new ModelField() { Name = "StatusTotal", Type = ModelFieldType.Int });
    
    
                FPHStore2.Model.Add(FPHDataModel2);            
    
                var table = new DataTable();                        
    
                table.Columns.Add("LOCATION", typeof(string));
                table.Columns.Add("Healthy", typeof(int));
                table.Columns.Add("Expiring (>3 but <= 6 months remaining)", typeof(int));
                table.Columns.Add("Expiring within 3 months", typeof(int));
                table.Columns.Add("Inactive Rep Qty", typeof(int));
                table.Columns.Add("Completed", typeof(int));
                table.Columns.Add("Not Completed", typeof(int));
                table.Columns.Add("Ahead of Projected Burn Rate", typeof(int));            
    
                table.Rows.Add(new object[8] { "Samples in the Field at Risk", 100, 200, 300, 400, 500, 600, 700 });
                table.Rows.Add(new object[8] { "New Trained Rep", 200, 300, 300, 400, 500, 100, 200 });
                table.Rows.Add(new object[8] { "Zero Lctn", 0, 0, 0, 0, 0, 0, 0 });
                table.Rows.Add(new object[8] { "Zero Lctn2", 0, 0, 0, 0, 0, 0, 0 });
                table.Rows.Add(new object[8] { "Zero Lctn3", 0, 0, 0, 0, 0, 0, 0 });
                table.Rows.Add(new object[8] { "Zero Lctn4", 0, 0, 0, 0, 0, 0, 0 });
                table.Rows.Add(new object[8] { "Zero Lctn5", 0, 0, 0, 0, 0, 0, 0 });
                table.Rows.Add(new object[8] { "Zero Lctn6", 0, 0, 0, 0, 0, 0, 0 });
                table.Rows.Add(new object[8] { "Returning Product", 100, 200, 300, 400, 500, 600, 700 });
                table.Rows.Add(new object[8] { "Rep to Rep Transfers in Last 90 Days", 25, 200, 300, 77, 500, 489, 0 });
                table.Rows.Add(new object[8] { "Ahead of Projected Burn Rate", 25, 200, 300, 77, 500, 489, 0 });
                
                table.Columns.Add("StatusTotal", typeof(int));
                foreach (DataRow r in table.Rows)
                {
                    int statusTotal = 0;
    
                    for (int i= 0; i < table.Columns.Count; i++)
                    {
                        if (r[i].GetType() == typeof(int))
                        {
                            statusTotal += (int) r[i];
                        }
                    }                
                    r["StatusTotal"] = statusTotal;
                }
    
                FPHStore2.Data = table;
    
                FPHStore2.DataBind();
                //FPHStore2.FilterBy(new JFunction() { Fn = "custFilter" });
                FPHGridPanel.Store.Add(FPHStore2);
            }
            
        }
    </script>
    
    <!DOCTYPE html>
    
    <html>
    <head>
        <title>Select all from Grid Panel</title>    
    
        <script>
    
            var sumColumnRender = function (value, metadata, record, rowIndex, colIndex, store, gridView) {         
                return store.data.items[rowIndex].data["LOCATION"] + " Total:<br/>" + value;
            }
    
            var custSumRender = function (value, cols, colHeader, metadata) {
                return ('Total ' + colHeader + ':<br/>' + value);
            }
    
            var custGrandTotal = function (value, cols, colHeader, metadata) {
                return ('<b><i>Grand Total</i></b>:<br/>(<i>' + value + '</i>)');
            }
    
        </script>
    </head>
    <body>
        <form runat="server">
            <ext:ResourceManager runat="server" />        
    
            <ext:GridPanel ID="FPHGridPanel" runat="server" 
                Border="false" 
                Title="TEST GRIDPANEL - user needs to select all (including header row) " 
                ForceFit="true">
                
                <Features>
                    <ext:Summary runat="server" ShowSummaryRow="true" />
                </Features>
    
                <View>
                    <ext:GridView EnableTextSelection="true" Selectable="true"></ext:GridView>
                </View>             
            </ext:GridPanel>
            
        </form>
    </body>
    </html>
    Last edited by fabricio.murta; May 17, 2019 at 5:42 PM.
  2. #2
    Hello @Caleb!

    Before we go to the actual example, in general terms, here's a couple ways you have to select all entries in a grid panel:

    - using the checkbox selection, you have a "select/deselect all" checkbox in the checkbox column's header.
    - using the selection model's selectAll() method.

    The first approach suggested above would allow for user interaction without further coding. The second allows you to programatically design your UI interface to select (and deselect) the rows in a grid, like clicking a button, or mapping a key combination.

    I'll review your example now, if something else shows up, I'll post again.

    Hope this helps!
    Fabrício Murta
    Developer & Support Expert
  3. #3
    Hello again, @Caleb!

    After a shallow analysis of your sample, it seems you want to copy the whole grid, columns included, maybe like in CSV format.

    Fact is, if you just select the whole grid and ctrl+c, it does not really copies anything.

    There is, actually, a plug in to implement this, I believe the exact way you want it: The Clipboard grid plugin. And as you would see in the linked example, it needs to use the Spreadsheet Selection Model in order to enable the Clipboard plugin.

    The means to select all records in the last post is still actual, except you may trigger the selection by clicking the pivot header (top-left header cell).

    But if you want to keep the current selection model, you could fairly easy implement a logic to get the current selection, add the column names (rather, the fields' names) and then copy it to the clipboard with some available tricks.

    To get the currently selected records, you do, in javascript: App.FPHGridPanel.getSelectionModel().getSelection();

    This will give you an array of records, in which you can then get the fields' values within its data field:

    var sel = App.FPHGridPanel.getSelectionModel().getSelection();
    
    console.log(Object.keys(sel[0].data).join(","));
    
    for (var x in sel) {
        console.log(Object.values(sel[x].data).join(","));
    }
    Hope this helps!
    Fabrício Murta
    Developer & Support Expert
  4. #4
    Hi Fabricio,

    I think the spreadhsheet plugin that you mentioned is closest to what we're looking for. However, playing around with it I'm still not seeing the user friendly verbiage for each column's header.

    It seems to me that its only getting the data inside of the gridPanel's backing store. Which is 85% of what we need.
    I think I have implemented the Ext.NET code correctly.

    The last 15%, is the header text on top of each column.

    Try and click on the copy button in this demo. Paste into a notepad. It has all the data in the backing store, but not the header verbiage on top of the columns.

    Click image for larger version. 

Name:	MissingHeaders.png 
Views:	108 
Size:	46.2 KB 
ID:	25262

    <%@ Page Language="C#" %>
    <%@ Import Namespace="System.Data" %>
    <script runat="server">    
    
        protected void Page_Load(object sender, EventArgs e){
            if (!Page.IsPostBack && !X.IsAjaxRequest)
            {
                var gridCM = FPHGridPanel.ColumnModel;
                    gridCM.Add(new Column() { DataIndex = "LOCATION", Text = "LOCATION", Sortable = true, CellWrap = true, Align = ColumnAlign.Center });
                    var gridSummary = new Renderer() { Fn = "custSumRender" };
                    gridCM.Add(new Column() { DataIndex = "Healthy", Text = "Healthy", Sortable = true, SummaryType = SummaryType.Sum, SummaryRenderer = gridSummary, CellWrap = true, Align = ColumnAlign.Center });
                    gridCM.Add(new Column() { DataIndex = "Expiring (>3 but <= 6 months remaining)", Text = "Expiring (>3 but <= 6 months remaining)", Sortable = true, SummaryType = SummaryType.Sum, SummaryRenderer = gridSummary, CellWrap = true, Align = ColumnAlign.Center });
                    gridCM.Add(new Column() { DataIndex = "Expiring within 3 months", Text = "Expiring within 3 months", Sortable = true, SummaryType = SummaryType.Sum, SummaryRenderer = gridSummary, CellWrap = true, Align = ColumnAlign.Center });
                    gridCM.Add(new Column() { DataIndex = "Inactive Rep Qty", Text = "Inactive Rep Qty", Sortable = true, SummaryType = SummaryType.Sum, SummaryRenderer = gridSummary, CellWrap = true, Align = ColumnAlign.Center });
                    gridCM.Add(new Column() { DataIndex = "Completed", Text = "Completed", Sortable = true, SummaryType = SummaryType.Sum, SummaryRenderer = gridSummary, CellWrap = true, Align = ColumnAlign.Center });
                    gridCM.Add(new Column() { DataIndex = "Not Completed", Text = "Not Completed", Sortable = true, SummaryType = SummaryType.Sum, SummaryRenderer = gridSummary, CellWrap = true, Align = ColumnAlign.Center });
                    gridCM.Add(new Column() { DataIndex = "Ahead of Projected Burn Rate", Text = "Ahead of Projected Burn Rate", Sortable = true, SummaryType = SummaryType.Sum, SummaryRenderer = gridSummary, CellWrap = true, Align = ColumnAlign.Center });
                    gridCM.Add(new Column() { DataIndex = "StatusTotal", Text = "", Sortable = true, SummaryType = SummaryType.Sum, SummaryRenderer = new Renderer() { Fn = "custGrandTotal" }, CellWrap = true, Align = ColumnAlign.Center, Renderer = new Renderer() { Fn= "sumColumnRender" } });
    
                var FPHStore2 = new Store();
                var FPHDataModel2 = new Model();
                FPHDataModel2.Fields.Add(new ModelField() { Name = "LOCATION", Type = ModelFieldType.String, });
                FPHDataModel2.Fields.Add(new ModelField() { Name = "Healthy", Type = ModelFieldType.Int, });
                FPHDataModel2.Fields.Add(new ModelField() { Name = "Expiring (>3 but <= 6 months remaining)", Type = ModelFieldType.Int, });
                FPHDataModel2.Fields.Add(new ModelField() { Name = "Expiring within 3 months", Type = ModelFieldType.Int, });
                FPHDataModel2.Fields.Add(new ModelField() { Name = "Inactive Rep Qty", Type = ModelFieldType.Int, });
                FPHDataModel2.Fields.Add(new ModelField() { Name = "Completed", Type = ModelFieldType.Int, });
                FPHDataModel2.Fields.Add(new ModelField() { Name = "Not Completed", Type = ModelFieldType.Int, });
                FPHDataModel2.Fields.Add(new ModelField() { Name = "Ahead of Projected Burn Rate", Type = ModelFieldType.Int, });
                FPHDataModel2.Fields.Add(new ModelField() { Name = "StatusTotal", Type = ModelFieldType.Int });
    
    
                FPHStore2.Model.Add(FPHDataModel2);            
    
                var table = new DataTable();                        
    
                table.Columns.Add("LOCATION", typeof(string));
                table.Columns.Add("Healthy", typeof(int));
                table.Columns.Add("Expiring (>3 but <= 6 months remaining)", typeof(int));
                table.Columns.Add("Expiring within 3 months", typeof(int));
                table.Columns.Add("Inactive Rep Qty", typeof(int));
                table.Columns.Add("Completed", typeof(int));
                table.Columns.Add("Not Completed", typeof(int));
                table.Columns.Add("Ahead of Projected Burn Rate", typeof(int));            
    
                table.Rows.Add(new object[8] { "Samples in the Field at Risk", 100, 200, 300, 400, 500, 600, 700 });
                table.Rows.Add(new object[8] { "New Trained Rep", 200, 300, 300, 400, 500, 100, 200 });
                table.Rows.Add(new object[8] { "Zero Lctn", 0, 0, 0, 0, 0, 0, 0 });
                table.Rows.Add(new object[8] { "Zero Lctn2", 0, 0, 0, 0, 0, 0, 0 });
                table.Rows.Add(new object[8] { "Zero Lctn3", 0, 0, 0, 0, 0, 0, 0 });
                table.Rows.Add(new object[8] { "Zero Lctn4", 0, 0, 0, 0, 0, 0, 0 });
                table.Rows.Add(new object[8] { "Zero Lctn5", 0, 0, 0, 0, 0, 0, 0 });
                table.Rows.Add(new object[8] { "Zero Lctn6", 0, 0, 0, 0, 0, 0, 0 });
                table.Rows.Add(new object[8] { "Returning Product", 100, 200, 300, 400, 500, 600, 700 });
                table.Rows.Add(new object[8] { "Rep to Rep Transfers in Last 90 Days", 25, 200, 300, 77, 500, 489, 0 });
                table.Rows.Add(new object[8] { "Ahead of Projected Burn Rate", 25, 200, 300, 77, 500, 489, 0 });
                
                table.Columns.Add("StatusTotal", typeof(int));
                foreach (DataRow r in table.Rows)
                {
                    int statusTotal = 0;
    
                    for (int i= 0; i < table.Columns.Count; i++)
                    {
                        if (r[i].GetType() == typeof(int))
                        {
                            statusTotal += (int) r[i];
                        }
                    }                
                    r["StatusTotal"] = statusTotal;
                }
    
                FPHStore2.Data = table;
    
                FPHStore2.DataBind();
                //FPHStore2.FilterBy(new JFunction() { Fn = "custFilter" });
                FPHGridPanel.Store.Add(FPHStore2);
            }
            
        }
    </script>
    
    <!DOCTYPE html>
    
    <html>
    <head>
        <title>Select all from Grid Panel</title>    
    
        <script>
    
            var sumColumnRender = function (value, metadata, record, rowIndex, colIndex, store, gridView) {         
                return store.data.items[rowIndex].data["LOCATION"] + " Total:<br/>" + value;
            }
    
            var custSumRender = function (value, cols, colHeader, metadata) {
                return ('Total ' + colHeader + ':<br/>' + value);
            }
    
            var custGrandTotal = function (value, cols, colHeader, metadata) {
                return ('<b><i>Grand Total</i></b>:<br/>(<i>' + value + '</i>)');
            }
    
            var copyClipboardData = function () {            
    
                if (!App.Spreadsheet_SelectionModel.hasSelection()) {
                    Ext.Msg.alert("No selection", "There is no selection");
                    return;
                }
    
                //cache X-clipboard plugin selection into temporary <textarea> -> so we can copy into OS clipboard
                const el = document.createElement('textarea');
                el.value = App.Clipboard_Plugin.getTextData("cell");
                el.setAttribute('readonly', '');
                el.style.position = 'absolute';
                el.style.left = '-9999px';
                document.body.appendChild(el);
                el.select();
                document.execCommand('copy');
                document.body.removeChild(el);                         
            };
    
        </script>
    </head>
    <body>
        <form runat="server">
            <ext:ResourceManager runat="server" />        
    
            <ext:Button runat="server" Text="Explicit copy button" Handler="copyClipboardData"/>                    
    
            <ext:GridPanel ID="FPHGridPanel" runat="server" 
                Border="false" 
                Title="TEST GRIDPANEL - user needs to select all (including header row) " 
                ForceFit="true">
                
                <Features>
                    <ext:Summary runat="server" ShowSummaryRow="true" />
                </Features>
    
                <View>
                    <ext:GridView EnableTextSelection="true" Selectable="true"></ext:GridView>
                </View>             
    
                <SelectionModel>
                    <ext:SpreadsheetSelectionModel ID="Spreadsheet_SelectionModel" runat="server" />
                </SelectionModel>
                <Plugins>
                    <ext:Clipboard ID="Clipboard_Plugin" runat="server" />
                </Plugins>
    
            </ext:GridPanel>
            
        </form>
    </body>
    </html>
  5. #5
    also ... ideally when we click on the top left corner to perform the "select all" ... I'd like to be able to hook into that event, and add in the logic wired to the explicit copy button so everything implicitly copies into the user's clipboard.
    It's cleaner than having an extra button ...
  6. #6
    Hello @caleb!

    Thanks for the sample, I ran it and I believe I understand your issue well.

    Unfortunately that's a double no to your requests. Including the header in the copy is not possible (out of the box), was already asked in Sencha forums and is logged as a feature to be done. There's no builtin event you can tie to the top-left header cell, to make it do something else out of the ordinary.

    This does not mean it is impossible to do so, though.

    As for the first line, one way is just fetching the field names from the selected records and merging it with the copied text:

    el.value = Object.keys(App.FPHGridPanel.getStore().getAt(0).data).join("\t") + "\n" + App.Clipboard_Plugin.getTextData("cell");
    This would fail if there's no records in the store -- but you only want to trigger it when a selection is made, so at least one item should be in the store. It could be more elegant to fetch the column reference fields from the grid's view, which would also allow grabbing a handle of the number column in the spreadsheet view. But would need at least one explicit loop to get thru the columns to fetch their handles.

    As for capturing the click event in the top-left cell, I'll ask you a little time to figure out how it could be done (or better understand why it's not feasible to and share here).

    Hope this helps!
    Last edited by fabricio.murta; May 07, 2019 at 6:33 PM.

Similar Threads

  1. Replies: 0
    Last Post: Aug 09, 2016, 6:23 AM
  2. Replies: 4
    Last Post: Sep 14, 2015, 6:06 AM
  3. Replies: 2
    Last Post: Sep 12, 2015, 11:04 AM
  4. Replies: 1
    Last Post: Nov 12, 2012, 2:29 PM
  5. Replies: 4
    Last Post: Oct 11, 2011, 2:42 AM

Posting Permissions