ASP.NET Custom Control in VS - How to make VS format correctly - asp.net

This is probably a pretty simple answer, but I haven't written a lot of controls, and I can't really think of the right words to Google it properly:
I have a custom control that I built, and when I create an instance in the HTML editor in VS, I type the following:
<cc1:MyControlName id="id1" runat="server">
When I type that closing angle bracket, VS reformats it to
<cc1:MyControlName id="id1" runat="server" />
the way it does with Buttons and other tags that are typically self-closing.
My control has inner content that I want to use, so I have to change the ending, and manually add the closing tag. I'd like it to behave like TextBox, where upon typing the closing bracket, it would add the and leave the cursor in the inside.
I'm assuming this is done via an attribute or something else defined in the class, but I can't seem to find what it is. Any ideas?
Obviously this isn't all that important, since it's just a few extra keystrokes, but I'd just like to make it as convenient to use as possible.
Thanks

Using the ParseChildrenAttribute class, declare the ParseChildren attribute for your control class. This will specify that the inner content should be read into a specific property (Name in the example). The PersistenceMode attribute specifies how to serialize the inner content.
[ParseChildren(true, "Name"),
DefaultProperty("Name")]
public class Foo
{
//...
[PersistenceMode(PersistenceMode.InnerDefaultProperty)]
public string Name
{
get ; set;
}
}
There's also a broader explanation here.

Related

How can I add properties/attributes to an HTML markup container tag in ASP.Net

I've seen all of the usual pages with information about how to create a sub-tag that allows content within a user control (using ITemplate and INamingContainer) but I've yet to see anyone able to add properties that become attributes to said tags, for example:
<asp:MyControl runat="server" ID="myControlTest" SomeAttribute="SomeValue">
<Content ContentAttribute="Something">
Blah
</Content>
</asp:MyControl>
If you see the ContentAttribute on the Content tag, that is what I'd like to be able to achieve, but if I set it all up using ITemplate and INamingContainer etc, I can add a property that does in fact appear in Intellisense for that tag but when I run the code, it says Content does not have property/attribute named ContentAttribute (it also gives the same as a warning in VS IDE but still allows me to compile it).
I have tried everything to make this work and so far the only way seems to be if I make the Content property on MyControl a class that inherits from System.Web.UI.Control and implements ITemplate. That works but unfortunately I have to specify the runat attribute on the Content tag (because it sees it as a control rather than a sub-tag) and I'd rather not do that if possible.
Hope I have explained this well enough, if I haven't please let me know and I'll do my best to elaborate further.
Thanks in advance.
I think what you're proposing is something like a MIME email where there are a variable number of sections, each with an identifier for the client to choose the best version of the email it can handle. I assume you're wanting to select the appropriate template at runtime, based on that attribute.
The standard .NET controls don't implement that way, so far as I can tell. Think of the Repeater which has:
<asp:Repeater id="myRepeater" runat="server">
<HeaderTemplate>...</HeaderTemplate>
<ItemTemplate>...</ItemTemplate>
<FooterTemplate>...</FooterTemplate>
</asp:Repeater>
Each of the subitems (templates) has a different name, not the same name with a separate attribute.
Is there any way for you to define, ahead of time, what all of the possible sections might be, the way the repeater does?
<asp:MyControl runat="server" ID="myCtlTest">
<SomethingTemplate>Blah</SomethingTemplate>
<OtherTemplate>Blah</OtherTemplate>
</asp:MyControl>
I'm guessing not but wanted to throw it out there in case.
Alternately, could the ContentAttribute move to MyControl? The SETter would then load/build the template for you depending on the value.
<asp:MyControl runat="server" ID="myCtlTest" ContentAttribute="Something">
<Template></Template>
</asp:MyControl>
...or it could be loaded with a method instead of using the property SETter.
If you will always need multiple templates, perhaps a combination of those two concepts would help.
<asp:MyControl runat="server" ID="myControlTest"
SomethingTemplate="Something"
OtherTemplate="Other">
<SomethingTemplate></SomethingTemplate>
<OtherTemplate></OtherTemplate>
</asp:MyControl>

Formatting strings in ASP.NET Razor

I am currently writing a small templating system in ASP.NET to allow users to add content. For example, the user can enter the string (variable type is string).
topHeader[x] = "They think it's all over. It is now!";
However, one change that's needed is the ability to add some basic HTML tags within this content, so the following can be done
topHeader[x] = "They think it's all over. <strong>It is now!</strong>";
or
topHeader[x] = "They think it's all over. <a title="Football News" href="URL">It is now!</a>";
If you add such things into strings now they are not formatted as HTML, but I want to somehow escape them so that they can be. Naturally I've looked on the Internet for the answer, but as Razor is fairly new there's not much out there to help me out.
Anyone have an idea of how to do this?
You need to create an IHtmlString implementation holding your HTML source.
Razor plans to have a helper method to do this for you, but, AFAIK, it doesn't yet, so I believe you'll need to create your own class that implements the interface and returns your HTML from the GetHtmlString() method.
EDIT: You can use the HtmlString class.
You can either change your topHeader dictionary to hold IHtmlStrings instead of Strings, or you can leave your code as is, but wrap it in an HtmlString in the Razor view:
<tag>#new HtmlString(topHeader[x])</tag>
Make sure to correctly escape any non-HTML special characters.
The helper method they added is called Html.Raw() and it is much cleaner.
Here is an example:
#Html.Raw("Hello <a>World</a>!")
SLaks is right, but you don't need to write your own implementation of IHtmlString, there's one built in to System.Web called HtmlString. So:
topHeader[x] = new HtmlString("They think it's all over. <a title=\"Football News\" href=\"URL\">It is now!</a>");
Should do the trick.

Easy Way to Change Style of Cents in Money Text

I have a label (literal, whatever), that is being filled with currency ($5000.00). Is there an easy way to style the cents, like make the font-size smaller.
I know I can split the two up, but I am using MVP, and am looking for an easy way than passing all these new properties to the control (4+ of these labels are present).
Open to any suggestions, like a new control instead of the label, whatever you got.
I would suggest creating a new control that simply extends to Label/Literal. You can call it MoneyLabel, though Literal would probably be easier to work with. From that point, you can override the Render method to change the HTML output, format the string as you like.
Referencing it is not that tricky, the pages would need to reference the assembly where the class is stored.
In this way, you are not introducing CSS and HTML from the codebehind, not having to create extra JavaScript from what you need, and this is extremely reusable and its into how you code .NET already
I suppose in an ideally semantic world you'd probably do something like:-
<span class="currency-symbol">£</span>
<span class="major-currency-unit">5000</span>
<span class="decimal-point">.</span>
<span class="minor-currency-unit">00</span>
and then style away to your heart's content.
I would inherit from the label class, then create a newlabel class, StyledMoneyLabel.
I haven't tested this code, but hopefully you'll get the idea.
class StyledMoneyLabel : Label
{
double money = 0.0;
Render(HtmltextWriter)
{
HtmlSpan dollars = new HtmlSpan(CSS_CLASS);
dollars.Text = money.ToString(FORMAT_STRING_FOR_GETTING_DOLLARS.
///etc for decimals and so forth
}
}
Ideally you need to format it like the following.
$5000.<span class="Cents">00</span>
How you accomplish this is a bit tricky, you can have a "Dollars" and a "Cents" property and then manipulate the string into the format.
You could use javascript and let the client do all the heavy lifting...eg, in jquery (you'd have to define the putSpansAroundDollarsAndCents() function)
<div class="currencyToSplit">$5000.00</div>
$(".currencyToSplit").each(function() { putSpansAroundDollarsAndCents($(this)) });

How can you move ASP.Net controls to different places on the Web form at runtime?

Is there an accepted way to "move" a control.
My client wants to place a certain chunk of markup (representing some visual element) in one of several different places on the page. The locations are different to the point that I can't effect the change on CSS along (by floating it or something).
I considered just putting the control in multiple spots with Visible set to "false," then displaying the one in the place they wanted for that particular page.
However, the code for this control is not trivial -- there's a couple template sections, for instance. Having to dupe this in multiple places would get unwieldy. Also, I don't want to have to work with this control strictly from the code-behind for the same reason.
So, I'd like to put it in one place on the Web form, the move it around based on where I want it. Could I put Placeholders in different spots, have the control in one spot, then remove and add it to the right spot? I suspect this would work.
Does someone have a better idea? Is there a best practice for this?
I'd recommend using a placeholder control, moving your markup into a separate user control, then loading this at runtime and adding it to the relevant placeholder.
Eg.
// Load a user control
MyControl userCtrl = (MyControl) LoadControl("~/Controls/MyControl.ascx");
// Or create an instance of your control
SubclassedControl subclassedCtrl = new SubclassedControl();
// Do stuff with controls here
userCtrl.LoadData();
subclassedCtrl.Text = "Hello World";
// Check which placeholder to add controls to
PlaceHolder placeHolder = (foo=="bar") ? placeHolder1 : placeHolder2;
// Add the controls
placeHolder.Controls.Add(userCtrl);
placeHolder.Controls.Add(subclassedCtrl);
This will avoid cluttering up your page with unnecessary markup, and loading it at runtime will also avoid unnecessary confusion later, when another developer looks at the code and can't immediately see why a control is in one place in the markup, but renders on a completely different part of the page.
An alternative (and one I've seen done many times before) is through javascript and the DOM. Render your control inside a hidden div tag. So you would render your content here:
<div id='rendercontent' style='display:none'>
.. control here ..
</div>
Then, lets say you wanted to move it all here (the span tag is inside because that's what we're going to replace):
<div id='newlocation1'><span></span></div>
You would define the following javascript:
<script language="JavaScript">
function replaceNode(newElementID, targetElementID)
{
var targetElement=document.getElementById(targetElementID);
var newElement=document.getElementById(newElementID);
targetElement.replaceChild(newElement, targetElement.firstChild);
}
</script>
And when you want to move the content to the new location, call:
<script language="JavaScript">
replaceNode('rendercontent','newlocation1');
</script>
Do Web Parts do what you want to do?
Or, you can change the parent programmatically of your controls to move them into a separate area.
You can override the Render method and place the controls wherever you want in the html.
You only need to add controls to the Controls collection that must interact on the server. The rest of your HTML can just be written to the response stream. If you override Render you can create the html anyway you see fit, placing the controls in any order.
Below is an example of how to write out your html.
protected override void Render(HtmlTextWriter writer)
{
AddAttributesToRender(writer);
writer.RenderBeginTag(TagKey);
writer.RenderBeginTag(HtmlTextWriterTag.Div);
_control.RenderControl(writer);
writer.RenderEndTag();
writer.RenderEndTag();
}
You could always put panels in the pre-defined locations and add the control to the specific panel at runtime.. Here's an example adding a label (the label could be replaced with any control).
Dim lblDisplay As Label = New Label()
lblDisplay.ID = "myLabel"
lblDisplay.Text = "Some Text"
pnlDisplay.Controls.Add(lblDisplay)
As far as...
"Also, I don't want to have to work
with this control strictly from the
code-behind for the same reason."
I think you're going to have to do most of your work in the code behind.
PS.. a good example of the whole usercontrol setup can be downloaded here..
http://www.asp.net/downloads/starter-kits/time-tracker/

How can I use a traditional HTML id attribute with an ASP.net runat='server' tag?

I am refactoring some CSS on a website. I have been working on, and noticed the absence of traditional HTML IDs in the code.
There is heavy use of CssClass='…', or sometimes just class='…', but I can't seem to find a way to say id='…' and not have it swapped out by the server.
Here is an example:
<span id='position_title' runat='server'>Manager</span>
When the response comes back from the server, I get:
<span id='$aspnet$crap$here$position_title'>Manager</span>
Any help here?
Use jQuery to select the element:
$("span[id$='position_title']")....
jQuery's flexible selectors, especially its 'begins with'/'ends with selectors' (the 'end with' selector is shown above, provide a great way around ASP.NET's dom id munge.
rp
The 'crap' placed in front of the id is related to the container(s) of the control and there is no way (as far as I know) to prevent this behavior, other than not putting it in any container.
If you need to refer to the id in script, you can use the ClientID of the control, like so:
<script type="text/javascript">
var theSpan = document.getElementById('<%= position_title.ClientID %>');
</script>
Most of the fixes suggested her are overkill for a very simple problem. Just have separate divs and spans that you target with CSS. Don't target the ASP.NET controls directly if you want to use IDs.
<span id="FooContainer">
<span runat="server" id="Foo" >
......
<span>
</span>
You can embed your CSS within the page, sprinkled with some server tags to overcome the problem. At runtime the code blocks will be replaced with the ASP.NET generated IDs.
For example:
[style type="text/css"]
#<%= AspNetId.ClientID %> {
... styles go here...
}
[/style]
[script type="text/javascript"]
document.getElementById("<%= AspNetId.ClientID %>");
[/script]
You could go a bit further and have some code files that generate CSS too, if you wanted to have your CSS contained within a separate file.
Also, I may be jumping the gun a bit here, but you could use the ASP.NET MVC stuff (not yet officially released as of this writing) which gets away from the Web Forms and gives you total control over the markup generated.
Ok, I guess the jury is out on this one.
#leddt, I already knew that the 'crap' was the containers surrounding it, but I thought maybe Microsoft would have left a backdoor to leave the ID alone. Regenerating CSS files on every use by including ClientIDs would be a horrible idea.
I'm either left with using classes everywhere, or some garbled looking IDs hardcoded in the css.
#Matt Dawdy: There are some great uses for IDs in CSS, primarily when you want to style an element that you know only appears once in either the website or a page, such as a logout button or masthead.
The best thing to do here is give it a unique class name.
You're likely going to have to remove the runat="server" from the span and then place a within the span so you can stylize the span and still have the dynamic internal content.
Not an elegant or easy solution (and it requires a recompile), but it works.
.Net will always replace your id values with some mangled (every so slightly predictable, but still don't count on it) value. Do you really NEED to have that id runat=server? If you don't put in runat=server, then it won't mangle it...
ADDED:
Like leddt said, you can reference the span (or any runat=server with an id) by using ClientID, but I don't think that works in CSS.
But I think that you have a larger problem if your CSS is using ID based selectors. You can't re-use an ID. You can't have multiple items on the same page with the same ID. .Net will complain about that.
So, with that in mind, is your job of refactoring the CSS getting to be a bit larger in scope?
I don't know of a way to stop .NET from mangling the ID, but I can think of a couple ways to work around it:
1 - Nest spans, one with runat="server", one without:
<style type="text/css">
#position_title { // Whatever
}
<span id="position_titleserver" runat="server"><span id="position_title">Manager</span></span>
2 - As Joel Coehoorn suggested, use a unique class name instead. Already using the class for something? Doesn't matter, you can use more than 1! This...
<style type="text/css">
.position_title { font-weight: bold; }
.foo { color: red; }
.bar { font-style: italic; }
</style>
<span id="thiswillbemangled" class="foo bar position_title" runat="server">Manager</span>
...will display this:
Manager
3 - Write a Javascript function to fix the IDs after the page loads
function fixIds()
{
var tagList = document.getElementsByTagName("*");
for(var i=0;i<tagList.length;i++)
{
if(tagList[i].id)
{
if(tagList[i].id.indexOf('$') > -1)
{
var tempArray = tagList[i].id.split("$");
tagList[i].id = tempArray[tempArray.length - 1];
}
}
}
}
If you're fearing classitus, try using an id on a parent or child selector that contains the element that you wish to style. This parent element should NOT have the runat server applied. Simply put, it's a good idea to plan your structural containers to not run code behind (ie. no runat), that way you can access major portions of your application/site using non-altered IDs. If it's too late to do so, add a wrapper div/span or use the class solution as mentioned.
Is there a particular reason that you want the controls to be runat="server"?
If so, I second the use of < asp : Literal > . . .
It should do the job for you as you will still be able to edit the data in code behind.
I usually make my own control that extends WebControl or HtmlGenericControl, and I override ClientID - returning the ID property instead of the generated ClientID. This will cause any transformation that .NET does to the ClientID because of naming containers to be reverted back to the original id that you specified in tag markup. This is great if you are using client side libraries like jQuery and need predictable unique ids, but tough if you rely on viewstate for anything server-side.
If you are accessing the span or whatever tag is giving you problems from the C# or VB code behind, then the runat="server" has to remain and you should use instead <span class="some_class" id="someID">. If you are not accessing the tag in the code behind, then remove the runat="server".

Resources