ComponentColumn: Full components defined on data?

  1. #1

    ComponentColumn: Full components defined on data?

    Hello,

    Can you help me adding a full component inside a gridPanel's cell?

    Here's a sample of what I've been trying to do:
    <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="gridPanelWithComponent.aspx.cs" Inherits="ExtNetPlayground.gridPanelWithComponent" %>
    
    <%@ Register Assembly="Ext.Net" Namespace="Ext.Net" TagPrefix="ext" %>
    <%@ Import Namespace="x=Ext.Net" %>
    
    <script runat="server">
        protected void Page_Load(object sender, EventArgs e)
        {
            if (!X.IsAjaxRequest)
            {
                var ctner = new x.Container
                {
                    Width = 120,
                    Height = 20,
                    Html = "Hello, world. :)", // real case is supposed to have other Ext.Net components inside, created from codeBehind as well.
                    StyleSpec = "border: solid red 2px"
                };
                this.Store1.DataSource = new []
                {
                    new { Index=1, Percentage=0.2, ctner=ctner }/*,
                    new object[] { 2, 0.4 }, yay, different containers on different lines!
                    new object[] { 3, 0.6 },
                    new object[] { 4, 0.8 },
                    new object[] { 5, 1.0 }*/
                };
    
                this.Store1.DataBind();
    
                pn.UI = x.UI.Primary;
                pn.Frame = true;
                pn.Title = "Thats supposed to be shown on third column from gridPanel above";
                pn.Items.Add(ctner);
            }
        }
    </script>
    
    <!DOCTYPE html>
    
    <html>
    <head runat="server">
        <title>ComponentColumn Overview - Ext.NET Examples</title>
        <link href="/resources/css/examples.css" rel="stylesheet" />
    
        <style>
            .x-over-editor-grid tr.x-grid-row {
                height: 25px;
            }
        </style>
    </head>
    <body>
        <form runat="server">
            <ext:ResourceManager runat="server" />
    
            <h1>ComponentColumn Overview</h1>
    
            <ext:GridPanel
                runat="server"
                Title="ComponentColumn Overview"
                Cls="x-over-editor-grid"
                Width="620"
                Height="180">
                <Store>
                    <ext:Store ID="Store1" runat="server">
                        <Model>
                            <ext:Model runat="server">
                                <Fields>
                                    <ext:ModelField Name="Index" Type="Int" />
                                    <ext:ModelField Name="Percentage" Type="Float" />
                                    <ext:ModelField Name="ctner" />
                                </Fields>
                            </ext:Model>
                        </Model>
                    </ext:Store>
                </Store>
                <ColumnModel runat="server">
                    <Columns>
                        <ext:RowNumbererColumn runat="server" />
    
                        <ext:ComponentColumn runat="server">
                            <Component>
                                <ext:ProgressBar runat="server" Text="Progress" />
                            </Component>
                            <Listeners>
                                <Bind Handler="cmp.updateProgress(record.get('Percentage'))" />
                            </Listeners>
                        </ext:ComponentColumn>
    
                        <ext:ComponentColumn runat="server" OverOnly="true" Width="180">
                            <Component>
                                <ext:Container runat="server" Layout="HBoxLayout">
                                    <Items>
                                        <ext:Button runat="server" Text="Button 1" Icon="Add" />
                                        <ext:Button runat="server" Text="Button 2" Icon="Decline" />
                                    </Items>
                                </ext:Container>
                            </Component>
                            <Renderer Handler="metadata.style='color:gray;'; return 'Move mouse here';" />
                        </ext:ComponentColumn>
                        <ext:ComponentColumn ID="ctnercol" runat="server" Width="200">
                            <Listeners>
                                <Bind Handler="cmp.component.add(record.get('ctner'))" />
                            </Listeners>
                        </ext:ComponentColumn>
                    </Columns>
                </ColumnModel>
            </ext:GridPanel>
    
            <br />
            <h1>Grid content sample</h1>
            <br />
    
            <ext:Panel runat="server" ID="pn" />
        </form>
    </body>
    </html>
    I want to add a full component from codeBehind (and not just its value) cause I have built a container with a ext:Button and a ext:DrawComponent with several ext:Sprites inside (all in code behind), and want to put this container with everything inside it on the cell.

    Is there something obvious I am missing? :)
  2. #2
    I am thinking if that could be an option to use a renderer:

    on line 20 of the code above,
    new { Index=1, Percentage=0.2, ctner=ctner.ToScript() },
    Then forget about the componentColumn, and use an ordinary column and run that script thru the renderer, kind of like this:
    (lines 99 thru 103 will become)
                        <ext:Column ID="ctnercol" runat="server" Width="200">
                            <Renderer Handler="componentRenderer" />
                       </ext:ComponentColumn>
    And the client-side script would be
    <script>
        function componentRenderer(value, id, r) {
            //return 'value'; // this just prints the script, I'd rather interpret it
            return "<div id='9898'>oi</div>"; // tried to set container's 'RenderTo' property to this ID (now fixed, single row), with no luck.
        }
    </script>
    Last edited by fabricio.murta; Nov 14, 2014 at 9:37 PM.
  3. #3

    Got it working!

    Well, it seems this way things worked out. I am quite sure there were a better way to do this but, shall anyone feel like testing this, here is the minimalistic sample:

    <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="gridPanelWithComponent.aspx.cs" Inherits="ExtNetPlayground.gridPanelWithComponent" %>
    
    <%@ Register Assembly="Ext.Net" Namespace="Ext.Net" TagPrefix="ext" %>
    <%@ Import Namespace="x=Ext.Net" %>
    
    <script type="text/javascript">
        var idList = new Array();
        var scriptList = new Array();
        var lastDataRec = -1; // know if we are executing on the record's cell for the first or second+ time (shouldn't happen but does!)
        var componentsInjected = false; // marks whether the components were already injected or not
    
        // Renders the element to contain the component and stores the script that is pertained to that container.
        function componentRenderer(value, id, r) {
            if (lastDataRec != id.recordIndex) {
                lastDataRec = id.recordIndex; // mark as first run for this record
                return ""; // return dummy value for this run. will be overriden by the next run
            };
            var destId = Ext.id();
            idList[idList.length] = destId;
            scriptList[scriptList.length] = value;
            return "<center><span id='" + destId + "'/></center>"; // ext:Column's 'Align' is ignored, so we force centering here.
        }
    
        // Walks the recorded element container ID list adjusting and evaluating their correspondent scripts
        function injectComponents() {
            if (componentsInjected) return; // do nothing if we already put the components in
            if (idList.length > 0) { // triggered when there are components to be injected (FIXME: couldn't find a better event trigger)
                componentsInjected = true;
                var scriptAsNew = ""; // this will be the resulting ExtJs script to be evaluated.
                for (var i = 0; i < idList.length; i++) {
                    // if the container has more than one element, remove the code surrounding the Ext.create() statement.
                    scriptAsNew = scriptList[i].replace(/^.+Ext\.create(.+)\}\);$/, "Ext.create$1");
                    scriptAsNew = scriptAsNew.replace(/^Ext\.create\("([^"]+)",/, "new $1("); // turn Ext.create() into new Ext()
                    scriptAsNew = scriptAsNew.replace(/renderTo:[^,]+,/, ""); // remove the renderTo: reference from the script
                    scriptAsNew = scriptAsNew.replace(/;$/, ""); // remove trailing semicolon
                    scriptAsNew += ".render(document.body, idList[i])"; // append render command informing the desired container's ID
                    eval(scriptAsNew);
                }
            }
        }
    </script>
    <script runat="server">
        protected void Page_Load(object sender, EventArgs e)
        {
            if (!X.IsAjaxRequest)
            {
                // this is a requirement of this use case: a component defined from code behind.
                var ctner = new x.Container
                {
                    Width = 120,
                    Height = 20,
                    Html = "Hello, world. :)",
                    StyleSpec = "border: solid red 2px"
                };
                var ctner_copy = new x.Container
                {
                    Width = ctner.Width,
                    Height = ctner.Height,
                    Html = ctner.Html,
                    StyleSpec = ctner.StyleSpec
                };
                
                // note: at this point, the script could be manipulated to turn from 'Ext.create()' into new Ext().render(to);
                // we'll leaving this to client-side's .replace+regexp for now.
                this.Store1.DataSource = new []
                {
                    new { Index=1, Percentage=0.2, ctner=ctner.ToScript() } // the container is encapsulated as the script to build it
                };
    
                this.Store1.DataBind();
    
                pn.UI = x.UI.Primary;
                pn.Frame = true;
                pn.Title = "Thats supposed to be shown on third column from gridPanel above";
                pn.Items.Add(ctner_copy);
            }
        }
    </script>
    
    <!DOCTYPE html>
    
    <html>
    <head runat="server">
        <title>ComponentColumn Overview - Ext.NET Examples</title>
        <link href="/resources/css/examples.css" rel="stylesheet" />
    
        <style>
            .x-over-editor-grid tr.x-grid-row {
                height: 25px;
            }
        </style>
    </head>
    <body>
        <form runat="server">
            <ext:ResourceManager runat="server" />
    
            <h1>ComponentColumn Overview</h1>
    
            <ext:GridPanel
                runat="server"
                Title="ComponentColumn Overview"
                Cls="x-over-editor-grid"
                Width="620"
                Height="180">
                <Store>
                    <ext:Store ID="Store1" runat="server">
                        <Model>
                            <ext:Model runat="server">
                                <Fields>
                                    <ext:ModelField Name="Index" Type="Int" />
                                    <ext:ModelField Name="Percentage" Type="Float" />
                                    <ext:ModelField Name="ctner" />
                                </Fields>
                            </ext:Model>
                        </Model>
                    </ext:Store>
                </Store>
                <ColumnModel runat="server">
                    <Columns>
                        <ext:RowNumbererColumn runat="server" />
    
                        <ext:ComponentColumn runat="server">
                            <Component>
                                <ext:ProgressBar runat="server" Text="Progress" />
                            </Component>
                            <Listeners>
                                <Bind Handler="cmp.updateProgress(record.get('Percentage'))" />
                            </Listeners>
                        </ext:ComponentColumn>
    
                        <ext:ComponentColumn runat="server" OverOnly="true" Width="180">
                            <Component>
                                <ext:Container runat="server" Layout="HBoxLayout">
                                    <Items>
                                        <ext:Button runat="server" Text="Button 1" Icon="Add" />
                                        <ext:Button runat="server" Text="Button 2" Icon="Decline" />
                                    </Items>
                                </ext:Container>
                            </Component>
                            <Renderer Handler="metadata.style='color:gray;'; return 'Move mouse here';" />
                        </ext:ComponentColumn>
                        <ext:Column ID="ctnercol" runat="server" DataIndex="ctner" Width="200" Text="ContainerHere">
                            <Renderer Handler="componentRenderer" />
                        </ext:Column>
                    </Columns>
                </ColumnModel>
                <Listeners>
                    <%-- FIXME: Couldn't find a better event where it would fit. Still, some events, like column sorting, are erasing contents. --%>
                    <AfterLayout Handler="injectComponents()" />
                </Listeners>
            </ext:GridPanel>
    
            <br />
            <h1>Grid content sample</h1>
            <br />
    
            <ext:Panel runat="server" ID="pn" />
        </form>
    </body>
    </html>
    I've tested it adding a simple container (like in the sample), a container with a DrawComponent with Sprites inside, and a container (HBoxLayout) with a DrawComponent + Button. I don't know to which extent this will work.

    I still have to work on a better update scheme to keep the figure into the cell when the grid is updated. Any advice will be deeply appreciated.

    EDIT: most of this solution was inspired and based on http://www.techmix.net/2010/11/25/ad...sing-renderer/.
    Last edited by fabricio.murta; Nov 15, 2014 at 2:59 AM.
  4. #4

    Grid reloading fixes

    In the solution previously posted, the column contents were being erased if, for example, I reordered the columns.

    In order to fix this, I had to learn more about the mechanics of the gridPanel.

    Further investigating I could isolate the problem of double-load-attempts. With the componentLayoutCounter from id in the componentRenderer()'s "id" parameter. I found that while loading it was both 1 and 2, and only when 2 it worked.

    During column reordering, it says '4' but, is run only once so I just test against 2 during page load. Once loaded, I let it write the container on every renderer call (to this point, seems to be just '4').

    So, what changes in the above code:
    Lines 9 and 10 becomes
    var pageLoading = true;
    Lines 14-17 becomes:
    if (pageLoading && id.column.componentLayoutCounter != 2) {
        return ""
    };
    Line 26 is erased (will let the script run again even if ran once)

    Line 28 sets pageLoading as false once the script has run the first time, and becomes:
    if (pageLoading) { pageLoading = false };
    It will tell the renderer to, from now on, redraw the component on every renderer call.

    Between lines 38 and 39 you will add:
    idList = new Array();
    scriptList = new Array();
    So the arrays are reset for the next time the grid is rewritten. It will let the arrays hold only the corresponding data the grid has, and correctly obeys reordering/sorting/whatever.

    In the end, you will have the <script /> tag like:
        var idList = new Array();
        var scriptList = new Array();
        var pageLoading = true;
    
        // Renders the element to contain the component and stores the script that is pertained to that container.
        function componentRenderer(value, id, r) {
            if (pageLoading && id.column.componentLayoutCounter != 2) {
                return ""
            };
    
            var destId = Ext.id();
            idList[idList.length] = destId;
            scriptList[scriptList.length] = value;
            return "<center><span id='" + destId + "'/></center>"; // ext:Column's 'Align' is ignored, so we force centering here.
        }
    
        // Walks the recorded element container ID list adjusting and evaluating their correspondent scripts
        function injectComponents() {
            if (idList.length > 0) { // triggered when there are components to be injected (FIXME: couldn't find a better event trigger)
                if (pageLoading) { pageLoading = false };
                var scriptAsNew = ""; // this will be the resulting ExtJs script to be evaluated.
                for (var i = 0; i < idList.length; i++) {
                    // if the container has more than one element, remove the code surrounding the Ext.create() statement.
                    scriptAsNew = scriptList[i].replace(/^.+Ext\.create(.+)\}\);$/, "Ext.create$1");
                    scriptAsNew = scriptAsNew.replace(/^Ext\.create\("([^"]+)",/, "new $1("); // turn Ext.create() into new Ext()
                    scriptAsNew = scriptAsNew.replace(/renderTo:[^,]+,/, ""); // remove the renderTo: reference from the script
                    scriptAsNew = scriptAsNew.replace(/;$/, ""); // remove trailing semicolon
                    scriptAsNew += ".render(document.body, idList[i])"; // append render command informing the desired container's ID
                    eval(scriptAsNew);
                }
                idList = new Array();
                scriptList = new Array();
            }
        }
    And will have a happy fully synchorinzed grid with custom data, filled with custom Ext.NET components built from code behind.

    I hope this some day helps someone else having the same problem than me. Maybe this also can help improve Ext.NET to better support customization inside its grid panel.
  5. #5
    Hi @avenger,

    Thank you for sharing the solution!

    Here is something for your information.
    http://forums.ext.net/showthread.php?48911
  6. #6
    yay! that info was posted on my very birthday! :D
  7. #7
    That is the sign:)

    Happy Birthday!

Similar Threads

  1. [CLOSED] bind ajax proxy data to ComponentColumn Combobox in a grid
    By Sowjanya in forum 2.x Legacy Premium Help
    Replies: 4
    Last Post: Dec 11, 2013, 3:01 AM
  2. [CLOSED] GridPanel - ComponentColumn - ComboBox: How to bind data??
    By jamesand in forum 2.x Legacy Premium Help
    Replies: 4
    Last Post: Aug 20, 2013, 4:28 PM
  3. [CLOSED] ComponentColumn with more components displayed
    By aisi_it_admin in forum 2.x Legacy Premium Help
    Replies: 2
    Last Post: Jun 07, 2013, 7:48 PM
  4. Replies: 2
    Last Post: Nov 12, 2012, 5:56 PM
  5. Export Data to XML using full postback
    By adeng in forum 2.x Help
    Replies: 0
    Last Post: Aug 11, 2012, 7:56 AM

Tags for this Thread

Posting Permissions