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?
Related
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!
I have a gallery which holds a large number of thumbnail images and I want to show 6 at a time.
I have this working using the code below, but I can not get the images to display as 2 rows of 3 - it shows as 6 rows of 1.
I can get the desired result by using a datalist but that stops the pageindex function from working.
I'm sure there's an easy solution but I can't figure it out.
<asp:GridView id="GridView1" runat="server" AutoGenerateColumns="False" AllowPaging="True" PageSize="6" onpageindexchanging="PageIndexChanged" PagerSettings-Mode="NextPrevious" PagerSettings-NextPageText="Next" PagerSettings-PreviousPageText="Previous">
<Columns>
<asp:TemplateField>
<itemtemplate>
<asp:Image ID="Image2" runat="server" ImageUrl='<%# Eval("userID", "/imageUpload/Handler.ashx?userID={0}&image=2")%>' ></asp:Image>
</itemtemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
You want to use a ListView.. Look at MSDN here: http://msdn.microsoft.com/en-us/library/bb398790.aspx
Most particularly the GROUPING option.
You might want to consider using a simple asp.net repeater control, and render your items as DIVs. You can use CSS "float" directive to cause your DIVs to automatically "wrap" within their container, to efficiently fit the space (very similar to the way text "wraps" within a text box when it reaches the end of the line).
Because they wrap dynamically, float'd DIVs can be a little more user-friendly: if your user has a wide high-resolution screen, maybe the images render as a 1x6 row. If your user shrinks their browser window, the DIVs will automatically re-arrange themselves into a 3x2 grid.
You wouldn't get data-paging behavior for free, but it's not difficult to implement on its own.
You'd probably be happier with a different control (repeater, etc.)... Gridviews are limited in that they match your datasource row-for-row.
If you have to do this in a gridview for some reason, one option would be to modify the SELECT in your data source so that it put your images into 3 columns. You might be able to do that with a pivot and rownumber() function, or other ways. Once you did that, you'd modify your grideview (copy your template column two more times).
i have this template field inside a gridview.
<asp:TemplateField ItemStyle-HorizontalAlign="Center">
<ItemTemplate>
<asp:ImageButton ID="ImageButton2" ImageUrl="~/images/DeleteRecord.gif" runat="server"
OnClientClick="return ConfirmacionBorrarClausula();" CommandName="BorrarClausula" CommandArgument='<%#Eval("ClausulaID")%>' OnCommand="gvClausulas_OnRowDeleting" CausesValidation="false"
</ItemTemplate>
</asp:TemplateField>
I have another one in the same page but in a different gridview, almost exactly like this one but the second one isnĀ“t working.
So i have two gridviews each one with a template field like the one here, one onRowDeleting working perfectly, the other one not working at all, when i click it, it asks for confirmation (javascript function) but when i click ok to delete, the grid loses it data and the page fires all the validators.
Thank you for your time.
Make sure the control IDs are set right. And Ispostback the control level set to true. And also Try deleting the control and add it again some time that might help. Try add it from design view.
i manage to solve it, the problem was the second gridview was losing its data on the pageload, i managed that but only with the first gridview.
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.