RadGrid - Exporting to CSV does not include template column - c#

So I've got the following column defined in my radgrid:
<telerik:GridTemplateColumn DataField="Payment.Customer.FirstName" DataType="System.String"
HeaderText="First Name" SortExpression="Payment.Customer.FirstName" UniqueName="FirstName">
<ItemTemplate>
<asp:HyperLink ID="hypFirstName" runat="server" Target="_blank" Text='<%# ((PaymentIssue)Container.DataItem).Payment.Customer.FirstName %>'
NavigateUrl='<%# string.Format("~/CustomerAdmin/Customer_View.aspx?customerId={0}", ((PaymentIssue)Container.DataItem).Payment.CustomerId) %>'></asp:HyperLink>
</ItemTemplate>
</telerik:GridTemplateColumn>
Now, if I use the RadGrid MasterTableView.ExportToExcel() function; all is well. However; if I try MasterTableView.ExportToCSV(), the column is blank.
Any ideas on what could be causing this?

OK; I figured this out. Turns out I need to set the Text property on the GridDataItems before exporting.
Sample code below:
Export Buttons Click Method
protected void lnkExport_Click(object sender, EventArgs e)
{
var linkButton = (LinkButton)sender;
switch (linkButton.CommandArgument)
{
case "Excel":
rgPaymentIssues.MasterTableView.ExportToExcel();
break;
case "CSV":
PrepareRadGridForExport();
rgPaymentIssues.MasterTableView.ExportToCSV();
break;
default:
break;
}
}
PrepareRadGridForExport snippet
foreach (GridDataItem gi in rgPaymentIssues.MasterTableView.Items)
{
var hypFirstName = (HyperLink) gi.FindControl("hypFirstName");
gi["FirstName"].Text = hypFirstName.Text;
}

Setting ExportOnlyData to 'False' solves this issue.
See the following link for the details.
http://www.telerik.com/help/aspnet-ajax/grid-csv-export.html

Just in case someone arrives here as I did, you could solve it another way, put another column with Readonly="true" so it doesn't appear in editing mode and the original column with Display="false" so it doesn't appear in view mode, then Export is all good, here's the extract:
<telerik:GridBoundColumn DataField="Email2" HeaderText="Email" UniqueName="Email2a" ReadOnly="true" Exportable="true" />
<telerik:GridTemplateColumn DataField="Email2" HeaderText="Email" UniqueName="Email2" Display="false" Exportable="false">
<EditItemTemplate>
...
</EditItemTemplate>
</telerik:GridTemplateColumn>
Happy coding!

Related

Access HyperLink inside ItemTemplate inside TemplateField ASP.Net WebForms

I have an ASP TemplateField inside a data populated GridView as follows:
<asp:GridView ID="gvReport" runat="server" AutoGenerateColumns="False" ShowHeaderWhenEmpty="true" OnRowDataBound="gvReport_RowDataBound" CssClass="table table-bordered">
<Columns>
<asp:BoundField DataField="Delete" HeaderText="Delete" SortExpression="Delete" />
<asp:TemplateField HeaderText="Delete">
<ItemTemplate>
<asp:HyperLink runat="server" NavigateUrl='<%# "Edit?from=Delete&ID=" + Eval("ID") %>' ImageUrl="Assets/SmallDelete.png" SortExpression="PathToFile" />
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
In other words, at the end of each row, there is a delete 'button'. I can tell whether a particular row/record has been deleted by checking the true/false value of the BoundField Delete in my code behind as follows:
if (e.Row.RowType == DataControlRowType.DataRow)
{
TableCell statusCell = e.Row.Cells[0];
if (statusCell.Text == "True")
{
//change ImageUrl of Hyperlink for this row
}
}
Right now, my icon is red for delete, but in the case that a record has already been deleted,
I would like to change the image to a different icon (a blue one).
How can I change this ImageUrl if statusCell evaluates to true?
ok, so we will need two parts here:
We need (want) to persist the data table that drives the grid).
The REASON for this?
We kill about 3 birds with one stone.
We use the data table store/save/know the deleted state
We use that delete information to toggle our image
(and thus don't have to store the state in the grid view).
We ALSO then case use that table state to update the database in ONE shot, and thus don't have to code out a bunch of database operations (delete rows). We just send the table back to the database.
In fact in this example, I can add about 7 lines of code, and if you tab around, edit the data? It ALSO will be sent back to the database. And this means no messy edit templates and all that jazz (which is panful anyway).
Ok, so, here is our grid - just some hotels - and our delete button with image:
markup:
<div style="width:40%;margin-left:20px">
<asp:GridView ID="GridView1" runat="server"
AutoGenerateColumns="False"
DataKeyNames="ID" CssClass="table table-hover borderhide" >
<Columns>
<asp:TemplateField HeaderText="HotelName" >
<ItemTemplate><asp:TextBox id="HotelName" runat="server" Text='<%# Eval("HotelName") %>' /></ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="FirstName" SortExpression="ORDER BY FirstName" >
<ItemTemplate><asp:TextBox id="FirstName" runat="server" Text='<%# Eval("FirstName") %>' /></ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="LastName" >
<ItemTemplate><asp:TextBox id="LastName" runat="server" Text='<%# Eval("LastName") %>' /></ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="City" >
<ItemTemplate><asp:TextBox id="City" runat="server" Text='<%# Eval("City") %>' /></ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Delete">
<ItemTemplate>
<asp:ImageButton ID="cmdDelete" runat="server" Height="20px" Style="margin-left:10px"
ImageUrl="~/Content/cknosel.png"
Width="24px" OnClick="cmdDelete_Click"></asp:ImageButton>
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
<br />
<asp:Button ID="cmdDeleteAll" runat="server" Text="Delete selected" Width="122px"
OnClientClick="return confirm('Delete all selected?')" />
</div>
Ok, and the code to load up the grid - NOTE WE persist the data table!!!!!
Code:
DataTable rstTable = new DataTable();
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
LoadGrid();
else
rstTable = (DataTable)ViewState["rstTable"];
}
void LoadGrid()
{
// load up our grid
using (SqlCommand cmdSQL = new SqlCommand("SELECT * from tblHotels ORDER BY HotelName ",
new SqlConnection(Properties.Settings.Default.TEST4)))
{
cmdSQL.Connection.Open();
rstTable.Clear();
rstTable.Load(cmdSQL.ExecuteReader());
GridView1.DataSource = rstTable;
GridView1.DataBind();
}
ViewState["rstTable"] = rstTable;
}
Ok, so far, real plain jane. The result is this:
Ok, so now all we need is to add the delete button click event.
That code looks like this:
protected void cmdDelete_Click(object sender, ImageClickEventArgs e)
{
ImageButton btn = (ImageButton)sender;
GridViewRow rRow = (GridViewRow)btn.Parent.Parent;
DataRow OneRow = rstTable.Rows[rRow.DataItemIndex];
// toggle data
if (OneRow.RowState == DataRowState.Deleted)
{
// undelete
OneRow.RejectChanges();
btn.ImageUrl = "/Content/cknosel.png";
}
else
{
// delete
OneRow.Delete();
btn.ImageUrl = "/Content/ckdelete.png";
}
}
Again, really simple. But NOTE how we use the data table row state (deleted or not).
And this ALSO toggles the image for the delete button.
So, we don't have to care, worry, or store/save the sate in the grid.
So, if I click on a few rows to delete, I now get this:
So far - very little code!!!
Ok, so now the last part. the overall delete selected button. Well, since that is dangerous - note how I tossed in a OnClientClick event to "confirm the delete. if you hit cancel, then of course the server side event does not run.
But, as noted since we persisted the table? Then we can send the table deletes right back to the server without a loop. The delete code thus looks like this:
protected void cmdDeleteAll_Click(object sender, EventArgs e)
{
// send updates (deletes) back to database.
using (SqlCommand cmdSQL = new SqlCommand("SELECT * from tblHotels WHERE ID = 0",
new SqlConnection(Properties.Settings.Default.TEST4)))
{
cmdSQL.Connection.Open();
SqlDataAdapter daUpdate = new SqlDataAdapter(cmdSQL);
SqlCommandBuilder daUpdateBuild = new SqlCommandBuilder(daUpdate);
daUpdate.Update(rstTable);
}
LoadGrid(); // refesh display
}
So, note how easy it is to update (delete) from the database. If you look close, I used text boxes for that grid - not labels. The reason of course is that if the user tabs around in the grid (and edits - very much like excel), then I can with a few extra lines of code send those changes back to the database. In fact the above delete button code will be 100% identical here (it will send table changes back with the above code. So, if we add rows, edit rows then the above is NOT really a delete command, but is in fact our save edits/deletes/adds command!

Calculating total underneath a gridview

I have a gridview that has a quantity and calculated price right next to it. When I change the quantity inside the Gridview, the price changes right away. I want to show total price outside the gridview . I am showing the total price outside the gridview, but the price is not changing dynamically when the quantity and price changes for individual items. Below is the image of what I want. I want to $560 to be displayed and calculated right away as soon as price changes.
Below is my gridview code:
<asp:GridView ID="grdShoppingCart" runat="server" AutoGenerateColumns="false" class="ui-responsive table-stroke ss-table ui-search-result-table" GridLines="None" OnRowDataBound="grdShoppingCart_RowDataBound" >
<Columns>
<asp:BoundField DataField="item" HeaderText="Item" HeaderStyle-Font-Bold="true" ItemStyle-HorizontalAlign="Left" HeaderStyle-HorizontalAlign="Left" ItemStyle-Width="250px" ControlStyle-CssClass="ss-row" />
<asp:TemplateField HeaderText="Description" HeaderStyle-Font-Bold="true" HeaderStyle-HorizontalAlign="Left" ItemStyle-HorizontalAlign="Left" ItemStyle-Width="300px" ControlStyle-CssClass="ss-row" >
<ItemTemplate>
<asp:Label ID="lblDesc" runat="server" Text='<%# Eval("child") %>' /><br />
Requested By: <asp:Label ID="lblDesc1" runat="server" Text='<%# Eval("RequestedBy") %>' />
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Quantity" HeaderStyle-HorizontalAlign="Left" ItemStyle-HorizontalAlign="Left" ControlStyle-CssClass="ss-row" >
<ItemTemplate>
<asp:TextBox Width="45px" TextMode="Number" ID="txtQuantity" Text='<%#Eval("Quantity") %>' runat="server" AutoPostBack="true" OnTextChanged="txtQuantity_TextChanged" ></asp:TextBox>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Price" HeaderStyle-HorizontalAlign="right" ItemStyle-HorizontalAlign="Right" ItemStyle-Width="100px" ControlStyle-CssClass="ss-row" >
<ItemTemplate>
<asp:Label ID="lblPrice" runat="server"></asp:Label>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField ShowHeader="False" HeaderStyle-HorizontalAlign="center" ItemStyle-HorizontalAlign="center" ItemStyle-Width="150px" ControlStyle-CssClass="ss-row" >
<ItemTemplate>
<asp:ImageButton CommandArgument='<%# Eval("recordId") %>' ID="imgbtnDelete" runat="server" ImageUrl="~/Images/delete1.png" CommandName="Delete" ToolTip="Click To Dletee" AlternateText="Click To Dletee"/>
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
his is what I tried to do in my code behind to show the total:
I declared this variable inside my class and put the value of price text box in total variable in rowDatabound
public string total;
protected void grdShoppingCart_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
Label lbl = (e.Row.FindControl("lblPrice") as Label);
TextBox tbq = (e.Row.FindControl("txtQuantity") as TextBox);
if(tbq.Text !="")
{
lbl.Text = (Convert.ToInt32(tbq.Text) * 28).ToString();
total= lbl.Text.ToString();
}
}
}
and in the aspx page, I wrote this:
Total: <%=total %>
below is the code that changes the price and quantity:
protected void txtQuantity_TextChanged(object sender, EventArgs e)
{
TextBox changeTextbox = sender as TextBox;
GridViewRow row = (GridViewRow)changeTextbox.NamingContainer;
TextBox tbq = (row.FindControl("txtQuantity") as TextBox);
Label lblPrice = (row.FindControl("lblPrice") as Label);
lblPrice.Text = (Convert.ToInt32(tbq.Text) * 28).ToString();
}
any help will be highly apprecaited.
Your code is close. just remember that a data grid will persist in a round trip (post back), and MORE important that if you ONLY data bind the grid on the FIRST postback = false?
Then the grid will display but the row data bind event does not re-fire (and thus no total)
So, we assume that on page back we re-bind.
Now you have this code:
total= lbl.Text.ToString();
You need
total = total + lbl.Text.ToString()
Or, of course this:
total += lbl.Text.ToString()
Or more so, you want a final total, then
total += lbl.Text * tbq.Text
but, what you have should work.
Edit:
I now see the problem is that the total price is not updating right away. But we should at least GET the final total working upon page re-fresh.
If you want the total to update as you edit? Then most easy would be in your text changed event to re-calc the total. So say code like this:
So, you can have your existing code first, and THEN add this loop in the same text changed event:
protected void TextBoxE_TextChanged(object sender, EventArgs e)
{
// total the grid
sTotal = 0;
GridViewRow gvr;
foreach (var gvr in GridView1.Rows)
{
TextBox txtV = gvr.FindControl("lblPrice");
if (txtV.Text != "")
sTotal += lblprice.Text;
}
}
So in your text changed, you can (and need to) re-loop the rows after you updated the one row. So the above loop needs to be run. Keep in mind that the row data bound event fires for EACH row on a databind. If you on post back do a gridview databind, then you will of course loose the values in the grid. However, my bets is that you NOT doing a databind, and thus a simple loop to re-total the amount should work just fine.
The MAJOR issue here is that you left out the WHOPPER of a issue. What is that? Why of course you left out where you total things up in the first place. Since that part of yours was wrong (or at least looks to be), then my post FIRST addresed that issue and you need the +=. If your total on intitail load works, then you shoud have posted that code and how it works - since what you posted will not work.
So now that we cleared up why my post addressed the lack of total on first render.
Once you get that working (and you should have posted how that works), then and ONLY then can we address the text changed event. As above shows, it is a easy matter to re-loop and total up all rows since your changes will persist and be in that data - so right after your text changed - run/add/do the above loop to re-total.

ASP.net GridView InvalidOperationException on foreach when deleting

I'm using ASP.net and i've added a delete button on a gridview and im trying to remove the row of the button clicked. The thing is though, that i get an InvalidOperationException on my foreach.
I may have other errors in the code or my logic may be wrong so if you see any of it please point them out. Thanks.
gridView cart.ASPX:
<asp:GridView ID="CartGrid" runat="server" AutoGenerateColumns="false" OnRowDeleting="RemoveSelected">
<Columns>
<asp:BoundField DataField="product_name" HeaderText="Name" />
<asp:BoundField DataField="price_per_unit" HeaderText="Price" />
<asp:BoundField DataField="unit" HeaderText="Unit" />
<asp:TemplateField>
<ItemTemplate>
<asp:LinkButton ID="delSelected" runat="server" Text="Delete" CommandName="Delete"></asp:LinkButton>
</ItemTemplate>
<ItemStyle Width="100px" />
</asp:TemplateField>
</Columns>
</asp:GridView>
delete method cart.ASPX.CS
protected void RemoveBtn(object sender, GridViewDeleteEventArgs e)
{
ArrayList cartList = (ArrayList)Session["cartList"];
GridViewRow row = (GridViewRow)CartGrid.Rows[e.RowIndex];
String name = row.Cells[0].Text;
//String name = (string)CartGrid.DataKeys[e.RowIndex].Value;
//String name = CartGrid.SelectedRow.Cells[1].Text;
foreach (product p in cartList)
{
if (p.product_name.Equals(name))
{
cartList.Remove(p);
}
}
CartGrid.DataBind();
}
Hi I think the issue is that you are using the cartlist for loop and at the same time you want to delete values from the same which is not possible .
just you need to change your approach like create the empty list and add all the index into it from where you have to delete the value Staring from bottom to top like from end of list so that it can not effect the index of the list by deleted value and after loop you can delete it using the new empty list which contain the list of all the values which should be deleted.
You can't modify the collection you're iterating over with the foreach - please see this question for more information.

FindControl recursive - error when finding my FileUpload control in GridView

I have been trying the whole day to fix this, hope someone can give me an answer!
(please keep in mind I'm a beginner in this coding).
I have a database where one of the fields is an imageurl. I have to be able to update this field and thought that I could do so using a GridView with an UpdateItemTemplate.
I soon found out that you have to use the FindControl recursive method to do so - so I implemented the code and I'm now stuck with another error.
I think I know why the error appears, but have no idea how to fix it. It seems that in the tools.cs file the identifier of the control is set to be of data type String, but I have no clue what to do with a FileUpload.
Here is the error message:
cannot convert from 'System.Web.UI.WebControls.FileUpload' to 'string'
ASP.NET GridView control:
<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False"
DataKeyNames="DrinkCategoryID" DataSourceID="ObjectDataSource1">
<Columns>
<asp:CommandField ShowDeleteButton="True" ShowEditButton="True" />
<asp:BoundField DataField="DrinkCategoryID" HeaderText="DrinkCategoryID"
InsertVisible="False" ReadOnly="True" SortExpression="DrinkCategoryID" />
<asp:TemplateField HeaderText="DrinksCategoryName"
SortExpression="DrinksCategoryName">
<EditItemTemplate>
<asp:FileUpload ID="FileUpload1" runat="server" />
</EditItemTemplate>
<ItemTemplate>
<asp:Label ID="Label1" runat="server"
Text='<%# Bind("DrinksCategoryName") %>'></asp:Label>
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
The tool (FindControl)
public static Control FindControlRecursive(Control Root, string Id)
{
if (Root.ID == Id)
return Root;
foreach (Control Ctl in Root.Controls)
{
Control FoundCtl = FindControlRecursive(Ctl, Id);
if (FoundCtl != null)
return FoundCtl;
}
return null;
}
And code behind for the web form (click event for the save button)
protected void btnGem_Click(object sender, EventArgs e)
{
FileUpload FileUpload1 = (FileUpload)Tools.FindControlRecursive(
GridView1, FileUpload1);
//This seems to work fine
TextBox txtBox = (TextBox)Tools.FindControlRecursive(GridView1, txtBox.Text);
}
On the first line of your button handler, you're passing the control itself as the second parameter of FindControlRecursive - you need to pass in the string ID of the control you're looking for. In other words:
protected void btnGem_Click(object sender, EventArgs e)
{
FileUpload FileUpload1 = (FileUpload)Tools.FindControlRecursive(GridView1, "FileUpload1");
TextBox txtBox = (TextBox)Tools.FindControlRecursive(GridView1, txtBox.Text); //This seems to work fine
}

Controlling Checkboxes inside a Gridview Row in ASP.NET

Not really sure how to handle this issue but here goes...
I have a gridview with two checkboxes for each row, below is an example of the item template:
<ItemTemplate>
<asp:CheckBox ID="MasterCheckbox" runat="server"/>
<asp:CheckBox ID="ChildCheckbox" runat="server" />
</ItemTemplate>
I would like the 'enabled' property of the ChildCheckbox to be controlled by the 'Checked' property of the MasterCheckbox ... so in other words the ChildCheckbox is only enabled when the MasterCheckbox has been checked.
I know that I will need to append a handler onto the MasterCheckbox control to invoke some javascript to perform the necessary actions on the client-side - this will probably be done in the row_databound() method?
I can't quite figure out the javascript required to get this to work, so any hints/tips would be welcome.
Thanks
Dal
First you dont need to answer your own question,you can add comments into your very first question.
Since you are using GridView , I think you are binding something for MasterCheckBox,
so let's say that it's boolean value in a dataTable.
For Example it's a row contaning column with name IsMasterChecked
You can handle Enabling the other one with binding custom expressions as
<ItemTemplate>
<asp:CheckBox ID="MasterCheckbox" runat="server" />
<asp:CheckBox ID="ChildCheckbox" runat="server" Enabled='<%# Convert.ToBoolean(Eval("IsMasterChecked")) %>'/>
</ItemTemplate>
or
<ItemTemplate>
<asp:CheckBox ID="MasterCheckbox" runat="server" />
<asp:CheckBox ID="ChildCheckbox" runat="server" Enabled='<%# Convert.ToBoolean(Eval("IsMasterChecked")) ? "true" : "false" %>'/>
</ItemTemplate>
Hope this helps.
Off the top of my head, I think what you will have to do is something along the lines of the following...
<asp:TemplateField HeaderText="Checkbox">
<ItemTemplate>
<asp:CheckBox ID="MasterCheckbox" runat="server" AutoPostBack="true" OnCheckedChanged="checkGridViewChkBox" />
</ItemTemplate>
</asp:TemplateField>
With the following code behind.
CheckBox MasterCheckbox;
CheckBox ChildCheckbox;
private void checkGridViewChkBox()
{
int i;
int x = GridView1.Rows.Count;
for (i = 0; i < x; i++) //loop through rows
{
findControls(i);
if (MasterCheckbox.Checked)
{
ChildCheckbox.Enabled = true;
}else{
ChildCheckbox.Enabled = false;
}
}
}
private void findControls(int i)
{
MasterCheckbox = (CheckBox)(GridView1.Rows[i].FindControl("MasterCheckbox"));
ChildCheckbox = (CheckBox)(GridView1.Rows[i].FindControl("ChildCheckbox"));
}
It's not terribly efficient but works ok.

Categories