i have situation where in i would like to read the content of the ITemplate.
for example i have something like
<WD:PopUpPanel runat="server" ID="MyPoPUp">
<InitScript>
// javascript
document.getElementByID('urName').value = 'enter your name';
</InitScript>
<Content>
Name:<asp:TextBox runat="Server" ID="urName"/>
</Content>
</WD:PopUpPanel>
basically the contents inside the InitScript is some javascript, which i want to use in ScriptManager.RegisterScript.
so my question is how do i define InitScript???
i tried
public ITemplate InitScript;
this gives me CompiledTemplateBuilder object, how do i read the content inside InitScript ???
thanks for reading, any help would be highly appreciated...
Firstly you need to instantiate your template into a template container control.
Below is an example of doing this - a user control with InitScript property; this template is being instantiated in the control's OnPreRender method. After this you could just add this template control to the user control child control collection or, as you asked in your question, render it as a string (I use my RenderControl utility method to render control as a string):
public class MyControl : UserControl
{
[PersistenceMode(PersistenceMode.InnerProperty)]
public ITemplate InitScript { get; set; }
protected override void OnPreRender(EventArgs e)
{
if (this.InitScript != null)
{
Control templateContainer = new Control();
InitScript.InstantiateIn(templateContainer);
//Controls.Add(templateContainer);
// here is "a raw" content of your template
var s = RenderControl(templateContainer);
}
}
private string RenderControl(Control control)
{
StringBuilder sb = new StringBuilder();
using (StringWriter stringWriter = new StringWriter(sb))
{
using (HtmlTextWriter textWriter = new HtmlTextWriter(stringWriter))
{
control.RenderControl(textWriter);
}
}
return sb.ToString();
}
}
Related
I am creating a server control that will sit in a aspx page. When the user selects a menu option, html controls (selects, inputs, etc) will have be added dynamically. I can do it using a user control, but I'm not sure how to go about it in a server control.
Can anyone tell me how I can add dynamic html into the control after it's already sitting in a page?
Since you are planning to add several controls as the output of your server control, you should consider inheriting from CompositeControl, this control is designed to work with several constituent controls, minimizing the code needed to do common things like keeping state and handling constituent control events
You need to override the CreateChildControls to add child controls. At the end of this method you should use this.ChildControlsCreated = true; to specify if the child controls were created, this is necessary because the CreateChildControls is called several times during the ASP.Net page life-cycle
You need to apply the same rules that you would typically apply to any server control, for example implement the INamingContainer interface to ensure that child controls will have a unique client ID
This is a simple example:
[DefaultProperty("UserText")]
[ToolboxData(#"<{0}:UserPassword runat=server UserText="""" PasswordText="""" />")]
public class UserPassword : CompositeControl
{
public event EventHandler Submitted = delegate { };
[Bindable(true)]
[Category("Appearance")]
[Description("User text")]
[DefaultValue("")]
[Localizable(true)]
public string UserText
{
get
{
var t = this.FindControl("Username") as TextBox;
return t.Text;
}
set
{
var t = this.FindControl("Username") as TextBox;
t.Text = value;
}
}
[Bindable(true)]
[Category("Appearance")]
[Description("Password text")]
[DefaultValue("")]
[Localizable(true)]
public string PasswordText
{
get
{
var t = this.FindControl("Password") as TextBox;
return t.Text;
}
set
{
var t = this.FindControl("Password") as TextBox;
t.Text = value;
}
}
protected override void CreateChildControls()
{
var p = new Panel { Width= new Unit(200), BackColor = Color.Silver };
var ul = new Label { Text = "Username: " };
var u = new TextBox { ID = "Username" };
var pal = new Label { Text = "Password: " };
var pa = new TextBox { ID = "Password", TextMode = TextBoxMode.Password };
var l = new Literal { Text = "<br />" };
var b = new Button { Text = "Log in" };
b.Click += (x, y) => this.Submitted(x, y);
p.Controls.Add(ul);
p.Controls.Add(u);
p.Controls.Add(l);
p.Controls.Add(pal);
p.Controls.Add(pa);
p.Controls.Add(l);
p.Controls.Add(l);
p.Controls.Add(b);
this.Controls.Add(p);
this.ChildControlsCreated = true;
}
}
If I click on a button of my Silverlight app, another third-party web app must gets some data.
My experience, until now, has been to create web service functions that you can call when you need, but in this case I have to give the possibility to the customer to "handle the click event on the button". In the actual case the third-party app is ASP.Net, but, if it were possible, I would like to do something portable.
Before to start with some crazy idea that will comes in my mind, I would ask: How would you do that?
Pileggi
I Use This Class To Create And Post a Form Dynamically
public class PassData
{
public static PassData Default = new PassData();
public void Send(string strUrl, Dictionary<string, object> Parameters, string ContainerClientID = "divContainer")
{
var obj = HtmlPage.Document.GetElementById(ContainerClientID);
if (obj != null)
{
HtmlElement divContainer = obj as HtmlElement;
ClearContent((HtmlElement)divContainer);
HtmlElement form = HtmlPage.Document.CreateElement("form");
form.SetAttribute("id", "frmPostData");
form.SetAttribute("name", "frmPostData");
form.SetAttribute("target", "_blank");
form.SetAttribute("method", "POST");
form.SetAttribute("action", strUrl);
if (Parameters != null)
foreach (KeyValuePair<string, object> item in Parameters)
{
HtmlElement hidElement = HtmlPage.Document.CreateElement("input");
hidElement.SetAttribute("name", item.Key);
hidElement.SetAttribute("value", item.Value.ToString());
form.AppendChild(hidElement);
}
divContainer.AppendChild(form);
form.Invoke("submit");
ClearContent((HtmlElement)divContainer);
}
}
private void ClearContent(System.Windows.Browser.HtmlElement obj)
{
foreach (HtmlElement item in obj.Children)
{
obj.RemoveChild(item);
}
}
}
divContainer is id of a div in html
Perhaps my previous question on output caching output caching was too complex.
Let's simplify.
How can I get the final, "ready for sending" rendered HTML from a page (or control) event in ASP.Net? I assume that this will be the same content that will be used for the output cache, so could be queried to find out what is about to be placed into the cache.
Code copied from:
http://aspcode.net/Last-second-HTML-changes-in-your-ASPNET-page.aspx
protected override void Render(HtmlTextWriter writer)
{
using(System.IO.MemoryStream msOur = new System.IO.MemoryStream())
{
using(System.IO.StreamWriter swOur = new System.IO.StreamWriter(msOur))
{
HtmlTextWriter ourWriter = new HtmlTextWriter(swOur);
base.Render(ourWriter);
ourWriter.Flush();
msOur.Position = 0;
using(System.IO.StreamReader oReader = new System.IO.StreamReader(msOur))
{
string sTxt = oReader.ReadToEnd();
Response.Write(sTxt);
oReader.Close();
}
}
}
}
while the method we use in Substitution control should return strings, so how is it possible to use a donut caching in web forms on a server control which should be rendered server side?
for example Loginview control?
UPDATE
This is now a fully working example. There a few things happening here:
Use the call back of a substitution control to render the output of the usercontrol you need.
Use a custom page class that overrides the VerifyRenderingInServerForm and EnableEventValidation to load the control in order to prevent errors from being thrown when the usercontrol contains server controls that require a form tag or event validation.
Here's the markup:
<asp:Substitution runat="server" methodname="GetCustomersByCountry" />
Here's the callback
public string GetCustomersByCountry(string country)
{
CustomerCollection customers = DataContext.GetCustomersByCountry(country);
if (customers.Count > 0)
//RenderView returns the rendered HTML in the context of the callback
return ViewManager.RenderView("customers.ascx", customers);
else
return ViewManager.RenderView("nocustomersfound.ascx");
}
Here's the helper class to render the user control
public class ViewManager
{
private class PageForRenderingUserControl : Page
{
public override void VerifyRenderingInServerForm(Control control)
{ /* Do nothing */ }
public override bool EnableEventValidation
{
get { return false; }
set { /* Do nothing */}
}
}
public static string RenderView(string path, object data)
{
PageForRenderingUserControl pageHolder = new PageForUserControlRendering();
UserControl viewControl = (UserControl) pageHolder.LoadControl(path);
if (data != null)
{
Type viewControlType = viewControl.GetType();
FieldInfo field = viewControlType.GetField("Data");
if (field != null)
{
field.SetValue(viewControl, data);
}
else
{
throw new Exception("ViewFile: " + path + "has no data property");
}
}
pageHolder.Controls.Add(viewControl);
StringWriter result = new StringWriter();
HttpContext.Current.Server.Execute(pageHolder, result, false);
return result.ToString();
}
}
See these related questions:
Turn off page-level caching in a
user control
UserControl’s RenderControl is
asking for a form tag in (C#
.NET)
One thing Micah's answer left out is that the substitution function must be static, accept a HttpContext parameter, and return a string. See this msdn page for more info.
I've also extended Micah's helper class to be a little more flexible.
Markup
<asp:Substitution ID="Substitution1" MethodName="myFunction" runat="server" />
Implemenation
public static string myFunction(HttpContext httpContext){
ViewManager vm = new ViewManager();
//example using a Button control
Button b = new Button();
b.Text = "click me"; //we can set properties like this
//we can also set properties with a Dictionary Collection
Dictionary<string,object> data = new Dictionary<string,object>();
data.add("Visible",true);
String s = vm.RenderView(b,data); //don't do anything (just for example)
//we can also use this class for UserControls
UserControl myControl = vm.GetUserControl("~mypath");
data.clear();
data.add("myProp","some value");
return vm.RenderView(myControl,data); //return for Substitution control
}
Class
using System.IO;
using System.ComponentModel;
public class ViewManager
{
private PageForRenderingUserControl pageHolder;
public ViewManager()
{
pageHolder = new PageForRenderingUserControl();
}
public UserControl GetUserControl(string path)
{
return (UserControl)pageHolder.LoadControl(path);
}
public string RenderView(Control viewControl, Dictionary<string, object> data)
{
pageHolder.Controls.Clear();
//Dim viewControl As UserControl = DirectCast(pageHolder.LoadControl(Path), UserControl)
if (data != null) {
Type viewControlType = viewControl.GetType();
dynamic properties = TypeDescriptor.GetProperties(viewControl);
foreach (string x in data.Keys) {
if ((properties.Item(x) != null)) {
properties.Item(x).SetValue(viewControl, data[x]);
}
}
}
pageHolder.Controls.Add(viewControl);
StringWriter result = new StringWriter();
HttpContext.Current.Server.Execute(pageHolder, result, false);
return result.ToString();
}
private class PageForRenderingUserControl : Page
{
public override void VerifyRenderingInServerForm(Control control)
{
// Do nothing
}
public override bool EnableEventValidation {
get { return false; }
// Do nothing
set { }
}
}
}
Thanks again to Micah for the code
I'm fairly certain you can't do this - the Substitution control will only allow you to insert a string into an outputcached page.
This makes sense if you think about the whole output of a server control, which could be a <table> that'll disrupt all your carefully crafted markup and/or something that requires a load of <script> injected into the page - whereas injecting a single string is something that's relatively straightforward.
I am developing an asp.net (3.5) application and I am puzzled with the behavior of the postbacks.
Consider the following scenario: I have a web user control that is basically a form. However each form field is a web user control in itself.
In the click event of the save button I iterate through all controls in my form and I retrieve the field value and the field name that refers to the database field that I am saving the value to.
The click event triggers a postback and it is during the postback that I visit the controls and here is the funny thing: the property value for the database field has become null! Could anyone shed a light here?
Here is some basic code:
[Serializable]
public partial class UserProfileForm : CustomIntranetWebappUserControl
{
protected override void OnInit(EventArgs e)
{
//AutoEventWireup is set to false
Load += Page_Load;
CancelLinkButton.Click += CancelButtonClickEvent;
SaveLinkButton.Click += SaveButtonClickEvent;
base.OnInit(e);
}
private void SaveButtonClickEvent(object sender, EventArgs e)
{
VisitFormFields();
}
private void VisitFormFields()
{
var userProfileVisitor = new UserProfileVisitor();
foreach (var control in Controls)
{
if (control is FormFieldUserControl)
{
var formField = (FormFieldUserControl) control;
formField.Visit(userProfileVisitor);
}
}
userProfileVisitor.Save();
}
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
BindText();
}
}
private void BindText()
{
LastNameFormLine.LabelText = string.Format("{0}:", HomePage.Localize("Last Name"));
LastNameFormLine.InputValue = UserProfile.LastName;
LastNameFormLine.IsMandatoryField = true;
LastNameFormLine.IsMultilineField = false;
LastNameFormLine.ProfileField = "UserProfile.LastName";
//... the rest of this method is exactly like the 4 lines above.
}
}
[Serializable]
public abstract class FormFieldUserControl : CustomIntranetWebappUserControl
{
public string ProfileField { get; set; }
public abstract void Visit(UserProfileVisitor userProfileVisitor);
}
[Serializable]
public partial class FormLineTextBox : FormFieldUserControl
{
//... irrelevant code removed...
public override void Visit(UserProfileVisitor userProfileVisitor)
{
if (userProfileVisitor == null)
{
Log.Error("UserProfileVisitor not defined for the field: " + ProfileField);
return;
}
userProfileVisitor.Visit(this);
}
}
[Serializable]
public class UserProfileVisitor
{
public void Visit(FormLineTextBox formLine)
{
// The value of formLine.ProfileField is null!!!
Log.Debug(string.Format("Saving form field type {1} with profile field [{0}] and value {2}", formLine.ProfileField, formLine.GetType().Name, formLine.InputValue));
}
// ... removing irrelevant code...
public void Save()
{
Log.Debug("Triggering the save operation...");
}
}
Remember ASP.NET is stateless. Any properties created are destroyed after the page has been render to the browser. So you have to recreate objects on each post back or store them in View, Session, or Application State.
When you do a property you have to tell it to save the view state it does not do it automatically. Here is a sample of a view state property.
public string SomePropertyAsString
{
get
{
if (this.ViewState["SomePropertyAsString"] == null)
return string.Empty;
return (string)this.ViewState["SomePropertyAsString"];
}
set { this.ViewState["SomePropertyAsString"] = value; }
}
public MyCustomType ObjectProperty
{
get
{
if (this.ViewState["ObjectProperty"] == null)
return null;
return (MyCustomType)this.ViewState["ObjectProperty"];
}
set { this.ViewState["ObjectProperty"] = value; }
}
First guess would be that BindText() shouldn't be in Page_Load, but in Page_Init, so the control state will be saved.
#David Basarab, this is not true afaik, and was only the case in .Net 1.1, in .Net2 and up this is all handled by the framework if you do all the magic stuff in the Init.
Your problem is that 'ProfileField' isn't available on the Postback, right?
The solution is to store the value for that in ViewState (instead of an auto-implemented property). Without that, it won't be available on the postback.