I'm trying to modify a field in each row of my gridview as it is rendered. I need to modify it with client side Javascript. I'm somewhat new to asp.net, but I think I should be doing something with clientscriptmanager.
I came up with a simple scenario which is basically what I want to do to avoid getting bogged down in the details. If I could accomplish the following I could accomplish my goal.
Say I have a grid view of names and salaries. I want to double each persons salary before displaying it. Obviously I can do it in the code behind, but due to the nature of the more complex actual thing I'm doing, I need to do it in javascript
<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="false"
onrowdatabound="GridView1_RowDataBound" >
<Columns>
<asp:BoundField HeaderText="Full Name" DataField="Name" ></asp:BoundField>
<asp:BoundField HeaderText="Salary" DataField="Salary" />
</Columns>
Its for sure that this won't be a good approach. If javascript is disabled in your browser then you won't get the actual data, which won't be desirable.
Anyways something like this will do it. Using jQuery
$("#GridView1 tr td:nth-child(2)").each ( function(){
var value = parseInt($(this).text(),10 ) * 2;
$(this).text(value);
});
This probably isn't what you're looking for, but i would use a template field in the case you are citing. Or better yet, modify the data being returned from the datastore to already have the value calculated.
<asp:TemplateField HeaderText="Salary">
<ItemTemplate>
<%#Eval("Salary") * 2 %>
</ItemTemplate>
</asp:TemplateField>
There are a lot of reasons you wouldn't do this with client side scripting. For one, it can be disabled on the browser. Plus, you are relying on the client to perform calculations that would be more efficiently and appropriately run on the server.
Related
I am making some changes to an internal application that has a Telerik RadGrid component on one .aspx page.
Each row in the RadGrid represents an order and there is a dropdown which allows setting the status of the order. Changing the status of the order updates a couple of other properties of the order which are displayed on that row.
Up until now, changing the order status has resulted in a complete page post back and re-render. I'm keen to change this to a partial post back using an UpdatePanel. I could wrap the status dropdown (a RadCombo) in an UpdatePanel which would take care of the actual database changes that are required (as per the code sample below), but then without updating the other properties on the RadGrid row, the updates are not presented to the user.
<telerik:RadGrid ID="OrdersGrid" runat="server" ...>
<MasterTableView DataKeyNames="OrderId" AllowMultiColumnSorting="false">
<NoRecordsTemplate ...></NoRecordsTemplate>
<Columns>
<telerik:GridBoundColumn ... />
...
<telerik:GridTemplateColumn HeaderText="Order Status" UniqueName="OrderStatus">
<ItemTemplate>
<asp:UpdatePanel runat="server">
<ContentTemplate>
<telerik:RadComboBox ID="RadOrderStatus" DataSourceID="OrderStatusDataSource" runat="server"
SelectedValue='<%# Bind("OrderStatus") %>' Skin="Metro" Width="180px" DataTextField="OrderStatus"
DataValueField="OrderStatus" AutoPostBack="True" EnableLoadOnDemand="False" OnSelectedIndexChanged="RadOrderStatus_SelectedIndexChanged">
</telerik:RadComboBox>
</ContentTemplate>
</asp:UpdatePanel>
</ItemTemplate>
</telerik:GridTemplateColumn>
</Columns>
<SortExpressions>...</SortExpressions>
</MasterTableView>
</telerik:RadGrid>
I'm wondering how I can use the UpdatePanel to effectively wrap the row of the RadGrid so that the entire row can be updated as opposed to just the cell that the dropdown is in. I've tried experimenting already with tag placement but I'm new to Telerik and therefore not very clued up.
At what level in the mark up can I place the UpdatePanel to get this to work as I'd like?
Or is there a Telerik way of doing this?
I could wrap the whole grid but if possible, I'd rather not have the whole grid update on each partial post back, the permitted operations are limited to the row level so I see a full grid update as wasteful.
You can't place an UpdatePanel around each row. For starters, there is no provision to do that (you can't do it with the standard GridView either). Then, if you manage to do that (e.g., override the Render event), you would get invalid markup because you can't have <div> elements inside the <table> and between the other <tr> nodes.
One note on the performance—the AJAX request will have the page go through its entire lifecycle on the server, so all code will be executed again and any time consuming operation will also be executed. The only difference between the AJAX and the full postback is what gets rendered and returned in the response, so you basically shave off network time only.
What you can do is the following:
wrap the entire grid. I would use RadAjaxPanel and RadAjaxLoadingPanel so you have a pretty loading indicator. Something like:
<telerik:RadAjaxLoadingPanel ID="RadAjaxLoadingPanel1" runat="server" Skin="Black"></telerik:RadAjaxLoadingPanel>
<telerik:RadAjaxPanel ID="RadAjaxPanel1" runat="server" LoadingPanelID="RadAjaxLoadingPanel1">
<telerik:RadGrid ID="RadGrid1" runat="server"></telerik:RadGrid>
</telerik:RadAjaxPanel>
OR, use client-side code and jQuery. The combo can call a WebService or a PageMethod that will return the data and you can use jQuery to traverse the DOM and update the other elements (textboxes, dropdowns, whatever you have). This is going to be more difficult.
I want to add a hidden key field in a GridView that is not displayed to the user. I tried doing something like:
<asp:GridView ...>
<Columns>
<asp:TemplateField>
<ItemTemplate>
<asp:HiddenField ID="secretkey" runat="server" Value='<%# Eval("secretkey") %>' />
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
But the hidden field still ends up visible as plain text in the html source. Is it possible to do something like this using server state?
Using datakeynames you can associate even multiple key to your rows. Check the following link http://msdn.microsoft.com/en-us/library/system.web.ui.webcontrols.gridview.datakeynames.aspx
The Session would be the best place to store small data that you need across PostBacks but can't sent to the client.
You could put a Dictionary indexed by row identifier into session and use that to fetch your secret key.
Note that sessions are maintained across pages, so unless you are ok with the information being in memory till the user signs out(gets logged out eventually) you will have to delete it when done.
Yes hidden field will be visible in html source, instead you should use datakey to use your secret key
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?
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 have an ASP.NET application that is fairly basic. It queries some data and displays the data in a GridView (0 - 2000 or so records possible). I've been trying to find some ways to make it zippier, best practices, etc. as it seems to be a little sluggish while the GridView is being rendered. I've seen some threads on utilizing CSS vs. setting all the styles directly on the GridView, but I'm not sure how this would look.
This is what the GridView looks like right now...
<asp:GridView ID="gvResults" runat="server" DataKeyNames="ORDNO" AutoGenerateColumns="False"
CellPadding="4" ForeColor="#333333" OnSelectedIndexChanged="gvResults_SelectedIndexChanged"
Width="100%" OnRowDataBound="gvResults_RowDataBound" meta:resourcekey="gvResultsResource1">
<AlternatingRowStyle BackColor="White" ForeColor="#284775" />
<Columns>
<asp:BoundField DataField="CSTNO" HeaderText="CUST" meta:resourcekey="BoundFieldResource1" />
<asp:BoundField DataField="ORDNO" HeaderText="RMA NUMBER" meta:resourcekey="BoundFieldResource2" />
<asp:BoundField DataField="CSTORD" HeaderText="CUST PO NUMBER" meta:resourcekey="BoundFieldResource3" />
<asp:BoundField DataField="ORDDTE" HeaderText="ORDER DATE" meta:resourcekey="BoundFieldResource4" />
<asp:BoundField DataField="INVDTE" HeaderText="INVOICE DATE" HtmlEncode="False" meta:resourcekey="BoundFieldResource5" />
<asp:CommandField ShowSelectButton="true" ButtonType="Link" meta:resourcekey="CommandFieldResource1" />
</Columns>
<EditRowStyle BackColor="#999999" />
<EmptyDataTemplate>
<span style="color: Red;">
<asp:Literal ID="litErrorNoRMAFound" runat="server" EnableViewState="False" meta:resourcekey="litErrorNoRMATagsFoundResource1"
OnInit="litErrorNoRMAFound_Init"></asp:Literal>
</span>
</EmptyDataTemplate>
<FooterStyle BackColor="#5D7B9D" Font-Bold="True" ForeColor="White" />
<HeaderStyle BackColor="#3494CC" Font-Bold="True" ForeColor="White" HorizontalAlign="Left" />
<PagerStyle BackColor="#284775" ForeColor="White" HorizontalAlign="Center" />
<RowStyle BackColor="#F7F6F3" ForeColor="#333333" />
<SelectedRowStyle BackColor="#E2DED6" Font-Bold="True" ForeColor="#333333" />
</asp:GridView>
Thanks in advance, for any of your ideas/comments.
EDIT
Requirements do not allow for paging of the data. I am also looking for specific information on CSS utilization and the GridView...
A few ideas:
Eliminate viewstate if possible.
If you're using IIS6 or better, consider enabling compression.
Enable paging on your GridView to keep request times down.
Make sure your deployed production solution is release-compiled with no rogue debugging or tracing directives
Don't use the GridView. If you want control, use the Repeater.
If you're not using paging (as mentioned in a comment), consider going to a DataList or even a Repeater to reduce the overhead of the object itself.
Enable paging will work ;)
Is viewstate enabled? That could be a lot of hidden data.
You could try using server-side viewstates. It puts a bit more load on the server, but page loading / updating should be much faster.
Using CSS should reduce the size of the markup.
http://www.jigar.net/howdoi/viewhtmlcontent197.aspx shows how to style the gridview with CSS.
You can replace the entire way the grid renders with the CSS Control Adapters, which should help reduce the markup size further.
You'll want to enable paging in order to avoid a situation where you are rendering hundreds of records to the page. In essence, you'll use the Page Controls in order to move through records rather than the scroll bar.
A related option that can help a lot is to use an ObjectDataSource that retrieves only those records that the grid wants to display. This uses the new ROW_NUMBER() construct in SQL Server. Here is an example of an ObjectDataSource method that retrieves a specific range of records. It uses my DAL but you should be able to get a pretty clear idea:
[DataObjectMethodAttribute(DataObjectMethodType.Select, true)]
public List<SelectClassData> GetList(string sortType, int startRowIndex, int maximumRows)
{
if (!BusinessUtilities.SQLSafe(sortType))
throw new Exception("Illegal value in request (unsafe).");
startRowIndex++;
int EndRow = startRowIndex + maximumRows;
using (BSDIQuery qry = new BSDIQuery())
{
if (sortType.Length == 0)
sortType = "Title";
return
qry.Command(
"With OrderedClassEvent as (Select ClassID, Title, StartDate, EndDate, ROW_NUMBER() OVER (ORDER BY " + sortType + ") as RowNum From ClassEvent)")
.Append(" Select ClassID, Title, StartDate, EndDate From OrderedClassEvent Where RowNum Between #StartRow AND ").ParamVal(startRowIndex)
.Append("#EndRow ").ParamVal(EndRow)
.Append("Order By RowNum")
.ReturnList<SelectClassData>();
}
}
With respect to using CSS, etc. that will not make the grid zippier but it will permit you to define the Grid attributes (e.g. FooterStyle, HeaderStyle, etc.) in a "Skin" file that you'll tuck away in an theme directory. It is optional but you could also define the styles for each attribute using CSS. The skin file will show a "template" GridView with the CssClass for each attribute. When you refer create your grid, you'll refer to the Skin file and then just leave out all of the attribute definitions found in that file (the gridview won't need them because it knows what to do based on the skin). This is especially great if you want to have all of your grids look the same.
You answered it yourself - zip it to make it zippier. Use a compression http handler.
You could output the first X items, then when the page has loaded fetch the next X items and so forth. This would probably make the page seem snappier for the end user.
You should also investigate what exacly makes the page slow, also check if the database query could be optimized using indexes or something.
As pagination is not allowed, I definitely explore to reduce total payload of this grid:
Using repeater control and build native TABLE to render this grid instead of datagrid control - this way I will have complete control over payload. Something like:
Using 100% CSS style instead of controls attributes.
Disabling the viewstate (?)
Note that performance issue is less relating to asp.net here and more to the amount of data transfer from server to client machine.
Disable viewstate if it is possible.
Use Repeater instead of grid and use
your own html, that would decrease
the rendered mark up
Try to use stored procedure
If you have any methods which getting
some data from other tables in your
db. Try to use view