[Bug 2.3.1]: UpdateContent exception on loaded control

Page 1 of 3 123 LastLast
  1. #1

    [Bug 2.3.1]: UpdateContent exception on loaded control

    aspx
    <%@ Page Language="C#" EnableViewState="false" %>
    <%@ Register Assembly="Ext.Net" Namespace="Ext.Net" TagPrefix="ext" %>
    
    
    <script runat="server">
        protected void Page_Load(object sender, EventArgs e) {
    	}
    	protected void CreateTest( object sender, DirectEventArgs e ) {
    		RP.ContentControls.Clear();
    		var uc = Page.LoadControl( "Test39.ascx" );
    		RP.ContentControls.Add( uc );
    		RP.UpdateContent();
    	}
    </script>
    
    
    <!DOCTYPE html>
    <html>
    <head id="Head1" runat="server">
    	<title>Ext.NET Example</title>
    </head>
    <body>
    	<form id="Form1" runat="server">
    
    
    		<ext:ResourceManager ID="ResourceManager1" runat="server" ScriptMode="Development" SourceFormatting="true" />
    		<ext:Viewport ID="vp" runat="server" Layout="HBoxLayout">
    			<Items>
    
    
    				<ext:Container ID="LP" runat="server" Border="true" Padding="5" Flex="1" Layout="FitLayout">
    					<Items>
    						<ext:Button runat="server" Text="Test Right Panel Load">
    						<DirectEvents>
    							<Click OnEvent="CreateTest" />
    						</DirectEvents>
    						</ext:Button>								
    					</Items>
    				</ext:Container>
    
    
    				<ext:Container ID="RP" runat="server" Flex="1" Layout="VBoxLayout">
    					<Items>
    					</Items>
    				</ext:Container>
    
    
    			</Items>
    		</ext:Viewport>
    	</form>
    </body>
    </html>
    ascx
    <%@ Control Language="C#" ClassName="Test39" %>
    
    
    <script runat="server">
        protected void Page_Load(object sender, EventArgs e) {
    		EventP.Title  = "Test Right Panel";
    		TitleL.InnerHtml = "This is a test title";
    		HeadL.InnerHtml = "This is a test head";
        }
    </script>
    
    
    <ext:Panel ID="EventP" runat="server" Flex="1">
    	<Content>
    		<div class="TitlePnl">
    			<h1 id="TitleL" runat="server" class="Title" />
    			<div class="HeadPnl">
    				<span id="HeadL" runat="server" class="Head" />
    			</div>
    		</div>
    	</Content>
    </ext:Panel>
    There seem to be 2 problems with this code. 1) id names don't match when calling extjs functions, and 2) setTitle is redundant.
  2. #2
    In my production code, I'm seeing both these issues and it's rampant in all ext.net controls. Both the naming reference issues and the redundant calls.
  3. #3
    Hi @michaeld,

    This
    EventP.Title  = "Test Right Panel";
    generates script, because it occurs during an AJAX request, i.e. DirectEvent.

    Yes, it turns out that the EventP is a dynamic control and the scripts are not supposed to be generated. But, I am afraid, we cannot determine it automatically - the EventP is a dynamic control or not.

    As for ids mismatching. First, the user control's Page_Load is executed after:
    RP.ContentControls.Add(uc);
    So, this executes.
    EventP.Title = "Test Right Panel";
    It uses the user control's ID generated by ASP.NET.

    Then, the "final" user control's id is generated during the UpdateContent method. Such a sequence. We will look if we can improve something.

    The options are:

    1. Suspend scripting for the EventP control.
    EventP.SuspendScripting();
    EventP.Title = "Test Right Panel";
    2. Suspend scripting at all inside the Page_Load.
    X.ControlsScripting = false;
    EventP.Title = "Test Right Panel";
    3. Use the IDynamicUserControl interface.
    <%@ Implements Interface="Ext.Net.IDynamicUserControl" %>
    
    <script runat="server">
        public void BeforeRender()
        {
            EventP.Title = "Test Right Panel";
            TitleL.InnerHtml = "This is a test title";
            HeadL.InnerHtml = "This is a test head";
        }
    </script>
    Though, there is a big disadvantage. The BeforeRender won't be executed for a static user control.
  4. #4
    Quote Originally Posted by Daniil View Post
    Yes, it turns out that the EventP is a dynamic control and the scripts are not supposed to be generated. But, I am afraid, we cannot determine it automatically - the EventP is a dynamic control or not.
    Can you elaborate on why?

    Still, this says it all. "You cannot determine it automatically."

    What this tells me is if we can't determine it automatically, then we may need some way to tell Ext what mode we're trying to render in. There seem to be at least 3 possibilities. 1. First Time Page Load mode (renders initialization extjs json), 2. Ajax Direct Event Normal Mode (renders javascript extjs function calls), 3. Ajax Direct Event Control Construction Mode (renders initialization extjs json and add new control names to the App global so they can be referenced)

    #3 seems to be the scenario that's missing. Nothing is presently being added to App global, and the new controls need to be.

    We should be able to do something like this in the DirectEvent that will be adding new controls to notify ExtNet that we are constructing new controls that will be calling Page_Load.

    One option is ...
    protected void CreateTest( object sender, DirectEventArgs e ) {
            // This tells the Page renderer we're going to be adding controls in a direct event
            RP.AddControlsInDirectEvent = true;
    
    
            RP.ContentControls.Clear();
            var uc = Page.LoadControl( "Test39.ascx" );
            RP.ContentControls.Add( uc );
            RP.UpdateContent();
        }
    Alternately, right now ContentControls is using System.Web.UI.ControlCollection. If instead you derived your own Ext.Net.ControlsCollection from System.Web.UI.ControlCollection, you could override Add and AddAt to set this mode automatically based on X.IsAjaxRequest. This makes the best sense to me.


    Quote Originally Posted by Daniil View Post
    The options are:

    1. Suspend scripting for the EventP control.
    EventP.SuspendScripting();
    EventP.Title = "Test Right Panel";
    2. Suspend scripting at all inside the Page_Load.
    X.ControlsScripting = false;
    EventP.Title = "Test Right Panel";
    3. Use the IDynamicUserControl interface.
    <%@ Implements Interface="Ext.Net.IDynamicUserControl" %>
    
    <script runat="server">
        public void BeforeRender()
        {
            EventP.Title = "Test Right Panel";
            TitleL.InnerHtml = "This is a test title";
            HeadL.InnerHtml = "This is a test head";
        }
    </script>
    Though, there is a big disadvantage. The BeforeRender won't be executed for a static user control.
    Okay, these make sense as an alternate for 2.3.X now, but none do I deem acceptable for the future. It means I have to write many different paths if I want to use the same user control for both Direct Events and Initial load. Please see my topic on http://forums.ext.net/showthread.php?27212-Ext-Net-3-0
    Last edited by michaeld; Nov 15, 2013 at 2:42 AM.
  5. #5
    Quote Originally Posted by michaeld View Post
    Can you elaborate on why?

    Still, this says it all. "You cannot determine it automatically."
    Well, ASP.NET is a stateless system. It doesn't save and restore the page's state of previous requests. So, all the controls are recreated on each request. We cannot determine automatically it is created at first time or not.

    Quote Originally Posted by michaeld View Post
    RP.AddControlsInDirectEvent = true;
    Well, good. But it is already possible to produce the same effect by:
    X.ControlsScripting = false;
    RP.ContentControls.Add(uc);
    X.ControlsScripting = true;
    It looks good enough for the scenario. I just didn't realize before that it can be called in the page rather than in the user control.

    Also the Ext.Net.BaseControl has the IsDynamic property. Where possible to determine, we set up it automatically, but it is public and you can set up it to true if needed. A control doesn't generate scripts if its IsDynamic is true.

    Quote Originally Posted by michaeld View Post
    Alternately, right now ContentControls is using System.Web.UI.ControlCollection. If instead you derived your own Ext.Net.ControlsCollection from System.Web.UI.ControlCollection, you could override Add and AddAt to set this mode automatically based on X.IsAjaxRequest. This makes the best sense to me.
    Do you mean if X.IsAjaxRequest is true, we could set up RP.AddControlsInDirectEvent to true automatically? Hmm... But if the page has the following, for example?
    protected void Page_Load(object sender, EventArgs e) 
    {
        this.Panel1.ContentControls.Add(userControl);
    }
    Quote Originally Posted by michaeld View Post
    Okay, these make sense as an alternate for 2.3.X now, but none do I deem acceptable for the future. It means I have to write many different paths if I want to use the same user control for both Direct Events and Initial load. Please see my topic on http://forums.ext.net/showthread.php?27212-Ext-Net-3-0
    We will review the thread. Thank you.
  6. #6
    Quote Originally Posted by Daniil View Post
    Well, good. But it is already possible to produce the same effect by:
    X.ControlsScripting = false;
    RP.ContentControls.Add(uc);
    X.ControlsScripting = true;
    That might prevent the redundant code, but you haven't addressed the fact that right now, the code doesn't do what I posed in scenario 3. You need to add the App globals for the new controls or else you can't refer to them in javascript. This is a new control on the page and it may have it's own DirectEvents/Methods even. They need to be added. I haven't even gotten to testing that scenario, but I'm pretty sure that won't work given that EventP.setTitle() exceptions. You may even think it might be appropriate to remove the ones that were destroyed in the RP.ContentControls.Clear() as well.

    Moreover, your proposal would kill any intentional scripting that was intended to happen in the control. Example, say the control wants to

    Quote Originally Posted by Daniil View Post
    Do you mean if X.IsAjaxRequest is true, we could set up RP.AddControlsInDirectEvent to true automatically? Hmm... But if the page has the following, for example?
    protected void Page_Load(object sender, EventArgs e) 
    {
        this.Panel1.ContentControls.Add(userControl);
    }
    I'm not sure one of us is on the same page. Based on the example you just gave, if you implemented as I said here:
    Quote Originally Posted by michaeld View Post
    Alternately, right now ContentControls is using System.Web.UI.ControlCollection. If instead you derived your own Ext.Net.ControlsCollection from System.Web.UI.ControlCollection, you could override Add and AddAt to set this mode automatically based on X.IsAjaxRequest. This makes the best sense to me.
    ... the Add would be able to wrap the Add in something like this...
    X.ControlsScripting = false;
    base.Add(uc);
    X.ControlsScripting = true;
    ... and work.


    Quote Originally Posted by Daniil View Post
    We will review the thread. Thank you.
    Great.


    Still, do you mind looping vladimir to look at this current topic?
  7. #7

    Proof

    aspx
    <%@ Page Language="C#" EnableViewState="false" %>
    
    
    <%@ Register Assembly="Ext.Net" Namespace="Ext.Net" TagPrefix="ext" %>
    
    
    <script runat="server">
        protected void Page_Load(object sender, EventArgs e) {
    		if(X.IsAjaxRequest && ucLoaded.Text == "1")
    			RP.ContentControls.Add( Page.LoadControl( "Test39.ascx" ) );
    		
    	}
    	protected void CreateTest( object sender, DirectEventArgs e ) {
    		RP.ContentControls.Clear();
    		X.ControlsScripting = false;
    		var uc = Page.LoadControl( "Test39.ascx" );
    		RP.ContentControls.Add( uc );
    		X.ControlsScripting = true;
    		RP.UpdateContent();
    		ucLoaded.Text = "1";
    	}
    </script>
    
    
    <!DOCTYPE html>
    <html>
    <head id="Head1" runat="server">
    	<title>Ext.NET Example</title>
    </head>
    <body>
    	<form id="Form1" runat="server">
    
    
    		<ext:ResourceManager ID="ResourceManager1" runat="server" ScriptMode="Development" SourceFormatting="true" />
    		<ext:Viewport ID="vp" runat="server" Layout="HBoxLayout">
    			<Items>
    
    
    				<ext:Container ID="LP" runat="server" Border="true" Padding="5" Flex="1" Layout="FitLayout">
    					<Items>
    						<ext:Button runat="server" Text="Test Right Panel Load">
    						<DirectEvents>
    							<Click OnEvent="CreateTest" />
    						</DirectEvents>
    						</ext:Button>								
    						<ext:Hidden ID="ucLoaded" runat="server" Text="0" />
    					</Items>
    				</ext:Container>
    
    
    				<ext:Container ID="RP" runat="server" Flex="1" Layout="VBoxLayout">
    					<Items>
    					</Items>
    				</ext:Container>
    
    
    			</Items>
    		</ext:Viewport>
    	</form>
    </body>
    </html>
    ascx
    <%@ Control Language="C#" ClassName="Test39" %>
    
    
    <script runat="server">
        protected void Page_Load(object sender, EventArgs e) {
    		EventP.Title  = "Test Right Panel";
    		TitleL.InnerHtml = "This is a test title";
    		HeadL.InnerHtml = "This is a test head";
        }
    	protected void MsgTest( object sender, DirectEventArgs e ) {
    		X.Msg.Alert( "Test UC DirectEvent", "Test UC DirectEvent" );
    	}
    </script>
    
    
    <ext:Panel ID="EventP" runat="server" Flex="1">
    	<Content>
    
    
    		<div class="TitlePnl">
    			<h1 id="TitleL" runat="server" class="Title" />
    			<div class="HeadPnl">
    				<span id="HeadL" runat="server" class="Head" />
    			</div>
    		</div>
    
    
    	</Content>
    	<Items>
    		<ext:Button ID="Button1" runat="server" Text="Test DirectEvent in Loaded Control">
    			<DirectEvents>
    				<Click OnEvent="MsgTest" />
    			</DirectEvents>
    		</ext:Button>
    	</Items>
    </ext:Panel>

    As predicted, this fails...
  8. #8
    Just a note. This last example is a show-stopper for the upgrades we're making to refresh the page less since that seems to be the second greatest complaint coming back from our beta-testers before our public launch. Mostly it's how long it takes just to edit items on a page because we do a refresh after most edits. We can address a lot of these without creating new user controls in the DirectEvent, but some will need to given their size. The user controls have their own Direct Events too so we need to get these to work. That's why I showed this last example.
  9. #9
    Quote Originally Posted by michaeld View Post
    Still, do you mind looping vladimir to look at this current topic?
    Vladimir is already involved into this discussion:) Generally speaking, I always discuss with him any heavy-weight cases.

    Currently, we have some idea which might make the case working without manual suspending of scripting. Though, we have to test it a bit more. We will update the thread with a final decision.

    The user controls have their own Direct Events too so we need to get these to work.
    You recreate the user control in the Page_Load, that is correct. But you have to define some ID for that control explicitly. Otherwise, it will be auto-generated and it will always a new one. So:
    protected void Page_Load(object sender, EventArgs e)
    {
        if (X.IsAjaxRequest && ucLoaded.Text == "1")
        {
            var uc = Page.LoadControl("TestUC.ascx");
            uc.ID = "UserControl1";
            RP.ContentControls.Add(uc);
        }
    }
    
    protected void CreateTest(object sender, DirectEventArgs e)
    {
        ...
        var uc = Page.LoadControl("TestUC.ascx");
        uc.ID = "UserControl1";
        RP.ContentControls.Add(uc);
        ...
    }
  10. #10
    Okay, I corrected code to address the oversight you mentioned. I added some more controls to the ascx to show another issue at the end. These may not be new problems.

    aspx
    <%@ Page Language="C#" EnableViewState="false" %>
    
    
    <%@ Register Assembly="Ext.Net" Namespace="Ext.Net" TagPrefix="ext" %>
    
    
    <script runat="server">
        protected void Page_Load(object sender, EventArgs e) {
            if( X.IsAjaxRequest && ucLoaded.Text == "1" ) {
                var uc = Page.LoadControl( "Test39.ascx" );
                uc.ID = "uc1";
                RP.ContentControls.Add(uc);
            }        
        }
        protected void CreateTest( object sender, DirectEventArgs e ) {
            RP.ContentControls.Clear();
            X.ControlsScripting = false;
            var uc = Page.LoadControl( "Test39.ascx" );
            uc.ID = "uc1";
            RP.ContentControls.Add( uc );
            X.ControlsScripting = true;
            RP.UpdateContent();
            ucLoaded.Text = "1";
        }
    </script>
    
    
    <!DOCTYPE html>
    <html>
    <head id="Head1" runat="server">
        <title>Ext.NET Example</title>
    </head>
    <body>
        <form id="Form1" runat="server">
    
    
            <ext:ResourceManager ID="ResourceManager1" runat="server" ScriptMode="Development" SourceFormatting="true" />
            <ext:Viewport ID="vp" runat="server" Layout="HBoxLayout">
                <Items>
    
    
                    <ext:Container ID="LP" runat="server" Border="true" Padding="5" Flex="1" Layout="FitLayout">
                        <Items>
                            <ext:Button runat="server" Text="Test Right Panel Load">
                            <DirectEvents>
                                <Click OnEvent="CreateTest" />
                            </DirectEvents>
                            </ext:Button>                                
                            <ext:Hidden ID="ucLoaded" runat="server" Text="0" />
                        </Items>
                    </ext:Container>
    
    
                    <ext:Container ID="RP" runat="server" Flex="1" Layout="VBoxLayout">
                        <Items>
                        </Items>
                    </ext:Container>
    
    
                </Items>
            </ext:Viewport>
        </form>
    </body>
    </html>
    ascx
    <%@ Control Language="C#" ClassName="Test39" %>
    
    
    <script runat="server">
        protected void Page_Load(object sender, EventArgs e) {
            EventP.Title  = "Test Right Panel";
            TitleL.InnerHtml = "This is a test title";
            HeadL.InnerHtml = "This is a test head";
        }
        protected void MsgTest( object sender, DirectEventArgs e ) {
            HeadL.InnerHtml = "This text was changed";
            Button1.Text = "Proves button changed in directEvent";
            ASPButton.Text = "Changed aspbutton";
            X.Msg.Alert( "Test UC DirectEvent", "Test UC DirectEvent" ).Show();
        }
    </script>
    
    
    <ext:Panel ID="EventP" runat="server" Flex="1">
        <Content>
    
    
            <div class="TitlePnl">
                <h1 id="TitleL" runat="server" class="Title" />
                <div class="HeadPnl">
                    <span id="HeadL" runat="server" class="Head" />
                </div>
                <asp:Button ID="ASPButton" runat="server" Text="AspButton" />
            </div>
    
    
        </Content>
        <Items>
            <ext:Button ID="Button1" runat="server" Text="Test DirectEvent in Loaded Control">
                <DirectEvents>
                    <Click OnEvent="MsgTest" />
                </DirectEvents>
            </ext:Button>
        </Items>
    </ext:Panel>


    The HeadL.InnerHtml does not get set, nor does AspButton. I imagine this is a problem in DirectEvents in general. I don't know if there is a solution, but would I need to wrap these in an UpdatePanel to get these microsoft controls to update too?
    Last edited by michaeld; Nov 20, 2013 at 12:58 AM.
Page 1 of 3 123 LastLast

Similar Threads

  1. Replies: 1
    Last Post: Apr 02, 2013, 5:03 AM
  2. Replies: 1
    Last Post: Oct 10, 2012, 11:47 AM
  3. [CLOSED] [1.0] User Control loaded several times
    By FVNoel in forum 1.x Legacy Premium Help
    Replies: 4
    Last Post: Jul 07, 2011, 10:33 AM
  4. Replies: 2
    Last Post: Oct 29, 2010, 8:51 AM
  5. Replies: 2
    Last Post: Feb 10, 2010, 10:45 AM

Posting Permissions