In a custom server control, I am simply writing out the number of child controls.
Is there a reason that the count would go to zero if <% %> tags are used within the body of the control tags?
Here's my extremely simplified control:
public class Script : Control
{
public override void RenderControl(HtmlTextWriter writer)
{
writer.Write(this.Controls.Count.ToString());
}
}
When passed only Literal data, the count written is 1 as expected:
<my:Script runat="server" ID="script3" >
function foo(){}
</my:Script>
When passed Literal data and some computed data, the count goes to zero:
<my:Script ID="Script1" runat="server" >
function foot(){}
<%=String.Empty %>
</my:Script>
There's nothing special about the String.Empty either. Anything I put here results in a count of zero.
Interestingly enough, other Control tags work fine however. The following counts 3:
<my:Script runat="server" ID="Script2">
hi
<asp:HyperLink runat="server" NavigateUrl="/" Text="hi" />
</my:Script>
Is there another way to get the child "content" of the custom control? I would think there is some way, as does it, but I can only inspect the metadata for System.Web.UI.WebControls.Content - not the implementation.
It's not possible to modify the Controls collection if your control has <%%> tags in the body (if you try to Add something, then you get an exception explaining just that). And for the same reason, the Controls collection is in fact empty. You can check if the collections is empty because of <%%> tags using the Controls.IsReadOnly property.
Turns out the answer was much more simple than the approach I was taking in the first place. Simply call the overridden RenderControl method with your own HtmlTextWriter and then use the captured markup however you want.
var htmlString = new StringBuilder();
var mywriter = new HtmlTextWriter(new StringWriter(htmlString));
base.RenderControl(mywriter);
Now the rendered markup is available in htmlString, regardless of <% %> tags used in the control's body.
Related
When i need to set some value to a Javacript, or any other part of the code, i usually use this:
<script>
<%# SomeFunction() %>
</script>
And this also works for HTML in the document body, like...
<somehtmltag property="<%# SomeFunction2() %>">
And in the code behind i create the function that returns a string, with all the necessary code.
If i add some parameter to a user control like:
<ts:PeopleCard ID="us" runat="server" Visible="<%# IsVisivle() %>" />
It also works, but i try to create the entire user control it does not work.
<%# AddUserControl() %>
Function AddUserControl() as String
Return "<ts:PeopleCard ID=""us"" runat=""server"" Visible=""true"" />"
End Function
I understand that this does not work, because this code must be processed by the server to be converted in the actual code.
The final HTML, it shows:
<ts:PeopleCard ID="us" runat="server" Visible="true" />
when it shouldn't, it should show the processed HTML\css by the server.
So my question is, is it possible to create a control this way? Is it possible to force ASP.NET to "re-process" the page, after I changed its contents in code behind?
I understand there's several other ways to do it. Including, creating the user control in conde behind.
But i need to know, if is possible to do this way...
Usually you have a parent tag that is runat server and you can then add your own object to it.
Dim newTag as New PeopleCard
newTag.Visible = true
pnl.Controls.Add(newTag)
An other option I've done is the past is to add a RenderMethod to a control. Each control have a SetRenderMethodDelegate function and it allows you to write directly to the HtmlTextWriter. This won't create an object for the controls you create yourself.
I've used code blocks before, but I'm having an issue in this instance.
I have a user control with a small number of properties. I don't want to set the properties in the parent control so I've resorted to doing it like this:
<uc2:ContactCard ID="ContactCard" runat="server"
Address="<% =CoAddressStack %>"
Email="<% =ContactEmail %>" />
The problem is that the properties are not being set with the value, instead they are set with the exact text inside the quotes.
The properties themselves come from an inherited base control.
I've been scratching my head for some time and seemingly tried every permutation. It could be I'm doing something wrong elsewhere - any advice?
The tag you're looking for is <%= ... %> not <% = ... %>
There is not a space before the =
Try writing this
Address="<%= 5 + 5 %>"
if it display 10, then its your property, in that case you can call a function like this
Address="<%= getValue() %>"
and in your codebehind something like
public string getValue()
{
return "hello world";
}
also try deleting the quotes: Address=<%= CoAddressStack %>
After further investigation, it seems it's not possible to embed a code block into a controls property directly without resorting to other measures. This for me makes the idea redundant as it's just as easy to set the controls properties from the code behind as it is to perform the extra measures.
Is it possible to add a div tag that can wrap a control from the behind? I want to send a list of controls to my function and have it dynamically add a div around each one, so that each control is within its own div.
Example:
html:
...
<p> some text</p>
Name: <asp:textbox id="txtName" runat="server" />
...
function should work to make the markup look like this-
...
<p> some text</p>
Name: <div id="divName" runat="server"><asp:textbox id="txtName" runat="server" /></div>
...
Yes, and there is more than one way to do this.
Recommended solution: create a custom control
You can easily create a custom control that extends System.Web.UI.WebControls.TextBox and overrides the Render method to write the markup for you. This gives you ultimate flexibility in how a control is rendered. This example uses base.Render() to render the TextBox itself, but wraps it in a DIV by writing directly to the HtmlTextWriter. The major benefits to this are that your custom control will be available in the code behind just like any other server control and that you can declare it in your .aspx/.ascx files.
To wit:
public class WrappedTextBox : System.Web.UI.WebControls.TextBox
{
protected override void Render(HtmlTextWriter writer)
{
writer.Write("<div id=\"div{0}\" runat=\"server\">", this.ID.Replace("txt", String.Empty));
base.Render(writer);
writer.WriteEndTag("div");
}
}
To use the control in an ASPX page, first Register the control's namespace:
<%# Register Assembly="WebApplication1" TagPrefix="cc" Namespace="WebApplication1" %>
Then add the control to your form like so:
<cc:WrappedTextBox ID="txtName" runat="server" Text="Wrapped!" />
cc is just an arbitrary prefix that I chose, but you can make up your own! (cc for "custom control")
Note that the runat=\"server\" is probably unnecessary in the div output (though you could be writing code that generates ASPX content, I suppose). The runat="server" just tells ASP.NET that you want it to be a server control, but in this case your WrappedTextBox is the server control. Also note that this isn't necessarily a best-practices example.. I wouldn't replace the txt normally, but rather would use a custom attribute for the div ID. (But this example will give the requested output.)
Alternate solution: Depth first search and modification of the Control Tree
protected void Page_Load(object sender, EventArgs e)
{
DeepControlSearch(Page);
}
private void DeepControlSearch(Control control)
{
if (control.HasControls())
{
for (int c = control.Controls.Count - 1; c > -1; c--)
{
DeepControlSearch(control.Controls[c]);
}
}
if (control is TextBox)
{
WrapTextBox((TextBox)control);
}
}
private void WrapTextBox(TextBox textBox)
{
System.Web.UI.HtmlControls.HtmlGenericControl div = new System.Web.UI.HtmlControls.HtmlGenericControl("div");
div.ID = String.Format("div{0}", textBox.ID.Replace("txt", String.Empty));
div.Attributes["runat"] = "server";
Control parent = textBox.Parent;
parent.Controls.Remove(textBox);
div.Controls.Add(textBox);
parent.Controls.Add(div);
}
The benefit to this approach is that you don't have to create custom controls (which requires a bit of up-front work, but usually is a worthwhile investment because you can reuse them in any ASP.NET project). The HtmlGenericControl can be manipulated like any other server control, but it is dynamic and so needs to be in ViewState if you want to access it on PostBack (unless you want to do it the oldskool way and loop through the posted Form elements to get values from dynamic controls).
Not sure I would recommend the second approach; this is really what custom controls are made for.
Alternate solution #2: emit JavaScript (jQuery makes it really easy) to wrap the controls
You could use ClientScript.RegisterStartupScript() to emit some JavaScript or jQuery to do this, for example:
ClientScript.RegisterStartupScript(
this.GetType(),
"wrap textboxes",
"$(document).ready(function(){$('input:text').each(function(){$(this).wrap('<div id=\"div' + this.id.replace('txt', '') + '\"></div>');});});",
true);
I have a Web Form project I am developing C# ASP.Net 4.5. I have a class that calls a response.write to display a message for user input validation purposes. The call to response.write is made inside the class in a method from creating a new instance of the class, thus the class method, by pressing a button on the form. But using the response.write causes the textboxes on my page to shrink considerably. Then when I press a different button the textboxes go back to normal. It only happens when I use response.write. Any help would be appreciated. Code call in class method:
HttpContext.Current.Response.Write("File not found");
By using that you're simply dumping text to the top of the page, typically outside of the <html> tags. This can have a knock-on effect to the rest of the pages style; i see the same when i am spitting out test responses.
Instead, put yourself a label control on your page and populate that instead. you can put it exactly where you want and simply call:
So put this: <asp:Label runat="Server" id="myLabel" /> where you want the message to appear.
Then in your code-behind, write this. It will populate the label with the given text.
myLabel.Text = "File not found";
The Label control will be rendered as a <span></span> - so styling it is nice and easy.
If you fancied using a <div> then use the Panel control.
If you're not fussed about any sort of style, go for a Literal control, which renders no html elements.
When you use the HttpContext.Current.Response.Write on code behind is direct send to the page your text, at any random point of page render.
Maybe on top, maybe on bottom, on some point that you can not control if you use the code behind to call it.
Change the way you show your message, at a minimum you can use a literal control to render there your output and show it.
You may want to use a control to display your error. For example:
In the aspx/ascx
<asp:Label id="ErrorMessage" runat="server" />
in the page/control code behind
//call TheClass
TheClass c = new TheClass();
string error = c.TheMethod();
if (!string.IsNullOrEmpty(error))
{
ErrorMessage.Text = error;
}
in TheClass
public class TheClass
{
public string TheMethod()
{
string result = "";
...
//When file is not found
result = "File not found";
...
return result;
}
}
I have the following C# code on one of my pages:
protected override void Render(HtmlTextWriter writer)
{
//an array named fieldNames is delcared here
writer.Write("<form id=\"Form1\" runat=\"server\" action=\"\">");
writer.Write("<asp:checkboxlist id=\"checkBoxes\" runat=\"server\">");
for (int count = 0; count < fieldNames.GetLength(0); count++)
{ //iterates over the array of field names
writer.Write("<asp:listitem text=" + fieldNames[count] + " value=" + fieldNames[count] + "/>");
}
writer.Write("</asp:checkboxlist>");
writer.Write("</form>");
}
The intent is to create a list of checkboxes which have had their attributes set dynamically.
When run this does not throw any errors but no controls appear on the page.
When I view the source of the page I get the following html:
<form id="Form1" runat="server" action="">
<asp:checkboxlist id="checkBoxes" runat="server">
<asp:listitem text='Spares Part No' value='Spares Part No'/>
<asp:listitem text='Description' value='Description'/>
<asp:listitem text='Site' value='Site'/>
<asp:listitem text='Rack/Bin Number' value='Rack/Bin Number'/>
</asp:checkboxlist>
</form>
Out of interest I posted this in another application and it runs fine with all the controls being displayed.
Is this a problem with the order in which events are called? I am at a bit of a loss as to what to try next so any advice would be great.
Thanks,
Oliver
Basically you can't do this.
The Render event comes very late in the page life cycle. You can't just output ASPX markup because the events that parse the markup, instantiate controls etc have already run.
What you should do is add a PlaceHolder control to your page in the markup, and then in an earlier event (e.g. Init or Load) add the controls you want to that placeholder. Again you can't just write out the ASPX markup however, you need to instantiate the controls, along the lines of:
var checkbox = new CheckboxList { Id = "checkBoxes" };
uxPlaceHolder.Controls.Add(checkbox);
checkbox.Items.Add(new ListItem { Text = "...", Value = "..." });
One way you could achieve what you want would be to use a VirtualPathProvider to generate the markup for .aspx requests as they are requested by the framework. Or you could look at what HTML output you actually want to generate (i.e. a list of input elements with some associated javascript) and render these directly. Both of these should probably be classified as nasty hacks however.
You're rendering server-side code - which the browser doesn't understand.
You have to add the CheckBoxList and its ListItems to the form before the page renders.
The server-side control renders the html for the browser - it is created, normally, by Asp.Net parsing the server-side markup.
You are directly writing html content to browser, so you should use only html tags.