..........
..........
Is there a way to get the reference to the UserControl rendered from UserControlRendererConfig? I attempted to get it from the ComponentAddedEventArgs.Control but it isn't the same. I use the actual UserControl's ClientID to bind objects.
Code:public static void AddDirectControl( this ComponentBase ctnr, string ctlPath, string ctlName, BindFn bindFn = null ) {
UserControlRendererConfig cfg = new UserControlRendererConfig {
UserControlPath = ctlPath,
UserControlId = ctlName,
UserControlClientIDMode = System.Web.UI.ClientIDMode.Static,
Mode = RenderMode.AddTo,
Element = ctnr.ClientID
};
if( bindFn!=null ) {
cfg.BeforeRender += delegate( ComponentAddedEventArgs eCmp ) {
bindFn( ((Container)eCmp.Control).ContentControls[0] );
};
}
X.DirectRendering = true;
UserControlRenderer.Render( cfg );
X.DirectRendering = false;
}
Hi @michaeld,
It will work only if:
where "config" - a UserControlRendererConfig instance.Code:if (config.ControlIdToRender.IsEmpty() && !config.SingleControl)
It is an excerpt from the UserControlRenderer's Build method (in UserControlScriptBuilder.cs).
Please note that in your case even if
an intermediary Container is created and the user control's content is rendered to that Container as Content (not as Items). Then that Container is rendered to the Element using RenderMode.AddTo.Code:Mode = RenderMode.AddTo
UserControlRenderer doesn't extract Ext.NET components from a user control, as UserControlLoader does.
Maybe, it is better to use a UserControlLoader instead of UserControlRenderer? There is the UserControlAdded event in UserControlLoader.
Also, we are considering a possibility to pass a user control into a UserControlRenderer's BeforeRender handler.
I found that if you set SingleControl on a UserControl, it ONLY renders the first item. This was actually the issue I was having that originated this thread. I figured that out and couldn't understand under any condition that might be appropriate to just render the first item in a UserControl (unless you know for sure it's just a single item). Honestly, I don't know where this class is documented well enough to understand all all the variations and how it can be properly used. I've come up with an extension above that ensures, at least so far, the user control is the only part that's rerendered without its parent as well. It handles autoloaders as well.
I'm not sure I'm following exactly how your posing a better solution. Please post a rewrite of a better implementation of the extension method that handles items. I just need one reliable way for content and one for items that only renders the user control and adds it to a parent.
Re: SingleControl
By default, UserControlRenderer creates an intermediary Container and put a user control to its ContentControls.
Let's consider an example.
Page
User ControlCode:<%@ Page Language="C#" %>
<%@ Register Assembly="Ext.Net" Namespace="Ext.Net" TagPrefix="ext" %>
<script runat="server">
protected void RenderUserControl(object sender, DirectEventArgs e)
{
UserControlRenderer.Render(new UserControlRendererConfig()
{
Element = this.Panel1.ClientID,
UserControlPath = "TestUC.ascx",
Mode = RenderMode.AddTo
});
}
</script>
<!DOCTYPE html>
<html>
<head runat="server">
<title>Ext.NET v2 Example</title>
</head>
<body>
<form runat="server">
<ext:ResourceManager runat="server" />
<ext:Button runat="server" Text="Render User Control" OnDirectClick="RenderUserControl" />
<ext:Panel
ID="Panel1"
runat="server"
Title="Panel"
Height="200"
Layout="FitLayout" />
</form>
</body>
</html>
If you run it, you will see that the Panel1's FitLayout doesn't affect the Container inside the user control. It is because there is an intermediary Container between them. So, the Panel1's FitLayout affects that intermediary Container which has no layout itself.Code:<%@ Control Language="C#" %>
<ext:Container runat="server" Html="User Control" StyleSpec="background-color: yellow;" />
A developer can set up SingleControl="true" (or ControlIdToRender) to avoid that intermediary Container. In other words, a developer can define an intermediary Container himself if there are several controls to render, not just the only one.
But an intermediary Container is required in a general case, because a user control can look like this one:
User Control
Generally speaking, the SingleControl option is quite out-of-date, becase now there is this setting.Code:<%@ Control Language="C#" %>
<script>
alert("Hello!");
</script>
<asp:Panel runat="server">
ASP.NET Panel
</asp:Panel>
<ext:Container runat="server" Html="User Control" StyleSpec="background-color: yellow;" />
With this setting the UserControlRender will extract all the Ext.NET controls (top level) from the user control and render it without an intermediary Container.Quote:
Items = true
Though, we keep SingleControl to avoid making a breaking change just removing that.
Re: UserControlRenderer vs UserControlLoader
I said that the UserControlRenderer cannot retrieve Ext.NET controls from a user control, but I just forgot about the "Items = true" option. So, it is not the benefit of UserControlLoader and you can use UserControlRenderer reliably.
Re: getting the user control in the BeforeRender handler
Code:void cfg_BeforeRender(ComponentAddedEventArgs args)
{
UserControl uc = ((args.Control as Container).ContentControls[0]) as UserControl;
}
The lack of good docs is one of our main problems. We apologize for the inconveniece. Hopefully, my explanation helps a bit.Quote:
Honestly, I don't know where this class is documented well enough to understand all all the variations and how it can be properly used.
If I could use UserControlLoader with a render and find a way to add it to a parent, that would be ideal? If this is possible, please let me know with an example. Or are you saying the better way is to set items=true on a UserControlRenderer to render just the control and it will attach to items to become part of the parent layout? Again, the goal is to only render the new control and attach it to the parent in a DirectEvent without rerendering the parent too (which clobbers the parent's other control values).
As for the lack of documentation, hopefully this detail you supply will help others too now or in the future. What vladimir did in the other thread was pretty awesome showing variations and examples.
So I'm finding this model doesn't work either if the user control being loaded has its own DirectEvents.
It is true for any dynamic ASP.NET control which has event handler and which is not recreated on next requests (for example, ASP.NET button with Click event handler)Quote:
So I'm finding this model doesn't work either if the user control being loaded has its own DirectEvents.
I do reload it on postback.
aspx
aspxCode:<%@ 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";
uc.ClientIDMode = System.Web.UI.ClientIDMode.Predictable;
Form.Controls.Add(uc);
}
}
protected void CreateTest( object sender, DirectEventArgs e ) {
UserControlRendererConfig cfg = new UserControlRendererConfig {
UserControlPath = "Test39.ascx",
UserControlId = "uc1",
UserControlClientIDMode = System.Web.UI.ClientIDMode.Predictable,
Mode = RenderMode.AddTo,
Element = RP.ClientID
};
UserControlRenderer.Render( cfg );
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>
This renders the following:Code:<%@ Control Language="C#" ClassName="Test39" %>
<script runat="server">
protected void Page_Load(object sender, EventArgs e) {
}
protected void MsgTest( object sender, DirectEventArgs e ) {
HeadL.InnerHtml = "This text was changed";
Button1.Text = "Proves button changed in directEvent";
ASPButton.Text = "Changed aspbutton";
Button1.Listeners.Render.Handler = "alert('Test');";
Button1.Render();
}
</script>
<ext:Panel ID="EventP" runat="server" Flex="1" Padding="5" Title="TestRightPanel">
<Content>
<div class="TitlePnl">
<h1 id="TitleL" runat="server" class="Title">Test Title</h1>
<div class="HeadPnl">
<span id="HeadL" runat="server" class="Head">Test Span</span>
</div>
<asp:Button ID="ASPButton" runat="server" Text="AspButton" />
</div>
</Content>
<Items>
<ext:Button ID="Button1" runat="server" Text="Test DirectEvent in Loaded Control" IDMode="Static">
<DirectEvents>
<Click OnEvent="MsgTest" />
</DirectEvents>
<Listeners>
<AfterRender Handler="" />
</Listeners>
</ext:Button>
</Items>
</ext:Panel>
The issue is that despite all IDModes set to Predictable, EventP.IDMode=Static is not rendered with the Predictable naming format. I can get this whole thing working if I set modes to Static and set EventP to Static but I shouldn't have to do that. I think there is a bug.Code:{script:"Ext.net.append(Ext.getBody(),[\"<div id=\\\"uc1_ct_Content\\\" class=\\\"x-hidden\\\"><div id=\\\"App.EventP_Container\\\"><div id=\\\"uc1_EventP_Content\\\" class=\\\"x-hidden\\\">\",\"<div class=\\\"TitlePnl\\\">\",\"<h1 id=\\\"uc1_TitleL\\\" class=\\\"Title\\\">Test Title</h1>\",\"<div class=\\\"HeadPnl\\\">\",\"<span id=\\\"uc1_HeadL\\\" class=\\\"Head\\\">Test Span</span>\",\"</div>\",\"<input type=\\\"submit\\\" name=\\\"uc1$ASPButton\\\" value=\\\"AspButton\\\" id=\\\"uc1_ASPButton\\\" />\",\"</div>\",\"</div></div>\",\"</div>\"].join(''));Ext.net.ResourceMgr.destroyCmp(\"App.uc1_ct\");Ext.create(\"Ext.panel.Panel\",{id:\"EventP\",padding:5,renderTo:\"App.EventP_Container\",flex:1,contentEl:\"uc1_EventP_Content\",items:[{id:\"Button1\",xtype:\"button\",text:\"Test DirectEvent in Loaded Control\",directEvents:{click:{fn:function(item,e){Ext.net.directRequest({control:this});}}}}],title:\"TestRightPanel\"});App.RP.add({id:\"uc1_ct\",xtype:\"container\",contentEl:\"uc1_ct_Content\"});App.ucLoaded.setValue(\"1\");"}