PDA

View Full Version : [Bug 2.3.1]: UpdateContent exception on loaded control



michaeld
Nov 14, 2013, 11:54 AM
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.

michaeld
Nov 14, 2013, 12:33 PM
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.

Daniil
Nov 14, 2013, 2:20 PM
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.

michaeld
Nov 15, 2013, 3:10 AM
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.



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

Daniil
Nov 15, 2013, 6:24 AM
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.





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.



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);
}



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.

michaeld
Nov 15, 2013, 2:36 PM
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


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:

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.



We will review the thread. Thank you.

Great.


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

michaeld
Nov 15, 2013, 2:50 PM
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...

michaeld
Nov 18, 2013, 11:16 PM
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.

Daniil
Nov 19, 2013, 12:53 PM
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);
...
}

michaeld
Nov 20, 2013, 1:51 AM
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?

Daniil
Nov 20, 2013, 3:51 AM
To update ASP.NET controls during a DirectEvent you have to call the Update method on that control (an extension method inside Ext.NET).

protected void MsgTest( object sender, DirectEventArgs e )
{
HeadL.InnerHtml = "This text was changed";
HeadL.Update();
Button1.Text = "Proves button changed in directEvent";
ASPButton.Text = "Changed aspbutton";
ASPButton.Update();
}

michaeld
Nov 20, 2013, 4:50 AM
To update ASP.NET controls during a DirectEvent you have to call the Update method on that control (an extension method inside Ext.NET).

protected void MsgTest( object sender, DirectEventArgs e )
{
HeadL.InnerHtml = "This text was changed";
HeadL.Update();
Button1.Text = "Proves button changed in directEvent";
ASPButton.Text = "Changed aspbutton";
ASPButton.Update();
}

This is the type of topic I was describing in http://forums.ext.net/showthread.php?27212-Ext-Net-3-0&p=121202#post121202 that forces 2 code paths for basic ajax updates. I tried EventP.UpdateContent() at the end in MsgTest as well and that does the trick too. I think this will address the last of my topics preventing my ability to move forward without causing constant refreshes, especially if you guys are able to fix that other weakness with manually suspending the scripting. Thank you for walking me through. I realize there's an efficiency to only sending the client the items that are actually changed, but there are times where that efficiency needs be be measured against the universality of being able to update and manage universal layouts or sections of code, especially in a blackbox setting.

michaeld
Nov 20, 2013, 12:21 PM
I added the following to MsgTest:


Button1.Listeners.Render.Handler = "alert('Test');";


This listener does not get updated.

UpdateContent seems to be the only way to reliably address this but shouldn't the listener be able to be updated regardless? In my production case, I do not have all the variables to construct the handler until after the DirectEvent.

michaeld
Nov 20, 2013, 1:04 PM
UpdateContent in my production case actually created new problems including an exception on the client-side. In my production case, I have an accordion panel and all the Panels need to refresh just to update the Buttons section.


Element.override.getWidth (ext-all-dev.js:28029)
Ext.define.measureOwnerWidth (ext-all-dev.js:54673)
Ext.define.measureAutoDimensions (ext-all-dev.js:54546)
Ext.define.calculate (ext-all-dev.js:73365)
Ext.define.runLayout (ext-all-dev.js:169263)
Ext.define.runCycle (ext-all-dev.js:169232)
Ext.define.run (ext-all-dev.js:169163)
Ext.define.statics.flushLayouts (ext-all-dev.js:42443)
Ext.define.statics.resumeLayouts (ext-all-dev.js:42459)
Ext.resumeLayouts (ext-all-dev.js:46374)
(anonymous function) (VM7226:1)
Ext.net.DirectEvent.Ext.data.Connection.executeScr iptDelay (extnet-all-debug.js:1389)
Ext.net.DirectEvent.Ext.data.Connection.executeScr ipt (extnet-all-debug.js:1380)
Ext.net.DirectEvent.Ext.data.Connection.requestSuc cessHandler (extnet-all-debug.js:1321)
Ext.apply.callback (ext-all-dev.js:12089)
Ext.define.onComplete (ext-all-dev.js:48205)
Ext.define.onStateChange (ext-all-dev.js:48151)
(anonymous function) (ext-all-dev.js:3335)


All because I'm trying to do one thing; update the listener's handler with UpdateContent on a panel that has a button in the Buttons section with a listener. I'd prefer to just isolate and update the button, but when I call Update on the button, I'm notified in a server exception "The Lazy control (DetailP) can not be updated."

michaeld
Nov 20, 2013, 1:32 PM
I'm going to take the listener topic onto another thread. I'll try to create a sample tomorrow related to the UpdateContent issue as it seems unrelated.

Daniil
Nov 21, 2013, 4:30 AM
I'm going to take the listener topic onto another thread.

For references,
http://forums.ext.net/showthread.php?27308

To get this

Button1.Listeners.Render.Handler = "alert('Test');";
rendered to client, you can re-render the Button

Button1.Render();

Yes, updating Listeners and DirectEvents during a DirectEvent is much more complicated to do automatically. It is not just the Hidden property, then we can just call client side show/hide methods.

michaeld
Nov 21, 2013, 10:03 AM
Yes, updating Listeners and DirectEvents during a DirectEvent is much more complicated to do automatically. It is not just the Hidden property, then we can just call client side show/hide methods.

Can you elaborate on why?

What this means is I cannot rely on Panel.UpdateContent() to universally and reliably update an entire section on a page in a DirectEvent without manual tweaking of multiple paths based on white-box knowledge of the code. I'm back to square one on the no-refresh page paradigm.

Daniil
Nov 22, 2013, 4:20 AM
Well, I didn't meant that. I meant that it is going to be quite complicated for us to reflect this:

Button1.Listeners.Render.Handler = "alert('Test');";

to client automatically without any Render or UpdateContent calls by developer.

Well, an UpdateContent call of the Button1's container should re-render that Button wholly including the Listeners section. If it doesn't work in some case, it might be a bug.

michaeld
Nov 27, 2013, 9:54 AM
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.


I want to revisit this above. Can you update me on status?


Moreover, I want to talk more about what it looks like building a reusable user control. It's starting to get pretty cut-n-paste ugly. I'm considering starting to extend Ext.Net again to support some basic logic if it can't be provided.

Let me start at the beginning...

Level 1
I have a basic panel in a user control. Page_Load populate its members. So far so good.

Level 2
Okay, but there are DirectEvents, so for efficiency I skip rendering on posts-back by adding a test at the beginning of Page_Load with X.IsAjaxRequest and return right away. Very basic and we address 2 modes: Page_Load and DirectEvent.

Level 3
At this level I feel like there's limited extnet support. In this scenario, I've got DirectEvents constructing the User Control and rendering needs to be turned back on. I need an exception.

But at present, there's no way to ask Ext.net two questions: "Am I in a post-back?" but also "Do I need to render this control anyway?" It would be nice if I could ask, but as present I have to do something like this manually when I load the UserControl: Context.Items["DirectControlRender"]=true, so that when the user control enters Page_Load I can test (X.IsAjaxRequest && Context.Items["DirectControl"]==null).

But that's not all. Because as we've described earlier, I have to wrap the UserControl loading around turning on and off X.ControlsScripting before calling Panel1.ContentControls.Add( uc ).

The more controls I do this to, the more I'm confident there really are 3 modes. What's the status on all this? I'm hoping for some extension to all this before 2.4. Is that on the roadmap?

michaeld
Nov 27, 2013, 12:12 PM
I added the following to MyExtensions.cs to Ext.Net:


using System;
using System.Web;


namespace Ext.Net {
public partial class X {
public static bool DirectRendering {
get {
return HttpContext.Current.Items["DirectRendering"] != null;
}
set {
if( value )
HttpContext.Current.Items["DirectRendering"] = true;
else
HttpContext.Current.Items.Remove( "DirectRendering" );
}
}
public static bool IsAjaxRequestNotRendering {
get {
return ( X.IsAjaxRequest && !X.DirectRendering );
}
}
}
}


Then added the following extension method in my project:


public delegate void BindFn( System.Web.UI.Control ctr );

public static System.Web.UI.Control AddDirectControl( this ComponentBase ctnr, string ctlPath, string ctlName, BindFn bindFn = null ) {
// Add the user control
var uc = ctnr.Page.LoadControl( ctlPath );
uc.ClientIDMode = System.Web.UI.ClientIDMode.Static;
uc.ID = ctlName;

// Clear the Contents of the container
ctnr.ContentControls.Clear();

// Give a chance to bind before adding the control
if( bindFn != null )
bindFn( uc );

// Add the control
X.ControlsScripting = false;
X.DirectRendering = true;
ctnr.ContentControls.Add( uc );
X.ControlsScripting = true;
X.DirectRendering = false;

// Update the Container
ctnr.ReRender();
return uc;
}



In the DirectEvent creating the control, I can now do this:


DateP.AddDirectControl( "/ctl/EventDataFull.ascx", "uc",
(uc) => {
UIDataLayer.BindDataToControl<CCalendarItem>( Context, uc, calendaritem );
}
);


In the Page_Load of the user control EventDateFull.ascx I can now:


protected void Page_Load( object sender, EventArgs e ) {

if( X.IsAjaxRequestNotRendering )
return;
//...


This helps... But I'm still waiting for a more robust solution in ext.net.

michaeld
Dec 01, 2013, 3:15 PM
This code above has been pretty reliable for rendering user controls and replacing contents, but is there a way to add a user control without rerendering its parent and destroying all the client data in the process? That is, I'd like to render the new usercontrol only and add it to the existing layout? How would I go about that?

I attempted to use Render with the control and AddTo(parent) without much luck.

Vladimir
Dec 02, 2013, 10:21 AM
Hi,

About 'X.DirectRendering', ASP,NET allows async action during page life cycle.
That flag will be in invalid state if run controls rendering in several parallel threads

Why don't you want to add any property to user control which indicates that user control is rendered first time?


var uc = (MyUserControl)ctnr.Page.LoadControl( ctlPath );
uc.IsFirstRender = true; // IsFirstRender is bool property in MyUserControl


About 'ControlsScripting', if you update from SVN then it should be not required anymore to deactivate scripting before user control rendering



is there a way to add a user control without rerendering its parent and destroying all the client data in the process? That is, I'd like to render the new usercontrol only and add it to the existing layout? How would I go about that?


There is UserControlRenderer


UserControlRenderer.Render(new UserControlRendererConfig{....});

UnifyEducation
Dec 02, 2013, 10:54 AM
sorry , i posted my code in wrong place.

michaeld
Dec 02, 2013, 11:49 AM
About 'X.DirectRendering', ASP,NET allows async action during page life cycle.
That flag will be in invalid state if run controls rendering in several parallel threads

I am not aware upon any condition in which typical WebForms has this happen? Are you thinking Asynch pages perhaps?



Why don't you want to add any property to user control which indicates that user control is rendered first time?


var uc = (MyUserControl)ctnr.Page.LoadControl( ctlPath );
uc.IsFirstRender = true; // IsFirstRender is bool property in MyUserControl


True, I could extend System.Web.UI.UserControl to support this property. Personally, though, it seems more natural to me that an ajax library should provide this extension class as part of an all in one solution and set this value automatically, but all I can do is suggest this to you. As it is, the more I use DirectEvents to construct controls dynamically without refresh, the more I'm extending over Ext.Net to create my own library of extensions to more naturally support reliable user control creation so that if I have to transfer coding responsibilities over to new programmers, they will be be able to reliably avoid some of the pitfalls I've run into. So far, I've found UserControlRender the most reliable control renderer.




About 'ControlsScripting', if you update from SVN then it should be not required anymore to deactivate scripting before user control rendering

Nice, I'll fix upon 2.4 as we're now dependent on using CDN.



There is UserControlRenderer


UserControlRenderer.Render(new UserControlRendererConfig{....});

Yeah, I agree. I switched, http://forums.ext.net/showthread.php?27438-UserControlRendererConfig-does-not-render-some-controls (http://forums.ext.net/showthread.php?27438-UserControlRendererConfig-does-not-render-some-controls&p=122036#post122036)

Daniil
Feb 25, 2014, 5:57 AM
About 'X.DirectRendering', ASP,NET allows async action during page life cycle.
That flag will be in invalid state if run controls rendering in several parallel threads

I am not aware upon any condition in which typical WebForms has this happen? Are you thinking Asynch pages perhaps?


It looks like Vladimir missed your answer. Sorry. Though, is that still actual for you or we can close the thread?

michaeld
Mar 07, 2014, 9:49 AM
This thread was one of those where we were discussing the issues with User Control reusability. Eventually the fixes Vladimir made in 2.4 and the insights I made along the way lead me to being able to create the new Delete-and-render model I posted in User Extensions.

To start to answer your question, the original topic of this thread is about UserContent in a loaded control. The redundant call issue was remedied. The ID issue, I believe I resolved by asserting IDModes explicitly.

But the topic evolved into other issues related to UserControl reusability when attempting to render entirely new User Controls in a DirectEvent/Method. I describe why I had to create the DirectRendering property because Ext.Net has no way to indicate a 3rd mode where the UserControl's Page lifecycle calls Page_Load and you actually want the Page_Load to bind control data.

Typically, in a standard reloading of a page in a DirectEvent/Method, User Controls that are statically bound in the presentation layer or are explicitly dynamically added in the Page_Load lifecycle may not need to be bound because the DirectEvent/Method being called does not require the heavier binding cost of all controls to complete its purpose/function. Typically one tests X.IsAjaxRequest and skips around the binding. That is, it's not always necessary to do all the binding of all controls every time a DirectEvent/Method is fired so it makes sense to
if(X.IsAjaxRequest) return;

However, I had cases where the DirectEvent/Method was responsible for rendering a totally new UserControl and that was triggering it's Page_Load. In that case, while in the DirectEvent/Method, you actually want the binding of the control to happen before it's rendering because its created during the DE/M. If the developer does the typical test of X.IsAjaxRequest, it will incidentally skip the binding which is not correct. That's why I created DirectRendering and IsAjaxRequestNotRendering properties so that instead in Page_Load I could test
if(X.IsAjaxRequestNotRendering) return;This property returns true only if DirectRendering==false and X.IsAjaxRequest==true so that the User Control can bind the control even though it is inside the DirectEvent/Method, just like it was an original render (GET). [in other words, bind if !X.IsAjaxRequest or (X.IsAjaxRequest and DirectRendering)].

Vladimir said my implementation wouldn't work in parallel mode. I never got the clarification of the scenario why. I suspect it has to do with async modes now popular with latest versions of MS MVC and WebForm Asynch Pages which might make sense as I use Context.Items which may change by the time it actually returns. I'm not sure that's actually true though without looking more closely the page life-cycle between the 1st pass and the 2nd reenter. I'm pretty sure that the Page_Load events called after the original DirectEvent/Method are within the same thread though.

Anyway, so to answer your question, close it if I'm right about that last claim. Keep it open if Vladimir has more to add.

But since 3.0 is now on the docket, it's really time for you guys to look at the 3rd mode I've raised as an issue and look at it as a design concern. There are really 3 modes - 1. Original Load/Refresh (GET), 2. DirectEvent/Method (POST), and 3. DirectEvent/Method (POST) but now while rendering a new User Control that will call its Page_Load (which btw may have its own user controls it's rendering too).

And in most cases, the developer needs to ask/test one thing. Do I bind or exit in Page_Load? In the 1st case yes, 2nd case no, and 3rd case yes. It might even be that the developer may need to know which of the 3 modes it's actually in.

Daniil
Mar 07, 2014, 12:18 PM
Hi @michaeld,

Thank you for the detailed answer.

I am going to add the link to your post to the Agenda of our next group meeting on March 11.

michaeld
Mar 07, 2014, 10:17 PM
Thanks.

While we're on this topic, I should note that I did run into other DirectEvent/Method scoping problems. I had advanced/special circumstances where, for efficiency, I needed even more information available to Page_Load events for the Page & User Controls to allow initialization to be done so the later rendered User Controls inside the DirectEvent/Method had access to that data previously initialized.

In those cases, it would be ideal to have access to some kind of config parameters (like extraParams) that could be passed in from the client-side proxy that are scoped/accessible in the Page_Load.

Client-side example:


App.direct.MyDirectMethod(param1, param2, { 'SpecialCase', true } );


Server-side example for Page's Page_Load:


// Do the initialization if not post-back or it is post-back and 'SpecialCase' was passed from the DirectEvent/Method
var sc = X.ExtraParams["SpecialCase"];
if(!X.IsAjaxRequest || (sc != null && (bool)sc == true) ) {
// Do initialization
}


I couldn't implement this without branching Ext.Net because it would require changes to the client Direct Method api to add the extra parameters to the postback request, extension of the X class static properties, and changes to the RaisePostBackEvent code to read the postback request for the extra parameters and set them so they are accessible to X.ExtraParams.

For DirectEvents, it would be a little easier because ExtraParams is already a part of DirectEvent and passed to the server. Still there is no way to scope to these values from Page_Load.


This proposed extension allows the developer to selectively run expensive initialization in Page_Loads prior to the DirectEvents/Method but only when the specific DirectEvent/Method absolutely requires it to occur (as determined by code from the client).

michaeld
Mar 13, 2014, 11:53 PM
Any update from the 11th?

geoffrey.mcgill
Mar 14, 2014, 5:51 AM
It's going to take some time for us to fully digest this request.

I have spend some time reviewing the full thread again, then discuss in detail with the crew.

We did discuss yesterday, but more review is required.