HtmlGenericControl("a") vs. HtmlAnchor - c#

I was looking into why one of my applications was running quite slowly. The application generates and displays a grid and populates it with work tasks (rendered as table cells). Inside each task there is an <a href="... which brings up some more info about the task.
As this is all built up dynamically from the codebehind, I've used HTMLTableRows/Cells to create the rows and cells, then used the Controls properties to add HTMLAnchors. Whenever I'm setting attributes I've used HTMLAnchor.HRef, HTMLTableCell.ColSpan, etc.
I noticed that if I use the generic HTMLGenericControl and then use its Attributes collection, e.g.
HTMLGenericControl a = new HTMLGenericControl("a");
a.Attributes["href"] = task.getLink();
it runs significantly quicker than what I would have thought is the preferred way of doing the same thing:
HtmlAnchor a = new HTMLAnchor;
a.HRef = task.getLink();
Does anyone have any explanation for where this apparent extra 'overhead' comes from?
EDIT
In case anyone is confused by my explanation, I posted another question for the same project, which has a screenshot.

#subkamran I had the same thought, but after looking it up, actually both HTMLAnchor and HTMLGenericControl are sub-classed from the same parent: HTMLContainerControl. One significant difference is that HTMLAnchor implements the IPostBackEventHandler to handle the "ServerClick" event. This surely adds something to the slowness ...

Related

Cannot add more than one row to a table dynamically using ASP/C#

I have a web app with HTML tables containing input boxes everywhere in it. I want to be able to add rows to these tables from the C# side of things. To accomplish this, I use an asp button that calls this method:
private void AddRow()
{
HtmlTableRow tRow = new HtmlTableRow();
HtmlTableCell cell = new HtmlTableCell();
HtmlTableCell cell2 = new HtmlTableCell();
HtmlTableCell cell3 = new HtmlTableCell();
HtmlTableCell cell4 = new HtmlTableCell();
cell.InnerHtml = "<input type=\"text\" size=\"29\">";
tRow.Cells.Add(cell);
cell2.InnerHtml = "<input type=\"text\" size=\"9\" class=\"units commas\" />";
cell2.Attributes.Add("class", "leftBorder textCenter");
tRow.Cells.Add(cell2);
cell3.InnerHtml = "<input type=\"text\" size=\"8\" class=\"value commas\" />";
cell3.Attributes.Add("class", "leftBorder textCenter");
tRow.Cells.Add(cell3);
cell4.InnerHtml = "<input type=\"text\" size=\"11\" readonly=\"readonly\" tabindex=\"-1\" class=\"totalA1 autoTotal\" /> ";
cell4.Attributes.Add("class", "leftBorder rightBorder textRight");
tRow.Cells.Add(cell4);
someTable.Rows.Add(tRow);
}
This works beautifully...for exactly one row. I can click the button, it adds a row. If I click it again, it doesn't do anything. More specifically, I suspect it's removing the currently added row, restoring the document to the 'default' state, and then adds a row (effectively doing nothing).
Assuming I'm right, I need to somehow be able to append a row to another dynamically created row, instead of just replace it. If I'm not right, I just need a means to be able to continually add rows on a button press.
How would I go about doing this?
EDIT: I should specify, all this could be done in a loop, all at once. I was hoping to get it to work on a button press just for the sake of testing, but it can all be neatly tucked into a loop of some kind. I've had (some) success dropping it in one.
The absolute best way to do what you need is client side, NOT server side. For what you're doing, it makes no sense to post back to the server EACH TIME you insert a new row (in my opinion). Is there a specific reason you must do this server side (C#)?
Use Javascript (jQuery) to dynamically insert new rows into your table upon the button click. The page won't refresh, it's faster, and makes for a LOT better user experience.
I assume you're familiar with Javascript, but you can tie a client side event to your server side button - just add onclientclick="insertRow();" where onclientclick is an attribute of the button control and insertRow() is a Javascript method defined in your page between <script></script> tags.
If you need me to write out an example on how to do this, please let me know and I can edit my answer.
The sentiments expressed by the other answers is completely correct in that this architecture is very flawed (it does things the way they were done in the mid 2000's, a lifetime ago in web). That being said, you have constraints and you can't change them. Here are two things I would check, one of which was already suggested in a comment:
Postback is interfering with table initialization. When a visitor first loads your aspx page, the browser will perform an HTTP GET request to your page. The IsPostback property of your aspx page class will be false. When the user clicks your button, they will make a POST request to your aspx page, passing along a bunch of variables for the current state of the page if they have modified it on their browser as well as a .NET-specific set of properties indicating what was pressed and what event handler the server should execute (in your case, the event handler calling AddRow will be called, but only after Page_Load is executed first). This is why they suggested wrapping your Page_Load logic in an if(!IsPostback){}.
ViewState is not enabled for the HtmlTable control. The ViewState is the .NET-specific implementation of serializing all that the server knows about the HTML (input boxes, etc.) into a hidden field in your output HTML. If you want .NET to remember what the state of various HTML tags are (what they contain, what the user filled out in each, etc.), then they need to have an entry in the ViewState, which is passed from postback to postback. Be warned, though, as soon as someone refreshes a page without a button click or using back and forward buttons on the browser (because remember they correspond to HTTP GET requests, not HTTP POST requests) the viewstate will be reinitialized anyways. The only way around this is stuffing stuff into the Session. If you're ok with that, then to enable the viewstate, use the .EnableViewState() method on the HtmlTable control, although keep in mind this will increase your web app's page size since .NET will serialize what the table contains into a string and put it in a hidden input variable. This will make it "easy" to write all the server side logic, but at a huge cost if the table becomes very big.
It may seem right now that the path of least resistance is to just make the existing infrastructure work, but believe me you are incurring ever increasing amounts of technical debt that either you or someone else will have to pay up down the line. I would highly recommend moving to a client-side add row method, it's the only way around this postback-viewstate-model madness in webforms.

Interaction of Web User Controls and Structure (Having conceptual difficulties)

In my asp .net C# project I have a page defualt.aspx on which I have placed 2 components.
So in all I have:
1) default.aspx (main page, not doing much code in it)
2) wuc_Lookup.ascx (doing a lot here, grabbing data, setting session, etc)
3) wuc_PageMessages.ascx (has a couple of panels and labels for message output
)
The intent is to use 3) in any page in my application. 1) and 2) are already working. My issue is that the Page_Load sequence is:
1st default Loads
2nd wuc_pageMessage loads
3rd wuc_lookup loads
The problem with this is that The wuc_pageMessage is relevant only after wuc_lookup runs.
My intent was not to put code in Page_Load for the message wuc_pageMessage control because I wanted to be able to call a method to post the message during the component load of wuc_lookup. I do this because only after wuc_lookup do I set the session which I use for the message value.
I actually got values showing up if I put the code in wuc_lookup to manipulate the code-in-front server control (panels and labels) using this.parent.findControl syntax...
But then when I try to rip that code and put it into the code-behind for wuc_pageMessage, and then call the method from the wuc_lookup it has fallen out of scope or context...
So I tried to change this.Parent by passing httpContext.current.handler as casting it as page...that didn't work...then I tried passing Object sender from the calling component...that didn't work either. Neither of them had the Parent property and or it was null which led me to believe that once the wuc_PageMessages.ascx loaded it was a dead deal until a repost happens and that is ugly and something I don't want to do.
I am having some implementation issues and I am not sure what to do. I have been stunk on this for eight hours and Is there just something I am not seeing?
I want to keep away from spagetti code. I don't want to have to scatter code-behind in 3 different files. Theoretically I should only need 2 of these to talk to eachother. i don't want code-behind in default..it's basically just a container. I want to trigger the wuc_pageMessage from wuc_Lookup.ascx without having it be "in" wuc_Lookup.ascx (peer web user controls) I want that to always be a peer relationship. Any advice would be great ...thanks...
Try moving the wuc_PageMessages logic from the page_load to the page_prerender event.
If you are going to use the preRender you would do it on the default.aspx preRender because this event fires after the wuc_lookup. Prerender will not fire on the components for some reason. So yes, this only solves part of the issue. I am not sure how you would get the alreeady loaded component of wuc_PageMessages to get back into scope. If you try to reference components on a component that has already loaded, you will get a null, like they are not there or not in scope anymore... anyone have any ideas?

How do I assign an instance of the Image class to an Image control programatically?

Another noob question from me... Apologies!
My initial code would be as follows (this is simplified):
Image pic = new Image();
pic.ImageUrl = "~/Images/photo.jpg";
pic.BorderColor = "Black";
How can I assign the 'pic' Image object to an Image Control already on my ASP.NET page?
The following doesn't work but illustrates what I'm trying to do:
MyImageControl = pic;
I'm sure there must be an easier solution than:
MyImageControl.ImageUrl = pic.ImageUrl;
MyImageControl.BorderColor = pic.BorderColor;
If you want to dynamically put controls on the page you need to do just that. Have a container then add them to the container. If you have some sort of list or array that you are storing the controls in, you just need to iterate through the collection, setting any properties you need and call container.controls.add(control); You will have to do this every post-back as their state will not be kept.
Using an asp:Panel as your container where you want the controls to show up is the easiest way to style and position the controls.
SOLUTION (moved from original post) :
I have come up with something which works for me but would still be interested if there is a way to do what I've asked above - My solution is as follows.... Rather than having a blank Image Control in my .aspx page, I changed it for a PlaceHolder instead. Then, in the C# code, I can use the following to include my Image on the page:
MyPlaceHolder.Controls.Add(pic);

Cannot change SkinID property dynamically

I get this error when I try to have my C# class change the skin of an asp control:
The 'SkinId' property can only be set in or before the Page_PreInit
event for static controls. For dynamic controls, set the property before
adding it to the Controls collection.
My goal is to provide a panel, call it ID="response", on every page, and then dynamically change it's CSS class from Error to Success, or Success to Error (so it's red or green). And also I make it visible = true, when a response is created.
Apparently, I am forced to use CssClass attribute, which is the only way this will work.
As a side-off-topic note:
In PHP, you would not have a problem of using different "pre-init" "post-init" etc. A completely unnecessary process. You would simply change the html before you send it back to the user. I'm a bit confused why ASP.NET decides to overcomplicate everything. It's a bit silly for me to take time to learn all these different complicated processes to simply display a webpage. It takes time to learn all the quirks written in difficult-to-read ASP life-cycle documents on microsoft. Not to insult any microsoft people, but it's just not practical.
If it is a static control, that is you are defining the Panel in your .aspx page, then the only place to change the SkinId is in the PreInit method e.g.:
protected override void OnPreInit(EventArgs e)
{
base.OnPreInit(e);
String panelSkin = ViewState("panelSkin").toString();
panel1.SkinId = panelSkin;
}
Of Course, the PreInit method is only called when the Page is being first Initialized -- not on a PostBack.
You could save the skinId you wanted to use to the ViewState and then call a Response.Redirect("myPage.aspx")... and as seen above grab the skinId string from the ViewState and set the Panel skinId accordingly.
Alternatively, rather than using a Panel try using an UpdatePanel from the .Net Ajax library. Clicking a button in the UpdatePanel (provided it's setup to Trigger an ASyncPostBack) will run the OnPreInit method.
That said, provided you are changing the background, going with the CssClass property would be the most efficient way to do this.
ASP, and its child ASP.NET, is basically a huge hack of vanilla HTML and the IIS page renderer. It hooks into various stages of the lifecycle that already existed in IIS, rather than having its own lifecycle like PHP. As such, there are things you can do in certain areas because the things it depends on either aren't set in stone so you can change them, or are so you can work with them. The great power of ASP.NET, which is the interop with .NET classes and the .NET Framework, IMO makes up for some of its idiosyncracies.
Anyway, Skins are part of Themes, which are loaded early in the process so the controls can be initialized with their proper default Styles. That's the key; the Theme is locked after PreInit, but the Styles (and CssClasses) behind the Skins are editable right up to and including PreRender, which includes event handlers (which fire validation). So, set the Style or the CssClass dynamically.
To do it without a full postback, you can put the controls that should change color in an AJAX UpdatePanel, which can be re-rendered separately from the other elements of the page and will keep its current contents until the DOM is modified via the JavaScript client-side.
Setting the CssClass attribute is much closer to what you'd do with PHP, so why not just do that?
The two real benefits of Skin files are setting defaults for all controls (no skinId at all) or setting properties that can't be controlled with css.

Building Dynamic UI with C#?

I need to expose some input fields based on what properties I find for particular types in an assembly.
I'm not sure how common an approach like that is. Perhaps there are easier ways. Maybe on the client side instead of server.
If anyone knows of a good way of doing something like this, I would appreciate the help.
Create input controls accordingly and simple add control to some div container? I'm not sure if it would be more complex than that.
I'll need to somehow add css classes to the controls as I build them so they get placed nicely; that might get tricky.
This all sounds like standard asp.net development. Any good tutorial should be able to help you. For the asp server controls, you use the CssClass property to set the class for the control.
Here is the asp.net tutorial from the W3C Schools.
I assume you will use reflection to figure out what properties entity has, then you would based on the type of the property create an input field. You would have to dynamically create control to handle input in code behind. Make sure you give that control and id. You will have to recreate these controls on the post back. This looks to me like dynamic property editor. There might be some free ones, google for it.
If the UI doesn't have to be completely dynamic you could include all the controls in the markup with any optional ones set to Visible="false". Then, selectively enable the appropriate controls in your code-behind. For example:
Default.aspx
<asp:Button ID="EvenButton" runat="server" Text="Even" Visible="false" />
<asp:Button ID="OddButton" runat="server" Text="Odd" Visible="false" />
Default.aspx.cs
protected void Page_Load(object sender, EventArgs e)
{
String msg = "A message to count";
if (msg.Length % 2 == 0)
{
// Enable the Even Button
EvenButton.Visible = true;
}
else
{
OddButton.Visible = true;
}
}
The advantage of this method is that you can lay things out with the appropriate CSS easily in the markup. If, on the other hand, your UI is much more dynamic than this, you'll probably have to resort to dynamically creating controls in the code-behind and adding them to the page via calls to Controls.Add(). This way, however, is harder to layout. And you have to deal with things like re-wiring any event handlers on each postback.
Hope that helps.
I ended up leveraging jQuery.
I laid out a simple markup with the basic layout I would need.
For creating controls dynamically, I did it all in javascript using jQuery methods.
This of course requires that you return some data set to the UI intelligently enough to render it.

Categories

Resources