the website I'm currently developing can display dynamically-built forms.
A form is composed of fields, which are created directly by the users and can be displayed as one of multiple types that we support (text box, list box, tickbox, radiobuttonlist etc.). The rendering logic uses a repeater that iterates over a collection of all the fields defined by the user.
Inside the repeater (directly in the aspx page), one instance of each of the types we support is defined.
<asp:Repeater ID="fieldRepeater" runat="server">
<ItemTemplate>
<asp:TextBox ID="textBox" runat="server" />
<asp:DropDownList ID="dropDownList" runat="server" />
<asp:CheckBox ID="checkBox" runat="server" />
<asp:RadioButtonList ID="radioButtonList" runat="server" />
[...]
</ItemTemplate>
</asp:Repeater>
During the loading, we figure out which control is required and actively hide all the other ones.
Being still new to the web based development world, this approach seems very odd to me. My guts would prefer keeping the UI clean and instanciate exactly the controls that are required in CodeBehind and not start "playing" with visibility... but the current approach has some obvious benefits as well.
Is it really how one would do it in a web app?
Are there some best practices here?
Thanks!
I have no idea what the best practice is here, but I have done something similar before in a previous project and tried both approaches. Both will work.
Creating the controls in code-behind can be fiddly, especially if you are having to deal with post-backs. The controls have to be created in OnInit, as otherwise they won't get the posted form values and viewstate populated. This will cause complications if any of the control creation is based on the values of other controls, as you won't known the values without manually delving into the posted form values.
The only practical disadvantage with your current approach that I can think of is that all four controls (TextBox, DropDownList etc.) have to be instantiated and processed server-side by ASP.Net, which is a bit of a waste of resources. But it's probably not too significant; maybe do some profiling to see. I do agree that it seems a bit odd though, it doesn't feel very "clean".
As you said you're new to web development, then I would recommend continuing with your current approach of including all the controls and hiding the irrelevant ones. I just found it simpler when I did, even though it may not seem as nice.
Good luck!
Related
Climbing the learning curve for creating asp.net webform pages with Visual Studio 2010 (VB).
I had written a fairly complicated .aspx page with form controls, including textboxes and buttons, etc. I never thought to place the form controls inside a <form> block. Instead, all the controls include the "runat" directive; for example, <asp:textbox id="txtUserName" runat="server"> etc. In the codebehind I access the data with strUserName = txtUserName.text. This seems to work just fine.
Now, though, I received some form pages from our contracted "professional" web developer wherein the form code is all enclosed in a <form runat="server">block, and none of the controls include the runat directive. Accessing the data from these controls is a little different: It uses the <input type="text name="txtUserName" id="txtUserName" /> method, and accessing the data in the codebehind
is strUserName = Request.Form("txtUserName").ToString.
My method seems to work fine, but I am wondering if there is a difference in behavior or reliability between my method and his. Even though my way works, am I doing it wrong?
Mine is based on online research I have done to learn this stuff, and I don't remember seeing anything that looked like his. However, just today I see places that are saying that on .aspx pages, form controls MUST be enclosed in a <form> block (i.e., this page at w3schools.com).
Can anyone clarify this for me?
Thanks for your help!
You're not doing it incorrectly (you're using my preferred approach) but your inputs should still be in an enclosing Form tag.
He's using HtmlControls (System.Web.UI.HtmlControls namespace) and you're using web controls (System.Web.UI.WebControls.) Your controls provide better functionality on the server (viewstate and accessing via server code) and his approach is lighter weight.
I have a WebForms page with a treeview on the left and grids on the right. When the user clicks a treeview node, the corresponding grid is populated using a SqlDataSource and then displayed. The whole thing's in a single UpdatePanel.
Here's my setup:
<asp:GridView runat="server" ID='LocationsRowGrid' AutoGenerateColumns="false" DataSourceID="SqlDataSource_LocationRow">
<Columns>
<asp:TemplateField HeaderText="Location">
<ItemTemplate>
<asp:DropDownList runat="server" ID="NAME_LCTN" OnDataBound="dropdown_DataBound"
DataTextField="NAME_TO_LCTN" DataValueField="NAME_TO_LCTN" DataSourceID="SqlDataSource_LocationNames">
</asp:DropDownList>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Move Time (HR)">
<ItemTemplate>
<asp:TextBox runat="server" ID="STD_MOVE_TME_AMNT" Text='<%# Bind("STD_MOVE_TME_AMNT") %>'></asp:TextBox>
</ItemTemplate>
</asp:TemplateField>
<asp:BoundField DataField="LAST_UPDATED_BY" HeaderText="Updated By" Visible="true" />
<asp:BoundField DataField="LAST_REV_DT" HeaderText="Revision Date" Visible="true" />
</Columns>
</asp:GridView>
There are other (larger) grids, but this is basically the template. The largest grid has about twelve columns, about six of which are templatefields with textboxes and one of which is a templatefield with a dropdown list. The dropdown is databound to another table, which has about 150 elements. The grid itself has about 100 entries.
This is slow. It seems like the problem might have to do with the rendering of the HTML - the server isn't taking TOO long to respond, but the browsers (Chrome and IE) are nearly falling over trying to render the result. My first (obvious) guess is that rendering 100 html selects, each with > 100 elements, is going to slow - especially when done all at once inside tables tags like ASP.NET will do.
Does this seem like a reasonable guess at the cause of the slowness?
For this project I am (currently) not allowed to use jQuery (or presumably any other javascript library) and must justify any and all javascript I use. Basically, other developers don't want to need any real understanding of javascript to be able to maintain this application when I'm done with it.
Given these constraints, is there anything I can do to reduce the size of the returned HTML and/or the render times in the browser? Thanks in advance.
My thought is reduce the amount of bound data on the initial page and institute paging. Unless this is a "report", the user does not need to see every row at one time.
Another possibility is you have the page set to also use viewstate for the grid. If so, you are consuming time in the bind to create the viewstate prior to rendering (at least in the default situation).
Beyond that, I agree with #n8wrl that you need to troubleshoot by isolating whether it truly is the rendering that is the issue. I would probably do this through tracing rather than turning things off, but you do have to make sure the problem is the rendering. Another control, like a repeater, may work, but I only see this as a benefit with something like CSS, which reduces the amount of tagged data to produce output.
Compressing the response stream in IIS can also speed things up. Rendering time will still be the same, but you will eliminate the time to send the HTML to the browser, which is at least part of your problem.
Several thoughts for you:
'Turn off' the rendering to see how much of the response time is in server-side querying. You can do this by commenting out DataSoource= statements.
Browsers tend to wait until an entire table has made it before rendering any of it. You might try different layouts such that not everything is in a single table. Maybe a repeater where each item is a stand-alone table? Each can then render as the markup appears.
You can start to get a lot more fancy with jQuery and AJAX to page your content. Instead of 100 rows maybe 10 rows with page 1, 2, 3... links?
So I haven't used ASP.NET for three years or so and I'm really rusty on it. My old code isn't available to me for review (at an old company). This question should be pretty basic, but I can't find any good or reliable or not-super-old resources on the issue, so I'm asking here.
Can I get a general overview of databinding again? I remember it being really useful for select boxes, etc., but I don't really remember how it works. Maybe a good ASP.NET tutorial in general, because I don't remember how it handles POST requests or anything like that really either. Should I just try ASP.NET MVC?
Relatedly, suppose I have a public variable in my codebehind page. Right now I am accessing it by saying Page.DataBind() at the end of the page load function and then running <%# variable %> in the ASPX, but that's not how I remember doing it before and I reckon that it's not very good practice. What's the best way to display variables from codebehind?
Databinding in general (at least in the WebForms model) is mostly a case of assigning fields to be displayed, setting the DataSource property to a suitable object that contains those fields e.g. a DataReader, DataTable, a Collection, and calling the DataBind method. So for your select case, you'd put an <asp:dropdownlist runat="server" id="MyDropDownList"> in the markup for the page, and then in the code
DataSet myDataSet;
myDataSet = someDataMethod();
MyDropDownList.DataTextField = fieldname;
MyDropDownList.DataValueField = fieldname;
MyDropDownList.DataSource = myDataSet;
MyDropDownList.DataBind();
Or you can avoid writing that kind of code and do it in the markup if you use a DataSource control e.g. <asp:SqlDataSource>, <asp:ObjectDataSource>
<asp:SqlDataSource runat="server" id="MySqlDataSource" ConnectionString="aConnectionString" SelectCommand="MyStoredProcName" SelectCommandType="StoredProcedure" />
<asp:dropdownlist runat="server" id="MyDropDownList" DataSourceId="MySqlDataSource" DataTextField="fieldname" DataValueField="fieldname">
For putting your variable on a page, the way you might have done it before is to have a label or textbox on the page, that in your code-behind you assign your variable to the Text property e.g.
<asp:label runat="server" id="MyLabel" />
MyLabel.Text = myVariable.ToString();
Postbacks: you can test the IsPostback property of a page in code-behind to determine if it's a postback or not. After the Page_Load method, other methods will fire if you've defined them e.g. SelectedIndexChanged for a DropDownList.
I really wanted to answer this question with examples and code etc.. but I would just be rehashing information that has been on the web for years and been explained in blogs and articles countless times. You can start with this article
which explains almost everything you need to know.
I have bolded the ones that I think are important to note that some my skim over.
<%# %> Syntax
Page.DataBind() versus Control.DataBind()
Data-bound list controls
Repeater control
DataList control
DataGrid control
Accessing data
DataSet class
DataReader class
Binding in list control templates
DataBinder.Eval method
Explicit casting
ItemDataBound event
As for learning MVC over webforms, that is whole different story. They both have pluses and minuses depending on your time, what you NEED to know and what portions of the project are important. Both techs can accomplish the same thing as they are all ASP.NET at their core, just different approaches so you will be fine either way.
I have been searching around to find the best option of doing this.
Basically, I want to do two way databinding of multiple controls (textbox, dropdownlist, checkbox, etc) to a single instance of custom class/entity, ie: Person.
It seems, the (only?) way to do this is using an like so:
<asp:FormView ID="FormView1" runat="server" DataKeyNames="OrderID"
DataSourceID="SqlDataSource1" DefaultMode="Edit">
<EditItemTemplate>
OrderID:
<asp:Label ID="OrderIDLabel1" runat="server" Text='<%# Eval("OrderID") %>' />
<br />
CustomerID:
<asp:DropDownList ID="CustomerIDDropDownList" runat="server"
DataValueField='CustomerID' DataSourceID="CustomerDataSource"
DataTextField="CompanyName"
SelectedValue='<%# Bind("CustomerID") %>'
/>
EmployeeID:
<asp:TextBox ID="EmployeeIDTextBox" runat="server"
Text='<%# Bind("EmployeeID") %>' />
<br />
Some issues:
- This is limited to using an ObjectDataSource control (ie: can't just use an instance of the desired class in the code behind)
- Forces you to define a second (likely identical layout) read only template....would be nice to have some some mechanism that could intelligently render a read-only view derived from the edit template.
- The binding declaration Text='<%# Bind("EmployeeID") %>' is loosely typed, so vulnerable to spelling errors
- etc
So my first question I guess is, is an asp:FormView the only way in ASP.Net to do declarative databinding of a single entity?
Secondly, how feasible would it be to hand roll some sort of a two way binding mechanism? I guess it would have to be reflection based, but I could live with that. Anyone recommendations on how one would declare the binding relationships in the aspx page? Would the proper way be like:
Text='<%# MySuperDuperBind("EmployeeID") %>'
And then somewhere (where?) my MySuperDuperBind implementation will get called as the page is rendered....how this is done is a bit beyond me though. And if I want to render in readonly, I can call a secondary function that will remove the editable UI control from the form and replace it with the corresponding read only version (ie: a Textbox is replaced with a Label).
Another alternative route is getting away from webforms and going to a client side templating solution such as this very nice looking solution:
http://weblogs.asp.net/dwahlin/archive/2009/05/03/using-jquery-with-client-side-data-binding-templates.aspx
However, I have no clue how to write the asp.net webservices properly in order to retrieve and save data in this type of an architecture.
Ideas?
is an asp:FormView the only way in
ASP.Net to do declarative databinding
of a single entity?
There's also DetailsView but it has the same issues.
I've mostly given up on 2-way databinding. It's great for prototyping and gets me 80-90% of the way to a complete solution but the last 10-20% is a nightmare. Binding any non-trivial object always seems to involve so many event handlers to customize behavior that it feels like spaghetti code to me.
I usually have two methods:
MapEntityToView(entity)
MapViewToEntity(entity)
that I call to display the entity and to populate from the page, respectively. It can be tedious to write but I don't have to wrestle with data binding issues.
I do use 1-way binding extensively for read only pages and displaying items in list controls.
I'm coming from a Rails background and doing some work on a ASP.NET project (not ASP MVC). Newbie question: what's the easiest way to make a custom editor for a table of records?
For example: I have a bunch of data rows and want to change the "category" field on each -- maybe a dropdown, maybe a link, maybe the user types it in.
In Rails, I'd iterate over the rows to build a table, and would have a form for each row. The form would have an input box or dropdown, and submit the data to a controller like "/item/edit/15?category=foo" where 15 was the itemID and the new category was "foo".
I'm new to the ASP.NET model and am not sure of the "right" way to do this -- just the simplest way to get back the new data & save it off. Would I make a custom control and append it to each row? Any help appreciated.
You can REALLY cheat nowadays and take a peek at the new Dynamic Data that comes with .NET 3.5 SP1. Scott Guthrie has a blog entry demoing on how quick and easy it'll flow for you here:
http://weblogs.asp.net/scottgu/archive/2007/12/14/new-asp-net-dynamic-data-support.aspx
Without getting THAT cutting edge, I'd use the XSD generator to generate a strongly typed DataSet that coincides with the table in question. This will also generate the TableAdapter you can use to do all your CRUD statements.
From there, bind it to a DataGrid and leverage all the standard templates/events involved with that, such as EditIndex, SelectedIndex, RowEditing, RowUpdated, etc.
I've been doing this since the early 1.0 days of .NET and this kind of functionality has only gotten more and more streamlined with every update of the Framework.
EDIT: I want to give a quick nod to the Matt Berseth blog as well. I've been following a lot of his stuff for a while now and it is great!
There are a few controls that will do this for you, with varying levels of complexity depending on their relative flexibility.
The traditional way to do this would be the DataGrid control, which gives you a table layout. If you want something with more flexibility in appearance, the DataList and ListView controls also have built-in support for editing, inserting or deleting fields as well.
Check out Matt Berseth's blog for some excellent examples of asp.net controls in action.
Thanks for the answers guys. It looks like customizing the DataGrid is the way to go. For any ASP.NET newbies, here's what I'm doing
<asp:DataGrid ID="GridView1" runat="server" AutoGenerateColumns="False">
<Columns>
<asp:BoundColumn DataField="RuleID" Visible="False" HeaderText="RuleID"></asp:BoundColumn>
<asp:TemplateColumn HeaderText="Category">
<ItemTemplate>
<!-- in case we want to display an image -->
<asp:Literal ID="litImage" runat="server">
</asp:Literal>
<asp:DropDownList ID="categoryListDropdown" runat="server"></asp:DropDownList>
</ItemTemplate>
</asp:TemplateColumn>
</Columns>
</asp:DataGrid>
This creates a datagrid. We can then bind it to a data source (DataTable in my case) and use things like
foreach (DataGridItem item in this.GridView1.Items)
{
DropDownList categoryListDropdown = ((DropDownList)item.FindControl("categoryListDropdown"));
categoryListDropdown.Items.AddRange(listItems.ToArray());
}
to populate the intial dropdown in the data grid. You can also access item.Cells[0].text to get the RuleID in this case.
Notes for myself: The ASP.NET model does everything in the codebehind file. At a high level you can always iterate through GridView1.Items to get each row, and item.findControl("ControlID") to query the value stored at each item, such as after pressing an "Update" button.