PDA

View Full Version : [CLOSED] Any alternative ways to perform same "ASCX" with multi instances assembling?



cleve
Jul 20, 2012, 3:39 PM
I code some Ext components into single ascx. And want to bind two same controls but with different data bindings. Coding like the following:



<ext:Panel runat="server" ID="pMain" Layout="FitLayout">
<TopBar>
<ext:Toolbar runat="server">
<Items>
<ext:Button runat="server" ID="B1" Icon="Reload" />
<ext:ToolbarSeparator runat="server" />
</Items>
</ext:Toolbar>
</TopBar>
<Items>
<ext:DataView runat="server" ItemSelector="li">
<Store>
<ext:Store runat="server" AutoLoad="false" />
</Store>
<Tpl runat="server">
<Html>
<div style="padding:10px;">
<ul>
<tpl for=".">
<li>
<a href="javascript:void(0)" onclick="{[this.instanceName]}.BeginView(ISSUtil.GUID.Normalize('{UniqueId}'))">
{Name}
</a>
</li>
</tpl>
</ul>
</div>
</Html>
</Tpl>
</ext:DataView>
</Items>
</ext:Panel>


Afore code is encapsulated into ascx file and re-assemble into Page by:



Control c1 = container.Page.LoadControl(controlPath);
Control c2 = container.Page.LoadControl(controlPath);
container.ContentControls.Add(c1);
container.ContentControls.Add(c2);


Error happened for sure if two more instances inserted into ContentControls because IDs are conflicted.
Can I have some alternative ways to "Add" same ascx control with more than one instance without worrying about ID conflict?

Daniil
Jul 20, 2012, 5:03 PM
Hi,

It should be ok with default IDMode.

I suspect you have overrode IDMode somewhere, isn't that so?

Please try to set up

IDMode="Legacy"
for the "pMain" Panel.

cleve
Jul 21, 2012, 2:20 AM
Hi Daniil, I cook up a more concrete example and share my finding:

Control1.ascx: to define two ext components (panel) with ID set to different and specific.


<%@ Control Language="C#" Inherits="Test.MyControl" %>
<ext:Panel runat="server" ID="p1" Title="X1" />
<ext:Panel runat="server" ID="p2" Title="X2" />
<script runat="server">

public override List<AbstractContainer> GetContainers()
{
return new List<AbstractContainer>() { p1, p2 };
}


</script>


Control.cs: to define a generic interface and gain access to ext component


using System;
using System.Collections.Generic;
using System.Web;
using System.Web.UI;


using Ext.Net;
namespace Test
{
public interface IMyInterface
{
List<AbstractContainer> GetContainers();
}


public abstract class MyControl : UserControl, IMyInterface
{
public abstract List<AbstractContainer> GetContainers();
}
}


Index.aspx: to assemble two same-kind instances into the page, with different methods


<%@ Page Language="C#" %>
<%@ Register Assembly="Ext.Net" Namespace="Ext.Net" TagPrefix="ext" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<script src="/scripts/ext/coolite.js" type="text/javascript"></script>
<script src="/scripts/ext/isscontrols.js" type="text/javascript"></script>
</head>
<body id="theBody">
<ext:ResourceManager ID="theManager" runat="server" IDMode="Inherit" Theme="Default"
EnableViewState="false" DisableViewState="true" ShowWarningOnAjaxFailure="false"
ScriptMode="Debug" />
<ext:Panel runat="server" ID="p" Layout="AccordionLayout" />
<script language="javascript" type="text/javascript">

var testFunc = function () {
}


</script>
<form runat="server" id="theForm" />
</body>
</html>
<script runat="server">

protected override void OnInit(EventArgs e)
{
base.OnInit(e);

// control level assemble, add into content control
Control c1 = Page.LoadControl("control1.ascx");
Control c2 = Page.LoadControl("control1.ascx");
p.ContentControls.Add(c1);
p.ContentControls.Add(c2);


// ext component level, not working
Test.IMyInterface i1 = c1 as Test.IMyInterface;
Test.IMyInterface i2 = c2 as Test.IMyInterface;
p.Items.Add(i1.GetContainers());
p.Items.Add(i2.GetContainers());
}


</script>


Findings:
1. LoadControl and then put instance as UserControl into Panel.ContentControls, ID works fine.
2. LoadControl and singulate ext components within and then put into Panel.Items, ID not generated.

The reason why I promote the second assembling method is because: sometimes you would logically make one Ext panel and one Ext Store canned within single ASCX. While assembling, you add panel to particular accordion let that panel fire the store on demand. At this scenario, ContentControls won't allow ASCX instance fit in because "Control with type 'Ext.Net.Store' cannot be handled by layout"
while the second method works fine because it selectively pick up panel to item collection. Unfortunately, second method has ID problem.

Any comments ?

Daniil
Jul 21, 2012, 10:16 PM
I have not understood yet the problem, but I can suggest something on this item:



The reason why I promote the second assembling method is because: sometimes you would logically make one Ext panel and one Ext Store canned within single ASCX. While assembling, you add panel to particular accordion let that panel fire the store on demand. At this scenario, ContentControls won't allow ASCX instance fit in because "Control with type 'Ext.Net.Store' cannot be handled by layout

You can place the Store into the Bin of some container, here is an example:
http://forums.ext.net/showthread.php?13726&p=57259&viewfull=1#post57259

cleve
Jul 22, 2012, 1:07 AM
I have not understood yet the problem, but I can suggest something on this item:



You can place the Store into the Bin of some container, here is an example:
http://forums.ext.net/showthread.php?13726&p=57259&viewfull=1#post57259

That's helpful. Brief in short, try the following and could you comment how to make it work.



<%@ Page Language="C#" %>
<%@ Register Assembly="Ext.Net" Namespace="Ext.Net" TagPrefix="ext" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<script src="/scripts/ext/coolite.js" type="text/javascript"></script>
<script src="/scripts/ext/isscontrols.js" type="text/javascript"></script>
</head>
<body id="theBody">
<ext:ResourceManager ID="theManager" runat="server" IDMode="Legacy" Theme="Default"
EnableViewState="false" DisableViewState="true" ShowWarningOnAjaxFailure="false"
ScriptMode="Debug" />
<ext:Panel runat="server" ID="p" Layout="AccordionLayout" IDMode="Legacy" />
<script language="javascript" type="text/javascript">

var testFunc = function () {
}


</script>
<form runat="server" id="theForm" />
</body>
</html>
<script runat="server">

protected override void OnInit(EventArgs e)
{
base.OnInit(e);


// not work
p.Items.Add(new Panel() { ID = "pTest", IDMode = IDMode.Legacy });
p.Items.Add(new Panel() { ID = "pTest", IDMode = IDMode.Legacy });


// not work either
//p.ContentControls.Add(new Panel() { ID = "pTest", IDMode = IDMode.Legacy });
//p.ContentControls.Add(new Panel() { ID = "pTest", IDMode = IDMode.Legacy });
}


</script>


Everything's IDMode set to "Legacy" still prompt "App.pTest" ID conflict.

Daniil
Jul 22, 2012, 11:53 PM
Well, the client ids will be the same in that case "App.pTest" in that case.

They were different in the previous case with the user controls because a user control implements the INamingContainer interface, therefore its ID participates in client ids of the inner controls (with IDMode="Legacy"). There are two user controls with auto-generated IDs, i.e. different ID. The Panel doesn't inherit the INamingContainer. But, the main fact is the one that the Panel is a single comparing with two user controls previously. So, no way to get unique client ids.

So, you have to set up some unique IDs in that case or don't set up at all to get them auto-generated.

cleve
Jul 23, 2012, 2:12 AM
Well, the client ids will be the same in that case "App.pTest" in that case.

They were different in the previous case with the user controls because a user control implements the INamingContainer interface, therefore its ID participates in client ids of the inner controls (with IDMode="Legacy"). There are two user controls with auto-generated IDs, i.e. different ID. The Panel doesn't inherit the INamingContainer. But, the main fact is the one that the Panel is a single comparing with two user controls previously. So, no way to get unique client ids.

So, you have to set up some unique IDs in that case or don't set up at all to get them auto-generated.

That explains.



<%@ Page Language="C#" %>
<%@ Register Assembly="Ext.Net" Namespace="Ext.Net" TagPrefix="ext" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
</head>
<body id="theBody">
<ext:ResourceManager ID="theManager" runat="server"
IDMode="Legacy"
Theme="Default"
ShowWarningOnAjaxFailure="false"
ScriptMode="Debug" />
<ext:Panel runat="server" ID="p" Width="300" Height="400" Layout="AccordionLayout" />
</body>
</html>
<script runat="server">

protected override void OnInit(EventArgs e)
{
// Option1: play with .Items (render ID conflict)

// p.Items.Add(new Panel() { ID = "pTest", Title = "A" });
// p.Items.Add(new Panel() { ID = "pTest", Title = "B" }); // will render ID conflict


// Option2: play with .ContentControls

//Control c1 = new UserControl();
//c1.Controls.Add(new Panel() { ID = "pTest", Title = "A" });
//Control c2 = new UserControl();
//c2.Controls.Add(new Panel() { ID = "pTest", Title = "B" });
//p.ContentControls.Add(c1);
//p.ContentControls.Add(c2);
}


</script>


With the code above (two options), Option 1 makes ID of "pTest" as-is while Option2 put a prefix for pTest hence make client-side Ext ID unique (asp.net ClientID).

Option1


<script type="text/javascript">
//<![CDATA[
Ext.net.ResourceMgr.init({id:"theManager",isMVC:true});Ext.onReady(function(){Ext.net.Direc tEvent.showFailure = Ext.emptyFn;Ext.create("Ext.panel.Panel",{id:"p",height:400,renderTo:"App.p_Container",width:300,items:[{id:"pTest",title:"A"}],layout:"accordion"});});
//]]>
</script>


Option2


<script type="text/javascript">
//<![CDATA[
Ext.net.ResourceMgr.init({id:"theManager",isMVC:true});Ext.onReady(function(){Ext.net.Direc tEvent.showFailure = Ext.emptyFn;Ext.create("Ext.panel.Panel",{id:"p",height:400,renderTo:"App.p_Container",width:300,items:[{id:"ctl03_pTest",title:"A"},{id:"ctl04_pTest",title:"B"}],layout:"accordion"});});
//]]>
</script>




My initial understanding of Ext.Net.AbstractContainer.Items collection is: it should be capable of assigning DOM ID to its elements which are expected to be compatible with other WebForm controls while making HTML rendering. But it doesn't. It's OK to just prompt ID should be named uniquely exception. But I doubt the underlying meaning is far from that and it should be promoted in different ways taking consideration of more architectural issues, here are the issues:

1.Some canned Ext elements in ASCX (e.g. some windows with data source retrieval on demand) should be treated singleton which means unique ID is required to reminder designer duplicated references are not allowed. Many other buttons or interface layouts modally share those Jscript objects.
2.Some ASCXs are widgets and share the responsibility of displaying portions of the complete interface. Multiple instances is possible to be embedded.

When programmer explicitly make some factory actions, like: panel.Items.Add(new Panel()), I think it looks more likely as widget part and it should make ID automatically unique. If, say we make a Singleton tag, and make those ext elements inside it unique ID is required for designer. Isn't it better? <ext:Singleton Level = "Page" ... /> looks so cool.

Daniil
Jul 23, 2012, 9:04 AM
When programmer explicitly make some factory actions, like: panel.Items.Add(new Panel()), I think it looks more likely as widget part and it should make ID automatically unique.

Yes, there will be unique ids (generated by Ext.NET) unless you have set up the same ids.


p.Items.Add(new Panel() { ID = "pTest", Title = "A" });
p.Items.Add(new Panel() { ID = "pTest", Title = "B" }); // will render ID conflict

When you compare Panels with user controls you don't set up the same IDs for these user controls, so, there are the different ids.


p.Items.Add(new Panel() { ID = "pTest", Title = "A" });
p.Items.Add(new Panel() { ID = "pTest", Title = "B" }); // will render ID conflict

Please clarify do you have any suggestions how we could manage client ids in that case where a developer puts two widgets with the same IDs into the same container?


My initial understanding of Ext.Net.AbstractContainer.Items collection is: it should be capable of assigning DOM ID to its elements which are expected to be compatible with other WebForm controls while making HTML rendering.

Not just DOM ids. There are also respective JavaScript classes instances for Ext.NET controls which also must have unique ids.
http://docs.sencha.com/ext-js/4-1/#!/api/Ext.AbstractComponent-cfg-id (http://docs.sencha.com/ext-js/4-1/#%21/api/Ext.AbstractComponent-cfg-id)

Also the itemId option can be helpful, please look at:
http://docs.sencha.com/ext-js/4-1/#!/api/Ext.AbstractComponent-cfg-itemId (http://docs.sencha.com/ext-js/4-1/#%21/api/Ext.AbstractComponent-cfg-itemId)

cleve
Jul 23, 2012, 3:54 PM
Please clarify do you have any suggestions how we could manage client ids in that case where a developer puts two widgets with the same IDs into the same container?


You have perfectly answered my question. Panel doesn't implement INamingContainer.
For your question, I can give you a quick example.

Control1.ascx


<%@ Control Language="C#" %>
<ext:Panel runat="server" ID="p1" Title="A1" Hidden="true" />
<ext:Panel runat="server" ID="pTest" Title="X1" Hidden="true" />
<script runat="server">

public Panel MainPanel { get { return pTest; } }


</script>


Control2.ascx


<%@ Control Language="C#" %>
<ext:Panel runat="server" ID="p1" Title="A1" Hidden="true" />
<ext:Panel runat="server" ID="pTest" Title="X2" Hidden="true" />
<script runat="server">

public Panel MainPanel { get { return pTest; } }


</script>


Index.aspx (have one instance of Control1.ascx and Control2.ascx each)


<%@ Page Language="C#" %>
<%@ Register Assembly="Ext.Net" Namespace="Ext.Net" TagPrefix="ext" %>
<%@ Register src="Control1.ascx" tagname="Control1" tagprefix="uc1" %>
<%@ Register src="Control2.ascx" tagname="Control2" tagprefix="uc2" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
</head>
<body id="theBody">
<form id="form1" runat="server">
<uc1:Control1 ID="u1" runat="server" />
<uc2:Control2 ID="u2" runat="server" />
<ext:ResourceManager ID="theManager" runat="server"
IDMode="Legacy"
Theme="Default"
ShowWarningOnAjaxFailure="false"
ScriptMode="Debug" />
<ext:Panel runat="server" Width="300" Height="400" Layout="VBoxLayout">
<Items>
<ext:Container runat="server" ID="cL" Layout="FitLayout" Height="200" />
<ext:Container runat="server" ID="cR" Layout="FitLayout" Height="200" />
</Items>
</ext:Panel>
</form>
</body>
</html>
<script runat="server">

protected override void OnInit(EventArgs e)
{
cL.Items.Add(u1.MainPanel);
u1.MainPanel.Hidden = false;
cR.Items.Add(u2.MainPanel);
u2.MainPanel.Hidden = false;
}


</script>


Oops, same ID conflict.

1. Nothing to blame for developer to name "pTest" in Control1.ascx, neither to Control2.ascx
2. Nothing to blame for developer to take fragments from .ascx to make second assemble while OnInit
3. You can't use Control1.ascx as a whole to fit into placeholder of ContentControls because each container just need one panel in it

It can be well explained that Items.Add won't make its added elements a ID check to keep unique.
Solutions are like:
1. Don't take panel from ascx at server-side and make assembling action at client-side, using container.add
2. Not promoted for ascx be hosting multiple panels

Daniil
Jul 23, 2012, 9:41 PM
1. Nothing to blame for developer to name "pTest" in Control1.ascx, neither to Control2.ascx

That's right.



2. Nothing to blame for developer to take fragments from .ascx to make second assemble while OnInit

I would agree, but, I think, you should remove the control from a user control. A general ASP.NET rule - a control within an INamingContainer or a Page must have a unique ID. In your case where will be two controls with the same "pTest" ID within the Page.



3. You can't use Control1.ascx as a whole to fit into placeholder of ContentControls because each container just need one panel in it

Solutions are like:
2. Not promoted for ascx be hosting multiple panels

Well, we would suggest to move that panel into a separate user control. Then just use that user control putting it into the Containers.

You could use that user control within your existing user control loading it via UserControlLoader.



It can be well explained that Items.Add won't make its added elements a ID check to keep unique.

Well, we can't change IDs, it's out of our competence. But, actually, we check it and thrown exceptions you are facing is the result of that check:)


1. Don't take panel from ascx at server-side and make assembling action at client-side, using container.add

Well, yes, a possible solution. But, again, you will need to provide unique ids.

Though there is another possible solution. There is a few changes you could apply to get your current scenario working.

1. Set up

ItemID="TestPanel"
for the both pTest Panels.

2. Set up

Namespace="cL"
for the cL Container.

3. Set up

Namespace="cR"
for the cR Container.

4. Remove the pTest Panels for the user controls and manually change its IDs.

protected override void OnInit(EventArgs e)
{
u1.MainPanel.ID = BaseControl.GenerateID();
cL.Items.Add(u1.MainPanel);
u1.MainPanel.Hidden = false;
u1.Controls.Remove(u1.MainPanel);

u2.MainPanel.ID = BaseControl.GenerateID();
cR.Items.Add(u2.MainPanel);
u2.MainPanel.Hidden = false;
u2.Controls.Remove(u2.MainPanel);
}

But, repeat myself, we would choose the option to move that pTest panel into a separate user control.

Daniil
Jul 27, 2012, 3:05 PM
Hi @cleve,

Please clarify can we mark the thread as closed?