I thought I would ask this question to see why many examples and people prefer to use inline databinding in the aspx code vs implementing an OnDataBinding event when using WebForms.
For any databound control (eg. Repeater, GridView, etc) I always implement the OnDataBinding method for field level controls if I need to do anything that isn't built in out of the box (eg. I need to do an Eval). Most examples I see have the code right in the aspx page using the inline <%# syntax.
Example of inline ASP.NET code:
<asp:Literal ID="litExample" runat="server"
Text='<%# Eval("ExampleField").ToString() %>' />
Example of how I prefer to do it:
In the aspx:
<asp:Literal ID="litExample" runat="server"
OnDataBinding="litExample_DataBinding" />
In the codebehind .cs:
protected void litExample_DataBinding(object sender, System.EventArgs e)
{
Literal lit = (Literal)(sender);
lit.Text = string.Format("{1} - {2}",
Eval("ExampleField").ToString(),
Eval("ExampleField2").ToString());
}
I personally prefer the codebehind method because it keeps my aspx pages clean and I don't have all this inline code all over the place and the next guy just knows to always look in the .cs files for code changes. The seperation of presentation and code is also maintained better this way as the HTML is place holders only and the codebind is determining what is actually being put in control.
Now these are very basic examples. The field could be a integer that you want to format with leading 0s or a DateTime that needs a specific format etc. It could also take all sort of manipulation and code to get the finally value that should be stored in the 'Text' property at the end.
Where do you draw the line and move it to the codebehind if you are using inline code?
What are the pros and cons for doing it either way?
Does one take more overhead than the other?
Edit Note: I am not talking about assigning a value to a control that is just on the page but one that is being databound to because it exists in a repeater template or gridview item template etc... Obviously a literal sitting on a page you can just assign in code.
Edit Note: I thought I would gather more response, especially with regards to the overhead. Do most people NOT use the OnDataBinding events?
I much prefer the opposite. I prefer to keep my code-behind limited to procedural code, and keep all my declarative code in my Aspx page. In your example above, the literal is absolutely declarative and therefore (by my preference) would not belong in code-behind. Much more robust functionality generally goes in my code-behind, and I don't want my developers to be cluttered by having to sift through a bunch of initialization lines when trying to understand it.
There's little performance difference between them. A data binding expression is parsed and compiles out to something like
control.DataBinding += new EventHandler(ControlDataBinding);
and also
private void ControlDataBinding(object sender, EventArgs e) {
control.Text = Eval("Field");
}
In this case, the OnDataBinding method is not overridden. The base Control.OnDataBinding method is executed, which raises the DataBinding event, causing the above code to execute.
When you override OnDataBinding, you're simply taking over before the base code is run, and get to set the Text property yourself (for example).
I dislike giving out partial answers, but I'll do it this time because I think it's neat, and it saved me recently:
I said that the data binding expression are parsed. In fact, all of the markup is parsed, code in C#, VB.NET or whatever language is generated, and this is them compiled into a class. When the page is requested, an instance of this class is created, and it begins its life.
You can locate these generated code files on disk sorry, I don't remember where. The interesting thing about them is that they still work, as code.
For instance, I recently had some fairly complex Infragistics grids set up, had all the formatting complete, and then found that I needed to be able to set the formatting at rumtime (to get the correct format into exported Excel files). In order to do this, I opened the source file (all grids were in a single user control) and was able to extract the configuration of each grid into a separate group of methods.
I was able to clean them up with ReSharper, extract common code sequences into a base class, and was left with one static method to set up each grid. I was then able to call them both for the initial setup, and for the setup of the dummy grid used for Excel export.
I prefer it your way with OnDataBinding. You can keep your codebehind clean by using a "Databind" region for all the OnDataBinding calls, and you can keep your markup clean by getting those horrible server-side code blocks out of there.
I think most people do it the inline way because it's easier to understand and to implement.
Actually I prefer to use the aspx for controls that you would expect to Bind, like listview, gridview, repeater and other similar controls.
For the other controls, I would set them in the codebehind, but directly (as part of the process I am doing, instead of calling the literal.DataBind or DataBind for the whole page). If it is an user/custom control, that I expect the callers to do a DataBind, then I would override DataBind and set the values.
That said, I usually has plenty of code outside the codebehind, and have a call to something like ShowUser, where I put those assignments to controls (instead of setting a property, then doing a bind, and having all those evals for simple controls).
I agree with caltrop. I like my markup to be clean and all my aspx/ascx code to reside in my code-behind files (where it belongs).
I only have one thing to add. I prefer not to litter my code with OnDataBinding() events wired for each of my databound controls. Instead I do it all in the OnDataBinding() event of the User Control that is being embedded in the bindable-control (such as the repeater in your sample).
For example, in my User Control's code-behind you would find:
protected override void OnDataBinding(EventArgs e)
{
base.OnDataBinding(e);
litExample.Text = Eval("ExampleField") + " - " + Eval("ExampleField2");
}
From here you can set the properties of all your controls or call other methods to set them. Notice how, in my example, I didn't need to perform the boxing like you did here: Literal lit = (Literal)(sender);
That alone should save you on some performance (nanoseconds of course, but something worth measure). Read the section "Performance" here: http://msdn.microsoft.com/en-us/library/yz2be5wk%28v=vs.80%29.aspx
I am also at war with using strings in my code. I would have either used const string variables to define "ExampleField" and "ExampleField2" or set them up as public properties in the User Control that could then be set by the containing control/page based on the column name of the data object it will be bound against. This affords more flexibility and re-use of the control.
FYI: You don not need to call ToString() on Eval, as this method already returns a string.
Related
I'm trying to build a very specific search page for a project and I'm having lot of trouble dealing with multiple postbacks invoked by dynamically-generated controls on a single page.
The page has to work like this:
There is a single checkbox, "Detailed search", that causes a postback on checking/unchecking.
When detailed search is not active, a simple grid with contents and buttons is displayed. Nothing special.
When detailed search is active, N checkboxes must be generated from some dynamic data, that represent the sections where you want the search to happen. Below the checkboxes, an AJAX-enabled tab control will appear, initially with no tab pages.
When checking one of the section checkboxes, a postback will occur. After the postback, data will be searched in the section selected by the user, then a new tab page containing a grid view of results and the name of the section will be added to the tab control. If the checkbox is unchecked, the tab page will disappear from the control, again, after a postback.
Now, the issue is that pretty much everything has to be generated dynamically, and that pretty much everything is connected to something else.
First issue: dealing with the "Detailed search" checkbox. Sounds easy, doesn't it? My initial idea was to set Page.Viewstate["DetailedSearchEnabled"] to true or false during the check/uncheck event handler, then create controls dynamically checking the value of DetailedSearchEnabled during Page_Load.
Nope. The postback event-handling happens between Page_Load and Page_LoadComplete. It would take an additional refresh for things to work as intended.
<< Then I'll just generate the controls on Page_LoadComplete! >>
Nope. Those controls need event handling as well, and if they're generated after Page_Load they will not be wired up correctly.
A possible solution would be generating everything in advance, on Page_Load, and only hiding/showing controls on Page_LoadComplete. But that is inefficient, and one important point of this search page is that only the minimum amount of controls should be generated.
The difficulty of this task seems to come from the way event wiring and the page life cycle work.
Surely there must be a better way of approaching this problem.
First issue: dealing with the "Detailed search" check box.
The correct approach (if you want to use page post-backs) is as follows:
In the CheckChanged event handler, save the value of the Checked property to ViewState["DetailedSearchEnabled"]. If the value is true, add the dynamic check boxes to the page. If the value is false, find and remove them.
Override LoadViewState. After calling base.LoadViewState, re-create the dynamic check boxes and wire up their events if ViewState["DetailedSearchEnabled"] is true. Note that neither Page_Load nor Page_LoadComplete is the appropriate place to do this.
Yes, you should create the dynamic check boxes at two points in the page life cycle. I recommend a helper method.
In general, your event handlers should add or remove just the dynamic controls (if any) affected by those particular events, but LoadViewState should re-create all dynamic controls that existed from the previous page request. You must store enough information in view state for LoadViewState to do so.
My answer to this other question demonstrates how to add and remove dynamic controls. You may want to use it as a reference.
Sounds to me like you should be using a CheckBoxList control to handle your dynamic checkboxes. You can add an remove items to the CheckBoxList during your post back and not have to worry about dynamically adding/removing actual controls/events to the form.
Here is a link to the msdn:
https://msdn.microsoft.com/en-us/library/14atsyf5(v=vs.85).aspx
Here is some sample code:
Protected void Button1_Click (object sender, System.EventArgs e)
{
CheckBoxList.Items.Add(new ListItem("TextValue1", "Value1"));
CheckBoxList.Items.Add(new ListItem("TextValue2", "Value2"));
}
If all else fails, you could still fall back on the quick-and-dirty old-fashioned ASP way.
Use Response.Write or <%...%> to generate your dynamic controls as plain old HTML (simple form fields, e.g. <input type="checkbox" name="foo" value="1" />).
Make sure you have a form field for every piece of information you may need after the postback(s). If necessary, use hidden form fields to 're-post' values across subsequent postbacks.
After postback, retrieve the values of the controls with the Request object.
Use those values to adjust the generation of controls as you see fit.
You should be able to do all of this in Page_Load. The advantage is total freedom. The disadvantage is total freedom to make a big mess of your aspx. So you may want to migrate all this dirty code out of your aspx, and into a custom-made control, which you can then add to your aspx.
When generating your own HTML, be careful not to introduce XSS vulnerabilities. Use HtmlEncode where necessary.
As you suggested yourself, there is a better way to tackle it.
If I was in the same situation, I would create web methods for interacting with the page, and use client side to do the UI. I'm currently working mostly with angular JS, although it does come with a learning curve. You could use ng-hide/ng-show to bind to the checkbox event to display the detailed search. When the n number of checkboxes needs to be displayed, you can then just fill them in with ng-repeat, for each of the items you need to display, after a check/uncheck you can dynamically populate new controls etc. through web method calls if extra data is needed.
Pure ASP postbacks are quite clunky from my experience, and not really suited for building a maintainable dynamic UI.
Instead of making so many postbacks, it would be better to use jquery and ajax calls to load the controls as needed and then attach events to it or you can even use UpdatePnael for that. Help Links:
https://www.simple-talk.com/dotnet/asp.net/ajax-basics-with-jquery-in-asp.net/
http://encosia.com/using-jquery-to-directly-call-aspnet-ajax-page-methods/
I recently started working with some legacy ASP.NET stuff, and I've run into an interesting problem:
I've got a table displaying a few values, some of which are evaluated in C# server-side. They all work correctly. Then all of a sudden...
<td><asp:Label ID="Label2" runat="server" class='<%=SevenDayThresholdTooHighOrLow%>'><%=ChgFromSevenDaysAgoInfo%></asp:Label></td>
ChgFromSevenDaysAgoInfo is evaluated properly.
SevenDayThresholdTooHighOrLow is rendered as a string inside of the class quotations. That is
class="<%=SevenDayThresholdTooHighOrLow%>".
In the code-behind file, the two variables are declared in the same scope, and assigned values pretty much one after the other. My Visual Studio doesn't complain about not finding certain variables in code like it would if the property did not exist.
What other factors could be influencing this oddity? What could I have missed?
Thank you very much for your help, everyone!
Eli
EDIT: I took a look at what is the use of Eval() in asp.net and tried to set my tag up that way (class='<%# Eval("SevenDayThresholdTooHighOrLow")%>'), unfortunately to no effect.
That class attribute probably isn't being evaluated for server-side code, likely because class isn't a property of Label. (Label isn't an HTML element, it's a server-side control. So instead of HTML attributes it uses object properties.)
There's a CssClass property, you might try that instead. But even then the approach is different because the system may still not attempt to evaluate server-side code injected into already server-side components. Still, worth a try.
What should definitely work is setting the CssClass property in the code-behind:
Label2.CssClass = SevenDayThresholdTooHighOrLow;
If you want to keep this out of code-behind (and who could blame you?) then another approach could be to replace the server-side control with an HTML element entirely. The idea being that if properties on this control aren't otherwise being set in server-side code, then does it really need to be a server-side control? If I remember correctly, a Label emits as a span:
<span class="<%=SevenDayThresholdTooHighOrLow%>"><%=ChgFromSevenDaysAgoInfo%></span>
You can't do that in just the markup code. You can't use server tags in a server control. You could use a data binding tag, but then you would need to trigger the data binding from the code behind anyway.
Just set the attribute from the code behind:
Label2.CssClass = SevenDayThresholdTooHighOrLow;
I think the question may seem a little weird, but here's the details.
The Goal : To retrieve a set of pairs (text, value) for various reasons. one example for that of them is retrieving the alphabet, each letter will be used as an anchor or a LinkButton, the text value will be the letter and the click event will take the value part of the pair and place it in a stringFormat() to form a "Parametrized URL"
I've used two approaches for this goal but I don't know which is better!
1. Using a Repeater
A repeater that will have a LinkButton in it's ItemTemplate and through this blocks and will set the text to the 'text' and using eval and the 'value' to create the QueryString.
2. Using StringBuilder
Create an instance of StringBuilder
Use a loop with a counter equal to the total pairs to be retrieved. and append a certain string format that will build a long string with all the needed anchors for navigation using some code like this
Links_strngBuilder.Append(string.Format("<a href='/data.aspx?page={0}'>{0}</a>", chrctr))
and finally convert the String Builder instance to a string and assign it to a label
Note: the chrctr text and value fields will be retrieved as you suggest [in each loop from the database] or [loaded in an array/arrayList/List<> to store the values and save all those connections to the db]
Where i work we will never use a stringbuild becouse of the designer. We dont want the designer in the codebehind if he has to make a simple change. so keep the markup in the view and codebehind in the codebehind.
Edit
Other advantage of repeater is the change of cycle is much easier. No need to recompile and perhaps redeploy the tweak the UI, just edit the ASPX template, save and refresh.
I don't know anything about how these two approaches perform (in terms of memory usage and speed) comparing to each other but I'd definitely go for Repeater because:
The code is much easier to understand & support
Using a StringBuilder reminds me the days of the classic ASP when the Response.Write was used widely.
You can't use any of the benefits of the Visual Designer with a StringBuilder.
-- Pavel
I think building the output fits more in client side(e.g. when you do an ajax call and want to show the results in html) than server side except when your build a custom server control.
In addition, if you use the repeater you have the extensibility option whenever requirements changed, and you have more control and facilities(like event handling and styling with css and so forth).
I'm adding a dynamically built set of checkboxes to an asp.net page from the code behind with something like this in a recursive fashion:
pnlPageAccessList.Controls.Add(myCheckboxControl);
The controls show up fine on the page, but they don't show up when I view source, nor can I access them from the code behind.
If I add the controls in the on_init method, they work. But I have some business rules driving changes to the list of controls itself that require I fire the add method elsewhere.
Has anyone seen this before? I'm away from work so I can't copy the exact code.
I have two terrible ideas for how to get it working. One involves some jQuery and a set of hidden controls holding a big array of integers; the other is run the method on_init AND on my other events so the controls at least show up. Both smell like ugly hacks. The second one I suspect won't work to read the values out of the checkboxes.
On the server side the page is recreated from scratch every postback, so if you add any controls dynamically, you have to re-add them on every postback.
As you add the controls at runtime they are not known at compile time, so there is no variables declared for the controls in the Page object. If you want to access the controls you either have to keep the reference from when you create the controls, or locate them in the Controls collection where you put them.
If you can set the ID for the checkbox controls you can use the FindControl method from code behind to retrieve the control instances.
#Anero is right that you can add an ID and use FindControl.
You can also use a checkbox list, and add checkboxes to that list. Then they're already in a predefined control in your markup and code-behind.
You don't say where the method has to be fired, but once they're added dynamically, they do have to be added on every postback. You probably have a little more flexibility than just adding them at the Init event, as long as you stay aware of where things like validation occurs (if it matters in this case), or where you want to handle the checkbox contents. You can defer as late as PreRender for getting checkbox content.
Well, looks like I'm going to have to do it clientside. Thanks for the answers. I'd be able to do it On_Init, but doing it clientside with a hidden control is gonna save me a lot of overhead and be more flexible moving forward.
If anyone's curious, here's the jQuery for finding all the checked checkboxes and putting their value attribute into a hidden control in a comma delimited list:
<script type="text/javascript">
$(document).ready(function () {
$('[id*=PagesPanel]').find(':checkbox').click(function () {
$('[id*=PagesPanel]').find(':checked').each(function () {
$('[id*=lblHiddenPageArray]').append($(this).val() + ", ");
});
});
});
</script>
I have an .aspx page setup. I have a lot of placeholders that need to be replaced. First name, last name, city, state, etc, etc. How do I go about doing this in an efficient manner?
Drop a bunch of...
<asp:Label runat="server" id="Label_FirstName" Text="" />
...everywhere?
Or is there a way to use the data binding syntax in the normal page area:
<% FirstName %>
Thanks in advance!
Note that
<asp:Label runat="server" id="Label_FirstName" Text="" />
will escape your strings (for example, replace < and > with < and >), but
<%= FirstName %>
will not. If that's your intention that's fine then, but be aware that you might open a channel for XSS attacks. To escape your strings properly, you might want to have
<%= System.Web.HttpUtility.HtmlEncode(FirstName) %>
instead.
You can certainly use:
<%= FirstName %>
Where FirstName is a property of the page. It doesn't necessarily have to be a property in the class, you can access pretty much anything from that line.
Update: As DrJokepu pointed out, if the data you are displaying is coming from user input, then it opens a XSS vulnerability. As was pointed out you use HtmlEncode to avoid that, in that case a more short syntax would be:
<%= Server.HtmlEncode(FirstName) %>
And if you have a base page, you can get define a method and get away with:
<%= HtmlEncode(FirstName) %>
If you go with asp.net labels, do EnableViewState = false where appropiate, so you avoid sending/receiving unnecessary viewstate.
For formatting use ids/css classes, and have the styles in a css stylesheet. The css can be cached by the browser regardless of whether the content is dynamic.
For lists of info, you can use ListView or Repeaters and still control the specific html that will be sent over the wire.
You can definitely use ASP-style tags (<%= %>) but I would argue that your first approach is cleaner and easier to maintain. When you use the ASP-style tags you will not be data binding, rather you will have access to all of the members (including fields, properties, and other methods) of the Page.
So both approaches will work if FirstName is a field or property on the Page you are working on. I personally find the control-based approach better but to each their own.
I like using labels as it is easier to mess about with colors, fonts, bolding ect... to display errors or draw the users attention to certain text.
I usually just have a set-up method in the codebehind.
if(!Page.IsPostBack)
SetupForm();
SetupForm()
{
Label_FirstName.Text = firstName;
}
You can use a bunch of labels or the Substitution control or even Literal Text if you want more control over the HTML.
You can use code in your markup like:
<%=this.FirstName%>
This will result in a property on your page called FirstName to be called and the return value from it to be placed inbetween the label.
It depends on the context in which you'll be using them. Generally using the asp:label controls is fine as you'll be able to access them from the codebehind on your page. The databinding method is generally used from within a databound control like a DataGrid or Repeater.
The real problem with the databinding method is that there isn't very good IDE support for this. To get it to work you have to have a property on your page class that you need to populate in the code behind, but if the property name changes, you'll also have to make sure you update your aspx page. If you use the labels method, the IDE will detect if there is a change in the label name, or if it has been deleted altogether and give you a compile time error.
Alternatively you could use jQuery to populate your data and just use spans as your placeholders. Be aware tho that jQuery requires a bit of a different way of thinking about your pages as it's using javascript to populate your fields.
My rules of thumb are:
If I'm labeling an input control, I use a Label so that I can set the AssociatedControlID property.
Otherwise I use a Literal or Localize control. I generally don't style Labels directly, so I don't need the extra markup they generate in other situations.
So, in your situation I'd probably use the Literal control.
Jeff Atwood explored the subject at Coding Horror and found out that performance just doesn't matter...
NOTE: it seems like Jeff posts have to be taken with a grain of salt, after all... sorry for rushing in the answer without reading the comments
Try this:
<%= FirstName %>